Skip to content

Commit 3a2eb55

Browse files
committed
feat: vue-jsx support
1 parent a55eebc commit 3a2eb55

File tree

5 files changed

+290
-0
lines changed

5 files changed

+290
-0
lines changed

Diff for: packages/plugin-vue-jsx/LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2019-present, Yuxi (Evan) You and Vite contributors
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Diff for: packages/plugin-vue-jsx/README.md

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# @vitejs/plugin-vue-jsx
2+
3+
Provides optimized Vue 3 JSX support via [@vue/babel-plugin-jsx](https://github.com/vuejs/jsx-next).
4+
5+
```js
6+
// vite.config.js
7+
import vueJsx from '@vitejs/plugin-vue-jsx'
8+
9+
export default {
10+
plugins: [vueJsx({
11+
// options are passed on to @vue/babel-plugin-jsx
12+
})]
13+
}
14+
```

Diff for: packages/plugin-vue-jsx/index.d.ts

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Plugin } from 'vite'
2+
3+
// https://github.com/vuejs/jsx-next/tree/dev/packages/babel-plugin-jsx#options
4+
export interface Options {
5+
transformOn?: boolean
6+
optimize?: boolean
7+
isCustomElement?: (tag: string) => boolean
8+
mergeProps?: boolean
9+
}
10+
11+
declare function createPlugin(options?: Options): Plugin
12+
13+
export default createPlugin

Diff for: packages/plugin-vue-jsx/index.js

+209
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
// @ts-check
2+
const babel = require('@babel/core')
3+
const jsx = require('@vue/babel-plugin-jsx')
4+
const importMeta = require('@babel/plugin-syntax-import-meta')
5+
const hash = require('hash-sum')
6+
7+
/**
8+
* @param {import('.').Options} options
9+
* @returns {import('vite').Plugin}
10+
*/
11+
module.exports = function vueJsxPlugin(options = {}) {
12+
let needHmr = false
13+
14+
return {
15+
name: 'vue-jsx',
16+
17+
config(config) {
18+
return {
19+
esbuild: false,
20+
define: {
21+
__VUE_OPTIONS_API__: true,
22+
__VUE_PROD_DEVTOOLS__: false,
23+
...config.define
24+
}
25+
}
26+
},
27+
28+
configResolved(config) {
29+
needHmr = config.command === 'serve' && !config.isProduction
30+
},
31+
32+
transform(code, id) {
33+
if (/\.[jt]sx$/.test(id)) {
34+
const plugins = [importMeta, [jsx, options]]
35+
if (id.endsWith('.tsx')) {
36+
plugins.push([
37+
require('@babel/plugin-transform-typescript'),
38+
// @ts-ignore
39+
{ isTSX: true, allowExtensions: true }
40+
])
41+
}
42+
43+
const result = babel.transformSync(code, {
44+
ast: true,
45+
plugins,
46+
sourceMaps: true,
47+
sourceFileName: id
48+
})
49+
50+
if (!needHmr) {
51+
return {
52+
code: result.code,
53+
map: result.map
54+
}
55+
}
56+
57+
// check for hmr injection
58+
/**
59+
* @type {{ name: string, hash: string }[]}
60+
*/
61+
const declaredComponents = []
62+
/**
63+
* @type {{
64+
* local: string,
65+
* exported: string,
66+
* id: string,
67+
* hash: string
68+
* }[]}
69+
*/
70+
const hotComponents = []
71+
let hasDefault = false
72+
73+
for (const node of result.ast.program.body) {
74+
if (node.type === 'VariableDeclaration') {
75+
const names = parseComponentDecls(node, code)
76+
if (names.length) {
77+
declaredComponents.push(...names)
78+
}
79+
}
80+
81+
if (node.type === 'ExportNamedDeclaration') {
82+
if (
83+
node.declaration &&
84+
node.declaration.type === 'VariableDeclaration'
85+
) {
86+
hotComponents.push(
87+
...parseComponentDecls(node.declaration, code).map(
88+
({ name, hash: _hash }) => ({
89+
local: name,
90+
exported: name,
91+
id: hash(id + name),
92+
hash: _hash
93+
})
94+
)
95+
)
96+
} else if (node.specifiers.length) {
97+
for (const spec of node.specifiers) {
98+
if (
99+
spec.type === 'ExportSpecifier' &&
100+
spec.exported.type === 'Identifier'
101+
) {
102+
const matched = declaredComponents.find(
103+
({ name }) => name === spec.local.name
104+
)
105+
if (matched) {
106+
hotComponents.push({
107+
local: spec.local.name,
108+
exported: spec.exported.name,
109+
id: hash(id + spec.exported.name),
110+
hash: matched.hash
111+
})
112+
}
113+
}
114+
}
115+
}
116+
}
117+
118+
if (node.type === 'ExportDefaultDeclaration') {
119+
if (node.declaration.type === 'Identifier') {
120+
const _name = node.declaration.name
121+
const matched = declaredComponents.find(
122+
({ name }) => name === _name
123+
)
124+
if (matched) {
125+
hotComponents.push({
126+
local: node.declaration.name,
127+
exported: 'default',
128+
id: hash(id + 'default'),
129+
hash: matched.hash
130+
})
131+
}
132+
} else if (isDefineComponentCall(node.declaration)) {
133+
hasDefault = true
134+
hotComponents.push({
135+
local: '__default__',
136+
exported: 'default',
137+
id: hash(id + 'default'),
138+
hash: hash(
139+
code.slice(node.declaration.start, node.declaration.end)
140+
)
141+
})
142+
}
143+
}
144+
}
145+
146+
if (hotComponents.length) {
147+
let code = result.code
148+
if (hasDefault) {
149+
code =
150+
code.replace(
151+
/export default defineComponent/g,
152+
`const __default__ = defineComponent`
153+
) + `\nexport default __default__`
154+
}
155+
156+
let callbackCode = ``
157+
for (const { local, exported, id, hash } of hotComponents) {
158+
code +=
159+
`\n${local}.__hmrId = "${id}"` +
160+
`\n${local}.__hmrHash = "${hash}"` +
161+
`\n__VUE_HMR_RUNTIME__.createRecord("${id}", ${local})`
162+
callbackCode +=
163+
`\n if (__${exported}.__hmrHash !== ${local}.__hmrHash) ` +
164+
`__VUE_HMR_RUNTIME__.reload("${id}", __${exported})`
165+
}
166+
167+
code += `\nimport.meta.hot.accept(({${hotComponents
168+
.map((c) => `${c.exported}: __${c.exported}`)
169+
.join(',')}}) => {${callbackCode}\n})`
170+
171+
result.code = code
172+
}
173+
174+
return {
175+
code: result.code,
176+
map: result.map
177+
}
178+
}
179+
}
180+
}
181+
}
182+
183+
/**
184+
* @param {import('@babel/core').types.VariableDeclaration} node
185+
* @param {string} source
186+
*/
187+
function parseComponentDecls(node, source) {
188+
const names = []
189+
for (const decl of node.declarations) {
190+
if (decl.id.type === 'Identifier' && isDefineComponentCall(decl.init)) {
191+
names.push({
192+
name: decl.id.name,
193+
hash: hash(source.slice(decl.init.start, decl.init.end))
194+
})
195+
}
196+
}
197+
return names
198+
}
199+
200+
/**
201+
* @param {import('@babel/core').types.Node} node
202+
*/
203+
function isDefineComponentCall(node) {
204+
return (
205+
node.type === 'CallExpression' &&
206+
node.callee.type === 'Identifier' &&
207+
node.callee.name === 'defineComponent'
208+
)
209+
}

Diff for: packages/plugin-vue-jsx/package.json

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"name": "@vitejs/plugin-vue-jsx",
3+
"version": "1.0.0",
4+
"license": "MIT",
5+
"author": "Evan You",
6+
"files": [
7+
"index.js"
8+
],
9+
"main": "index.js",
10+
"types": "index.d.ts",
11+
"scripts": {
12+
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s --commit-path . --lerna-package plugin-vue-jsx",
13+
"release": "node ../../scripts/release.js --skipBuild"
14+
},
15+
"engines": {
16+
"node": ">=12.0.0"
17+
},
18+
"repository": {
19+
"type": "git",
20+
"url": "git+https://github.com/vitejs/vite.git"
21+
},
22+
"bugs": {
23+
"url": "https://github.com/vitejs/vite/issues"
24+
},
25+
"homepage": "https://github.com/vitejs/vite/tree/main/packages/plugin-vue-jsx#readme",
26+
"dependencies": {
27+
"@babel/core": "^7.12.10",
28+
"@babel/plugin-syntax-import-meta": "^7.10.4",
29+
"@babel/plugin-transform-typescript": "^7.12.1",
30+
"@vue/babel-plugin-jsx": "^1.0.0",
31+
"hash-sum": "^2.0.0"
32+
}
33+
}

0 commit comments

Comments
 (0)