parent
24bad5a0ad
commit
fd9d323a68
12 changed files with 329 additions and 5 deletions
6
.changeset/honest-melons-walk.md
Normal file
6
.changeset/honest-melons-walk.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
'@astrojs/vue': minor
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Support Vue JSX
|
|
@ -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'];
|
||||
}
|
||||
|
|
7
packages/astro/test/fixtures/vue-jsx/astro.config.mjs
vendored
Normal file
7
packages/astro/test/fixtures/vue-jsx/astro.config.mjs
vendored
Normal 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 })],
|
||||
});
|
10
packages/astro/test/fixtures/vue-jsx/package.json
vendored
Normal file
10
packages/astro/test/fixtures/vue-jsx/package.json
vendored
Normal 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"
|
||||
}
|
||||
}
|
28
packages/astro/test/fixtures/vue-jsx/src/components/Counter.jsx
vendored
Normal file
28
packages/astro/test/fixtures/vue-jsx/src/components/Counter.jsx
vendored
Normal 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>
|
||||
)
|
||||
},
|
||||
})
|
15
packages/astro/test/fixtures/vue-jsx/src/components/Result.vue
vendored
Normal file
15
packages/astro/test/fixtures/vue-jsx/src/components/Result.vue
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
<template>
|
||||
<pre>{{ value }}</pre>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
35
packages/astro/test/fixtures/vue-jsx/src/pages/index.astro
vendored
Normal file
35
packages/astro/test/fixtures/vue-jsx/src/pages/index.astro
vendored
Normal 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>
|
30
packages/astro/test/vue-jsx.test.js
Normal file
30
packages/astro/test/vue-jsx.test.js
Normal 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']);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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-')
|
||||
}
|
||||
})
|
||||
],
|
||||
});
|
||||
```
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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) });
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
123
pnpm-lock.yaml
123
pnpm-lock.yaml
|
@ -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}
|
||||
|
|
Loading…
Reference in a new issue