初始化

This commit is contained in:
yziiy
2025-08-11 11:06:07 +08:00
parent 083bc37c00
commit 5607d11395
19772 changed files with 3108723 additions and 18 deletions

View File

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

View File

@@ -0,0 +1,73 @@
# @vitejs/plugin-vue-jsx [![npm](https://img.shields.io/npm/v/@vitejs/plugin-vue-jsx.svg)](https://npmjs.com/package/@vitejs/plugin-vue-jsx)
Provides Vue 3 JSX & TSX support with HMR.
```js
// vite.config.js
import vueJsx from '@vitejs/plugin-vue-jsx'
export default {
plugins: [
vueJsx({
// options are passed on to @vue/babel-plugin-jsx
}),
],
}
```
## Options
### include
Type: `(string | RegExp)[] | string | RegExp | null`
Default: `/\.[jt]sx$/`
A [picomatch pattern](https://github.com/micromatch/picomatch), or array of patterns, which specifies the files the plugin should operate on.
### exclude
Type: `(string | RegExp)[] | string | RegExp | null`
Default: `undefined`
A [picomatch pattern](https://github.com/micromatch/picomatch), or array of patterns, which specifies the files to be ignored by the plugin.
> See [@vue/babel-plugin-jsx](https://github.com/vuejs/jsx-next) for other options.
## HMR Detection
This plugin supports HMR of Vue JSX components. The detection requirements are:
- The component must be exported.
- The component must be declared by calling `defineComponent` via a root-level statement, either variable declaration or export declaration.
### Supported patterns
```jsx
import { defineComponent } from 'vue'
// named exports w/ variable declaration: ok
export const Foo = defineComponent({})
// named exports referencing variable declaration: ok
const Bar = defineComponent({ render() { return <div>Test</div> }})
export { Bar }
// default export call: ok
export default defineComponent({ render() { return <div>Test</div> }})
// default export referencing variable declaration: ok
const Baz = defineComponent({ render() { return <div>Test</div> }})
export default Baz
```
### Non-supported patterns
```jsx
// not using `defineComponent` call
export const Bar = { ... }
// not exported
const Foo = defineComponent(...)
```

View File

@@ -0,0 +1,243 @@
'use strict';
const node_crypto = require('node:crypto');
const path = require('node:path');
const babel = require('@babel/core');
const jsx = require('@vue/babel-plugin-jsx');
const vite = require('vite');
function _interopNamespaceDefault(e) {
const n = Object.create(null);
if (e) {
for (const k in e) {
n[k] = e[k];
}
}
n.default = e;
return n;
}
const babel__namespace = /*#__PURE__*/_interopNamespaceDefault(babel);
const ssrRegisterHelperId = "/__vue-jsx-ssr-register-helper";
const ssrRegisterHelperCode = `import { useSSRContext } from "vue"
export ${ssrRegisterHelper.toString()}`;
function ssrRegisterHelper(comp, filename) {
const setup = comp.setup;
comp.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add(filename);
if (setup) {
return setup(props, ctx);
}
};
}
function vueJsxPlugin(options = {}) {
let root = "";
let needHmr = false;
let needSourceMap = true;
const { include, exclude, babelPlugins = [], ...babelPluginOptions } = options;
const filter = vite.createFilter(include || /\.[jt]sx$/, exclude);
return {
name: "vite:vue-jsx",
config(config) {
return {
// only apply esbuild to ts files
// since we are handling jsx and tsx now
esbuild: {
include: /\.ts$/
},
define: {
__VUE_OPTIONS_API__: config.define?.__VUE_OPTIONS_API__ ?? true,
__VUE_PROD_DEVTOOLS__: config.define?.__VUE_PROD_DEVTOOLS__ ?? false
}
};
},
configResolved(config) {
needHmr = config.command === "serve" && !config.isProduction;
needSourceMap = config.command === "serve" || !!config.build.sourcemap;
root = config.root;
},
resolveId(id) {
if (id === ssrRegisterHelperId) {
return id;
}
},
load(id) {
if (id === ssrRegisterHelperId) {
return ssrRegisterHelperCode;
}
},
async transform(code, id, opt) {
const ssr = opt?.ssr === true;
const [filepath] = id.split("?");
if (filter(id) || filter(filepath)) {
const plugins = [[jsx, babelPluginOptions], ...babelPlugins];
if (id.endsWith(".tsx") || filepath.endsWith(".tsx")) {
plugins.push([
// @ts-ignore missing type
await import('@babel/plugin-transform-typescript').then(
(r) => r.default
),
// @ts-ignore
{ isTSX: true, allowExtensions: true }
]);
}
if (!ssr && !needHmr) {
plugins.push(() => {
return {
visitor: {
CallExpression: {
enter(_path) {
if (isDefineComponentCall(_path.node)) {
const callee = _path.node.callee;
callee.name = `/* @__PURE__ */ ${callee.name}`;
}
}
}
}
};
});
}
const result = babel__namespace.transformSync(code, {
babelrc: false,
ast: true,
plugins,
sourceMaps: needSourceMap,
sourceFileName: id,
configFile: false
});
if (!ssr && !needHmr) {
if (!result.code)
return;
return {
code: result.code,
map: result.map
};
}
const declaredComponents = [];
const hotComponents = [];
let hasDefault = false;
for (const node of result.ast.program.body) {
if (node.type === "VariableDeclaration") {
const names = parseComponentDecls(node);
if (names.length) {
declaredComponents.push(...names);
}
}
if (node.type === "ExportNamedDeclaration") {
if (node.declaration && node.declaration.type === "VariableDeclaration") {
hotComponents.push(
...parseComponentDecls(node.declaration).map(
({ name }) => ({
local: name,
exported: name,
id: getHash(id + name)
})
)
);
} else if (node.specifiers.length) {
for (const spec of node.specifiers) {
if (spec.type === "ExportSpecifier" && spec.exported.type === "Identifier") {
const matched = declaredComponents.find(
({ name }) => name === spec.local.name
);
if (matched) {
hotComponents.push({
local: spec.local.name,
exported: spec.exported.name,
id: getHash(id + spec.exported.name)
});
}
}
}
}
}
if (node.type === "ExportDefaultDeclaration") {
if (node.declaration.type === "Identifier") {
const _name = node.declaration.name;
const matched = declaredComponents.find(
({ name }) => name === _name
);
if (matched) {
hotComponents.push({
local: node.declaration.name,
exported: "default",
id: getHash(id + "default")
});
}
} else if (isDefineComponentCall(node.declaration)) {
hasDefault = true;
hotComponents.push({
local: "__default__",
exported: "default",
id: getHash(id + "default")
});
}
}
}
if (hotComponents.length) {
if (hasDefault && (needHmr || ssr)) {
result.code = result.code.replace(
/export default defineComponent/g,
`const __default__ = defineComponent`
) + `
export default __default__`;
}
if (needHmr && !ssr && !/\?vue&type=script/.test(id)) {
let code2 = result.code;
let callbackCode = ``;
for (const { local, exported, id: id2 } of hotComponents) {
code2 += `
${local}.__hmrId = "${id2}"
__VUE_HMR_RUNTIME__.createRecord("${id2}", ${local})`;
callbackCode += `
__VUE_HMR_RUNTIME__.reload("${id2}", __${exported})`;
}
code2 += `
import.meta.hot.accept(({${hotComponents.map((c) => `${c.exported}: __${c.exported}`).join(",")}}) => {${callbackCode}
})`;
result.code = code2;
}
if (ssr) {
const normalizedId = vite.normalizePath(path.relative(root, id));
let ssrInjectCode = `
import { ssrRegisterHelper } from "${ssrRegisterHelperId}"
const __moduleId = ${JSON.stringify(normalizedId)}`;
for (const { local } of hotComponents) {
ssrInjectCode += `
ssrRegisterHelper(${local}, __moduleId)`;
}
result.code += ssrInjectCode;
}
}
if (!result.code)
return;
return {
code: result.code,
map: result.map
};
}
}
};
}
function parseComponentDecls(node, source) {
const names = [];
for (const decl of node.declarations) {
if (decl.id.type === "Identifier" && isDefineComponentCall(decl.init)) {
names.push({
name: decl.id.name
});
}
}
return names;
}
function isDefineComponentCall(node) {
return node && node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "defineComponent";
}
function getHash(text) {
return node_crypto.createHash("sha256").update(text).digest("hex").substring(0, 8);
}
module.exports = vueJsxPlugin;
module.exports.default = vueJsxPlugin;

View File

@@ -0,0 +1,14 @@
import { FilterPattern, Plugin } from 'vite';
import { VueJSXPluginOptions } from '@vue/babel-plugin-jsx';
interface FilterOptions {
include?: FilterPattern;
exclude?: FilterPattern;
}
type Options = VueJSXPluginOptions & FilterOptions & {
babelPlugins?: any[];
};
declare function vueJsxPlugin(options?: Options): Plugin;
export { FilterOptions, Options, vueJsxPlugin as default };

View File

@@ -0,0 +1,227 @@
import { createHash } from 'node:crypto';
import path from 'node:path';
import * as babel from '@babel/core';
import jsx from '@vue/babel-plugin-jsx';
import { createFilter, normalizePath } from 'vite';
const ssrRegisterHelperId = "/__vue-jsx-ssr-register-helper";
const ssrRegisterHelperCode = `import { useSSRContext } from "vue"
export ${ssrRegisterHelper.toString()}`;
function ssrRegisterHelper(comp, filename) {
const setup = comp.setup;
comp.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add(filename);
if (setup) {
return setup(props, ctx);
}
};
}
function vueJsxPlugin(options = {}) {
let root = "";
let needHmr = false;
let needSourceMap = true;
const { include, exclude, babelPlugins = [], ...babelPluginOptions } = options;
const filter = createFilter(include || /\.[jt]sx$/, exclude);
return {
name: "vite:vue-jsx",
config(config) {
return {
// only apply esbuild to ts files
// since we are handling jsx and tsx now
esbuild: {
include: /\.ts$/
},
define: {
__VUE_OPTIONS_API__: config.define?.__VUE_OPTIONS_API__ ?? true,
__VUE_PROD_DEVTOOLS__: config.define?.__VUE_PROD_DEVTOOLS__ ?? false
}
};
},
configResolved(config) {
needHmr = config.command === "serve" && !config.isProduction;
needSourceMap = config.command === "serve" || !!config.build.sourcemap;
root = config.root;
},
resolveId(id) {
if (id === ssrRegisterHelperId) {
return id;
}
},
load(id) {
if (id === ssrRegisterHelperId) {
return ssrRegisterHelperCode;
}
},
async transform(code, id, opt) {
const ssr = opt?.ssr === true;
const [filepath] = id.split("?");
if (filter(id) || filter(filepath)) {
const plugins = [[jsx, babelPluginOptions], ...babelPlugins];
if (id.endsWith(".tsx") || filepath.endsWith(".tsx")) {
plugins.push([
// @ts-ignore missing type
await import('@babel/plugin-transform-typescript').then(
(r) => r.default
),
// @ts-ignore
{ isTSX: true, allowExtensions: true }
]);
}
if (!ssr && !needHmr) {
plugins.push(() => {
return {
visitor: {
CallExpression: {
enter(_path) {
if (isDefineComponentCall(_path.node)) {
const callee = _path.node.callee;
callee.name = `/* @__PURE__ */ ${callee.name}`;
}
}
}
}
};
});
}
const result = babel.transformSync(code, {
babelrc: false,
ast: true,
plugins,
sourceMaps: needSourceMap,
sourceFileName: id,
configFile: false
});
if (!ssr && !needHmr) {
if (!result.code)
return;
return {
code: result.code,
map: result.map
};
}
const declaredComponents = [];
const hotComponents = [];
let hasDefault = false;
for (const node of result.ast.program.body) {
if (node.type === "VariableDeclaration") {
const names = parseComponentDecls(node);
if (names.length) {
declaredComponents.push(...names);
}
}
if (node.type === "ExportNamedDeclaration") {
if (node.declaration && node.declaration.type === "VariableDeclaration") {
hotComponents.push(
...parseComponentDecls(node.declaration).map(
({ name }) => ({
local: name,
exported: name,
id: getHash(id + name)
})
)
);
} else if (node.specifiers.length) {
for (const spec of node.specifiers) {
if (spec.type === "ExportSpecifier" && spec.exported.type === "Identifier") {
const matched = declaredComponents.find(
({ name }) => name === spec.local.name
);
if (matched) {
hotComponents.push({
local: spec.local.name,
exported: spec.exported.name,
id: getHash(id + spec.exported.name)
});
}
}
}
}
}
if (node.type === "ExportDefaultDeclaration") {
if (node.declaration.type === "Identifier") {
const _name = node.declaration.name;
const matched = declaredComponents.find(
({ name }) => name === _name
);
if (matched) {
hotComponents.push({
local: node.declaration.name,
exported: "default",
id: getHash(id + "default")
});
}
} else if (isDefineComponentCall(node.declaration)) {
hasDefault = true;
hotComponents.push({
local: "__default__",
exported: "default",
id: getHash(id + "default")
});
}
}
}
if (hotComponents.length) {
if (hasDefault && (needHmr || ssr)) {
result.code = result.code.replace(
/export default defineComponent/g,
`const __default__ = defineComponent`
) + `
export default __default__`;
}
if (needHmr && !ssr && !/\?vue&type=script/.test(id)) {
let code2 = result.code;
let callbackCode = ``;
for (const { local, exported, id: id2 } of hotComponents) {
code2 += `
${local}.__hmrId = "${id2}"
__VUE_HMR_RUNTIME__.createRecord("${id2}", ${local})`;
callbackCode += `
__VUE_HMR_RUNTIME__.reload("${id2}", __${exported})`;
}
code2 += `
import.meta.hot.accept(({${hotComponents.map((c) => `${c.exported}: __${c.exported}`).join(",")}}) => {${callbackCode}
})`;
result.code = code2;
}
if (ssr) {
const normalizedId = normalizePath(path.relative(root, id));
let ssrInjectCode = `
import { ssrRegisterHelper } from "${ssrRegisterHelperId}"
const __moduleId = ${JSON.stringify(normalizedId)}`;
for (const { local } of hotComponents) {
ssrInjectCode += `
ssrRegisterHelper(${local}, __moduleId)`;
}
result.code += ssrInjectCode;
}
}
if (!result.code)
return;
return {
code: result.code,
map: result.map
};
}
}
};
}
function parseComponentDecls(node, source) {
const names = [];
for (const decl of node.declarations) {
if (decl.id.type === "Identifier" && isDefineComponentCall(decl.init)) {
names.push({
name: decl.id.name
});
}
}
return names;
}
function isDefineComponentCall(node) {
return node && node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "defineComponent";
}
function getHash(text) {
return createHash("sha256").update(text).digest("hex").substring(0, 8);
}
export { vueJsxPlugin as default };

View File

@@ -0,0 +1,49 @@
{
"name": "@vitejs/plugin-vue-jsx",
"version": "3.0.1",
"license": "MIT",
"author": "Evan You",
"files": [
"dist"
],
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
},
"scripts": {
"dev": "unbuild --stub",
"build": "unbuild && pnpm run patch-cjs",
"patch-cjs": "tsx ../../scripts/patchCJS.ts",
"prepublishOnly": "npm run build"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"repository": {
"type": "git",
"url": "git+https://github.com/vitejs/vite-plugin-vue.git",
"directory": "packages/plugin-vue-jsx"
},
"bugs": {
"url": "https://github.com/vitejs/vite-plugin-vue/issues"
},
"homepage": "https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue-jsx#readme",
"dependencies": {
"@babel/core": "^7.20.7",
"@babel/plugin-transform-typescript": "^7.20.7",
"@vue/babel-plugin-jsx": "^1.1.1"
},
"devDependencies": {
"vite": "^4.0.3"
},
"peerDependencies": {
"vite": "^4.0.0",
"vue": "^3.0.0"
}
}