Support Vue JSX (#4897)

Co-authored-by: Dan Jutan <danjutan@gmail.com>
This commit is contained in:
Bjorn Lu 2022-09-29 11:25:45 +08:00 committed by GitHub
parent 24bad5a0ad
commit fd9d323a68
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 329 additions and 5 deletions

View file

@ -0,0 +1,6 @@
---
'@astrojs/vue': minor
'astro': patch
---
Support Vue JSX

View file

@ -27,7 +27,7 @@ function guessRenderers(componentUrl?: string): string[] {
return ['@astrojs/vue'];
case 'jsx':
case 'tsx':
return ['@astrojs/react', '@astrojs/preact'];
return ['@astrojs/react', '@astrojs/preact', '@astrojs/vue (jsx)'];
default:
return ['@astrojs/react', '@astrojs/preact', '@astrojs/vue', '@astrojs/svelte'];
}

View file

@ -0,0 +1,7 @@
import { defineConfig } from 'astro/config';
import vue from '@astrojs/vue';
// https://astro.build/config
export default defineConfig({
integrations: [vue({ jsx: true })],
});

View file

@ -0,0 +1,10 @@
{
"name": "@test/vue-jsx",
"version": "0.0.0",
"private": true,
"dependencies": {
"@astrojs/vue": "workspace:*",
"astro": "workspace:*",
"vue": "^3.2.39"
}
}

View file

@ -0,0 +1,28 @@
import { defineComponent, ref } from 'vue';
export default defineComponent({
props: {
start: {
type: String,
required: true
},
stepSize: {
type: String,
default: "1"
}
},
setup(props) {
const count = ref(parseInt(props.start))
const stepSize = ref(parseInt(props.stepSize))
const add = () => (count.value = count.value + stepSize.value);
const subtract = () => (count.value = count.value - stepSize.value);
return () => (
<div class="counter">
<h1><slot /></h1>
<button onClick={subtract}>-</button>
<pre>{count.value}</pre>
<button onClick={add}>+</button>
</div>
)
},
})

View file

@ -0,0 +1,15 @@
<template>
<pre>{{ value }}</pre>
</template>
<script>
export default {
props: {
value: {
type: Number,
required: true
}
}
}
</script>

View file

@ -0,0 +1,35 @@
---
import Counter from '../components/Counter.jsx'
import Result from '../components/Result.vue'
---
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width"
/>
<title>Vue component</title>
<style>
:global(:root) {
font-family: system-ui;
padding: 1em;
}
</style>
</head>
<body>
<main>
<Result value={2345}></Result>
<Counter start="0">SSR Rendered, No Client</Counter>
<Counter start="1" client:load>SSR Rendered, client:load</Counter>
<!-- Test island deduplication, i.e. same UID as the component above. -->
<Counter start="1" client:load>SSR Rendered, client:load</Counter>
<!-- Test island deduplication account for non-render affecting props. -->
<Counter start="1" step-size="2" client:load>SSR Rendered, client:load</Counter>
<Counter start="10" client:idle>SSR Rendered, client:idle</Counter>
<!-- Test that two client:visibles have unique uids -->
<Counter start="100" client:visible>SSR Rendered, client:visible</Counter>
<Counter start="1000" client:visible>SSR Rendered, client:visible</Counter>
</main>
</body>
</html>

View file

@ -0,0 +1,30 @@
import { expect } from 'chai';
import * as cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Vue JSX', () => {
let fixture;
before(async () => {
fixture = await loadFixture({
root: './fixtures/vue-jsx/',
});
});
describe('build', () => {
before(async () => {
await fixture.build();
});
it('Can load Vue JSX', async () => {
const html = await fixture.readFile('/index.html');
const $ = cheerio.load(html);
const allPreValues = $('pre')
.toArray()
.map((el) => $(el).text());
expect(allPreValues).to.deep.equal(['2345', '0', '1', '1', '1', '10', '100', '1000']);
});
});
});

View file

@ -94,3 +94,40 @@ export default {
})],
}
```
### jsx
You can use Vue JSX by setting `jsx: true`.
__`astro.config.mjs`__
```js
import { defineConfig } from 'astro/config';
import vue from '@astrojs/vue';
export default defineConfig({
integrations: [
vue({ jsx: true })
],
});
```
This will enable rendering for both Vue and Vue JSX components. To customize the Vue JSX compiler, pass an options object instead of a boolean. See the `@vitejs/plugin-vue-jsx` [docs](https://github.com/vitejs/vite/tree/main/packages/plugin-vue-jsx) for more details.
__`astro.config.mjs`__
```js
import { defineConfig } from 'astro/config';
import vue from '@astrojs/vue';
export default defineConfig({
integrations: [
vue({
jsx: {
// treat any tag that starts with ion- as custom elements
isCustomElement: tag => tag.startsWith('ion-')
}
})
],
});
```

View file

@ -34,6 +34,8 @@
},
"dependencies": {
"@vitejs/plugin-vue": "^3.0.0",
"@vitejs/plugin-vue-jsx": "^2.0.1",
"@vue/babel-plugin-jsx": "^1.1.1",
"@vue/compiler-sfc": "^3.2.39"
},
"devDependencies": {

View file

@ -1,8 +1,13 @@
import type { Options } from '@vitejs/plugin-vue';
import type { Options as VueOptions } from '@vitejs/plugin-vue';
import type { Options as VueJsxOptions } from '@vitejs/plugin-vue-jsx';
import vue from '@vitejs/plugin-vue';
import type { AstroIntegration, AstroRenderer } from 'astro';
import type { UserConfig } from 'vite';
interface Options extends VueOptions {
jsx?: boolean | VueJsxOptions;
}
function getRenderer(): AstroRenderer {
return {
name: '@astrojs/vue',
@ -11,8 +16,23 @@ function getRenderer(): AstroRenderer {
};
}
function getViteConfiguration(options?: Options): UserConfig {
function getJsxRenderer(): AstroRenderer {
return {
name: '@astrojs/vue (jsx)',
clientEntrypoint: '@astrojs/vue/client.js',
serverEntrypoint: '@astrojs/vue/server.js',
jsxImportSource: 'vue',
jsxTransformOptions: async () => {
const jsxPlugin = (await import('@vue/babel-plugin-jsx')).default;
return {
plugins: [jsxPlugin],
};
},
};
}
async function getViteConfiguration(options?: Options): Promise<UserConfig> {
const config: UserConfig = {
optimizeDeps: {
include: ['@astrojs/vue/client.js', 'vue'],
exclude: ['@astrojs/vue/server.js'],
@ -23,15 +43,26 @@ function getViteConfiguration(options?: Options): UserConfig {
noExternal: ['vueperslides'],
},
};
if (options?.jsx) {
const vueJsx = (await import('@vitejs/plugin-vue-jsx')).default;
const jsxOptions = typeof options.jsx === 'object' ? options.jsx : undefined;
config.plugins?.push(vueJsx(jsxOptions));
}
return config;
}
export default function (options?: Options): AstroIntegration {
return {
name: '@astrojs/vue',
hooks: {
'astro:config:setup': ({ addRenderer, updateConfig }) => {
'astro:config:setup': async ({ addRenderer, updateConfig }) => {
addRenderer(getRenderer());
updateConfig({ vite: getViteConfiguration(options) });
if (options?.jsx) {
addRenderer(getJsxRenderer());
}
updateConfig({ vite: await getViteConfiguration(options) });
},
},
};

View file

@ -2319,6 +2319,16 @@ importers:
astro: link:../../..
vue: 3.2.39
packages/astro/test/fixtures/vue-jsx:
specifiers:
'@astrojs/vue': workspace:*
astro: workspace:*
vue: ^3.2.39
dependencies:
'@astrojs/vue': link:../../../../integrations/vue
astro: link:../../..
vue: 3.2.39
packages/astro/test/fixtures/vue-with-multi-renderer:
specifiers:
'@astrojs/svelte': workspace:*
@ -3044,6 +3054,8 @@ importers:
packages/integrations/vue:
specifiers:
'@vitejs/plugin-vue': ^3.0.0
'@vitejs/plugin-vue-jsx': ^2.0.1
'@vue/babel-plugin-jsx': ^1.1.1
'@vue/compiler-sfc': ^3.2.39
astro: workspace:*
astro-scripts: workspace:*
@ -3051,6 +3063,8 @@ importers:
vue: ^3.2.37
dependencies:
'@vitejs/plugin-vue': 3.1.0_vite@3.1.3+vue@3.2.39
'@vitejs/plugin-vue-jsx': 2.0.1_vite@3.1.3+vue@3.2.39
'@vue/babel-plugin-jsx': 1.1.1
'@vue/compiler-sfc': 3.2.39
devDependencies:
astro: link:../../astro
@ -4319,6 +4333,18 @@ packages:
'@babel/helper-plugin-utils': 7.19.0
dev: false
/@babel/plugin-syntax-import-meta/7.10.4_@babel+core@7.19.1:
resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==}
peerDependencies:
'@babel/core': ^7.0.0-0
peerDependenciesMeta:
'@babel/core':
optional: true
dependencies:
'@babel/core': 7.19.1
'@babel/helper-plugin-utils': 7.19.0
dev: false
/@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.19.1:
resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==}
peerDependencies:
@ -4454,6 +4480,19 @@ packages:
'@babel/helper-plugin-utils': 7.19.0
dev: false
/@babel/plugin-syntax-typescript/7.18.6_@babel+core@7.19.1:
resolution: {integrity: sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
peerDependenciesMeta:
'@babel/core':
optional: true
dependencies:
'@babel/core': 7.19.1
'@babel/helper-plugin-utils': 7.19.0
dev: false
/@babel/plugin-transform-arrow-functions/7.18.6_@babel+core@7.19.1:
resolution: {integrity: sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==}
engines: {node: '>=6.9.0'}
@ -4903,6 +4942,23 @@ packages:
'@babel/helper-plugin-utils': 7.19.0
dev: false
/@babel/plugin-transform-typescript/7.19.1_@babel+core@7.19.1:
resolution: {integrity: sha512-+ILcOU+6mWLlvCwnL920m2Ow3wWx3Wo8n2t5aROQmV55GZt+hOiLvBaa3DNzRjSEHa1aauRs4/YLmkCfFkhhRQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
peerDependenciesMeta:
'@babel/core':
optional: true
dependencies:
'@babel/core': 7.19.1
'@babel/helper-create-class-features-plugin': 7.19.0_@babel+core@7.19.1
'@babel/helper-plugin-utils': 7.19.0
'@babel/plugin-syntax-typescript': 7.18.6_@babel+core@7.19.1
transitivePeerDependencies:
- supports-color
dev: false
/@babel/plugin-transform-unicode-escapes/7.18.10_@babel+core@7.19.1:
resolution: {integrity: sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==}
engines: {node: '>=6.9.0'}
@ -9868,6 +9924,26 @@ packages:
- supports-color
dev: false
/@vitejs/plugin-vue-jsx/2.0.1_vite@3.1.3+vue@3.2.39:
resolution: {integrity: sha512-lmiR1k9+lrF7LMczO0pxtQ8mOn6XeppJDHxnpxkJQpT5SiKz4SKhKdeNstXaTNuR8qZhUo5X0pJlcocn72Y4Jg==}
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
vite: ^3.0.0
vue: ^3.0.0
peerDependenciesMeta:
vite:
optional: true
dependencies:
'@babel/core': 7.19.1
'@babel/plugin-syntax-import-meta': 7.10.4_@babel+core@7.19.1
'@babel/plugin-transform-typescript': 7.19.1_@babel+core@7.19.1
'@vue/babel-plugin-jsx': 1.1.1_@babel+core@7.19.1
vite: 3.1.3
vue: 3.2.39
transitivePeerDependencies:
- supports-color
dev: false
/@vitejs/plugin-vue/3.1.0_vite@3.1.3+vue@3.2.39:
resolution: {integrity: sha512-fmxtHPjSOEIRg6vHYDaem+97iwCUg/uSIaTzp98lhELt2ISOQuDo2hbkBdXod0g15IhfPMQmAxh4heUks2zvDA==}
engines: {node: ^14.18.0 || >=16.0.0}
@ -9893,6 +9969,44 @@ packages:
vscode-uri: 2.1.2
dev: false
/@vue/babel-helper-vue-transform-on/1.0.2:
resolution: {integrity: sha512-hz4R8tS5jMn8lDq6iD+yWL6XNB699pGIVLk7WSJnn1dbpjaazsjZQkieJoRX6gW5zpYSCFqQ7jUquPNY65tQYA==}
dev: false
/@vue/babel-plugin-jsx/1.1.1:
resolution: {integrity: sha512-j2uVfZjnB5+zkcbc/zsOc0fSNGCMMjaEXP52wdwdIfn0qjFfEYpYZBFKFg+HHnQeJCVrjOeO0YxgaL7DMrym9w==}
dependencies:
'@babel/helper-module-imports': 7.18.6
'@babel/plugin-syntax-jsx': 7.18.6
'@babel/template': 7.18.10
'@babel/traverse': 7.19.1
'@babel/types': 7.19.0
'@vue/babel-helper-vue-transform-on': 1.0.2
camelcase: 6.3.0
html-tags: 3.2.0
svg-tags: 1.0.0
transitivePeerDependencies:
- '@babel/core'
- supports-color
dev: false
/@vue/babel-plugin-jsx/1.1.1_@babel+core@7.19.1:
resolution: {integrity: sha512-j2uVfZjnB5+zkcbc/zsOc0fSNGCMMjaEXP52wdwdIfn0qjFfEYpYZBFKFg+HHnQeJCVrjOeO0YxgaL7DMrym9w==}
dependencies:
'@babel/helper-module-imports': 7.18.6
'@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.19.1
'@babel/template': 7.18.10
'@babel/traverse': 7.19.1
'@babel/types': 7.19.0
'@vue/babel-helper-vue-transform-on': 1.0.2
camelcase: 6.3.0
html-tags: 3.2.0
svg-tags: 1.0.0
transitivePeerDependencies:
- '@babel/core'
- supports-color
dev: false
/@vue/compiler-core/3.2.39:
resolution: {integrity: sha512-mf/36OWXqWn0wsC40nwRRGheR/qoID+lZXbIuLnr4/AngM0ov8Xvv8GHunC0rKRIkh60bTqydlqTeBo49rlbqw==}
dependencies:
@ -12988,6 +13102,11 @@ packages:
resolution: {integrity: sha512-lNovG8CMCCmcVB1Q7xggMSf7tqPCijZXaH4gL6iE8BFghdQCbaY5Met9i1x2Ex8m/cZHDUtXK9H6/znKamRP8Q==}
dev: true
/html-tags/3.2.0:
resolution: {integrity: sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==}
engines: {node: '>=8'}
dev: false
/html-void-elements/2.0.1:
resolution: {integrity: sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==}
dev: false
@ -16974,6 +17093,10 @@ packages:
svelte: 3.50.1
dev: false
/svg-tags/1.0.0:
resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==}
dev: false
/synckit/0.7.3:
resolution: {integrity: sha512-jNroMv7Juy+mJ/CHW5H6TzsLWpa1qck6sCHbkv8YTur+irSq2PjbvmGnm2gy14BUQ6jF33vyR4DPssHqmqsDQw==}
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}