diff --git a/packages/plugin-vue-jsx/package.json b/packages/plugin-vue-jsx/package.json index 2b29d9f0..0a9b17b6 100644 --- a/packages/plugin-vue-jsx/package.json +++ b/packages/plugin-vue-jsx/package.json @@ -37,6 +37,7 @@ "dependencies": { "@babel/core": "^7.27.1", "@babel/plugin-transform-typescript": "^7.27.1", + "@rolldown/pluginutils": "^1.0.0-beta.9", "@vue/babel-plugin-jsx": "^1.4.0" }, "devDependencies": { diff --git a/packages/plugin-vue-jsx/src/index.ts b/packages/plugin-vue-jsx/src/index.ts index 7234bd39..bdec6c58 100644 --- a/packages/plugin-vue-jsx/src/index.ts +++ b/packages/plugin-vue-jsx/src/index.ts @@ -6,6 +6,10 @@ import jsx from '@vue/babel-plugin-jsx' import { createFilter, normalizePath } from 'vite' import type { ComponentOptions } from 'vue' import type { Plugin } from 'vite' +import { + exactRegex, + makeIdFiltersToMatchWithQuery, +} from '@rolldown/pluginutils' import type { Options } from './types' export * from './types' @@ -40,14 +44,14 @@ function vueJsxPlugin(options: Options = {}): Plugin { let needSourceMap = true const { - include, + include = /\.[jt]sx$/, exclude, babelPlugins = [], defineComponentName = ['defineComponent'], tsPluginOptions = {}, ...babelPluginOptions } = options - const filter = createFilter(include || /\.[jt]sx$/, exclude) + const filter = createFilter(include, exclude) return { name: 'vite:vue-jsx', @@ -85,197 +89,213 @@ function vueJsxPlugin(options: Options = {}): Plugin { root = config.root }, - resolveId(id) { - if (id === ssrRegisterHelperId) { - return id - } + resolveId: { + filter: { id: exactRegex(ssrRegisterHelperId) }, + handler(id) { + if (id === ssrRegisterHelperId) { + return id + } + }, }, - load(id) { - if (id === ssrRegisterHelperId) { - return ssrRegisterHelperCode - } + load: { + filter: { id: exactRegex(ssrRegisterHelperId) }, + handler(id) { + if (id === ssrRegisterHelperId) { + return ssrRegisterHelperCode + } + }, }, - async transform(code, id, opt) { - const ssr = opt?.ssr === true - const [filepath] = id.split('?') + transform: { + filter: { + id: { + include: include ? makeIdFiltersToMatchWithQuery(include) : undefined, + exclude: exclude ? makeIdFiltersToMatchWithQuery(exclude) : undefined, + }, + }, + async handler(code, id, opt) { + const ssr = opt?.ssr === true + const [filepath] = id.split('?') - // use id for script blocks in Vue SFCs (e.g. `App.vue?vue&type=script&lang.jsx`) - // use filepath for plain jsx files (e.g. App.jsx) - 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 - { ...tsPluginOptions, isTSX: true, allowExtensions: true }, - ]) - } + // use id for script blocks in Vue SFCs (e.g. `App.vue?vue&type=script&lang.jsx`) + // use filepath for plain jsx files (e.g. App.jsx) + 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 + { ...tsPluginOptions, isTSX: true, allowExtensions: true }, + ]) + } - if (!ssr && !needHmr) { - plugins.push(() => { - return { - visitor: { - CallExpression: { - enter(_path: babel.NodePath) { - if ( - isDefineComponentCall(_path.node, defineComponentName) - ) { - const callee = _path.node.callee as types.Identifier - callee.name = `/* @__PURE__ */ ${callee.name}` - } + if (!ssr && !needHmr) { + plugins.push(() => { + return { + visitor: { + CallExpression: { + enter(_path: babel.NodePath) { + if ( + isDefineComponentCall(_path.node, defineComponentName) + ) { + const callee = _path.node.callee as types.Identifier + callee.name = `/* @__PURE__ */ ${callee.name}` + } + }, }, }, - }, - } - }) - } + } + }) + } - const result = babel.transformSync(code, { - babelrc: false, - ast: true, - plugins, - sourceMaps: needSourceMap, - sourceFileName: id, - configFile: false, - })! + 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, + if (!ssr && !needHmr) { + if (!result.code) return + return { + code: result.code, + map: result.map, + } } - } - interface HotComponent { - local: string - exported: string - id: string - } + interface HotComponent { + local: string + exported: string + id: string + } - // check for hmr injection - const declaredComponents: string[] = [] - const hotComponents: HotComponent[] = [] - let hasDefault = false + // check for hmr injection + const declaredComponents: string[] = [] + const hotComponents: HotComponent[] = [] + let hasDefault = false - for (const node of result.ast!.program.body) { - if (node.type === 'VariableDeclaration') { - const names = parseComponentDecls(node, defineComponentName) - if (names.length) { - declaredComponents.push(...names) + for (const node of result.ast!.program.body) { + if (node.type === 'VariableDeclaration') { + const names = parseComponentDecls(node, defineComponentName) + if (names.length) { + declaredComponents.push(...names) + } } - } - if (node.type === 'ExportNamedDeclaration') { - if ( - node.declaration && - node.declaration.type === 'VariableDeclaration' - ) { - hotComponents.push( - ...parseComponentDecls( - node.declaration, - defineComponentName, - ).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 === 'ExportNamedDeclaration') { + if ( + node.declaration && + node.declaration.type === 'VariableDeclaration' + ) { + hotComponents.push( + ...parseComponentDecls( + node.declaration, + defineComponentName, + ).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) { + 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: _name, + exported: 'default', + id: getHash(id + 'default'), + }) + } + } else if ( + isDefineComponentCall(node.declaration, defineComponentName) + ) { + hasDefault = true hotComponents.push({ - local: _name, + local: '__default__', exported: 'default', id: getHash(id + 'default'), }) } - } else if ( - isDefineComponentCall(node.declaration, defineComponentName) - ) { - 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`, - ) + `\nexport default __default__` - } - if (needHmr && !ssr && !/\?vue&type=script/.test(id)) { - let code = result.code - let callbackCode = `` - for (const { local, exported, id } of hotComponents) { - code += - `\n${local}.__hmrId = "${id}"` + - `\n__VUE_HMR_RUNTIME__.createRecord("${id}", ${local})` - callbackCode += `\n__VUE_HMR_RUNTIME__.reload("${id}", __${exported})` + if (hotComponents.length) { + if (hasDefault && (needHmr || ssr)) { + result.code = + result.code!.replace( + /export default defineComponent/g, + `const __default__ = defineComponent`, + ) + `\nexport default __default__` } - const newCompNames = hotComponents - .map((c) => `${c.exported}: __${c.exported}`) - .join(',') + if (needHmr && !ssr && !/\?vue&type=script/.test(id)) { + let code = result.code + let callbackCode = `` + for (const { local, exported, id } of hotComponents) { + code += + `\n${local}.__hmrId = "${id}"` + + `\n__VUE_HMR_RUNTIME__.createRecord("${id}", ${local})` + callbackCode += `\n__VUE_HMR_RUNTIME__.reload("${id}", __${exported})` + } - code += `\nimport.meta.hot.accept(({${newCompNames}}) => {${callbackCode}\n})` - result.code = code - } + const newCompNames = hotComponents + .map((c) => `${c.exported}: __${c.exported}`) + .join(',') - if (ssr) { - const normalizedId = normalizePath(path.relative(root, id)) - let ssrInjectCode = - `\nimport { ssrRegisterHelper } from "${ssrRegisterHelperId}"` + - `\nconst __moduleId = ${JSON.stringify(normalizedId)}` - for (const { local } of hotComponents) { - ssrInjectCode += `\nssrRegisterHelper(${local}, __moduleId)` + code += `\nimport.meta.hot.accept(({${newCompNames}}) => {${callbackCode}\n})` + result.code = code + } + + if (ssr) { + const normalizedId = normalizePath(path.relative(root, id)) + let ssrInjectCode = + `\nimport { ssrRegisterHelper } from "${ssrRegisterHelperId}"` + + `\nconst __moduleId = ${JSON.stringify(normalizedId)}` + for (const { local } of hotComponents) { + ssrInjectCode += `\nssrRegisterHelper(${local}, __moduleId)` + } + result.code += ssrInjectCode } - result.code += ssrInjectCode } - } - if (!result.code) return - return { - code: result.code, - map: result.map, + if (!result.code) return + return { + code: result.code, + map: result.map, + } } - } + }, }, } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index df2bc9ee..a9fb113d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -146,6 +146,9 @@ importers: '@babel/plugin-transform-typescript': specifier: ^7.27.1 version: 7.27.1(@babel/core@7.27.1) + '@rolldown/pluginutils': + specifier: ^1.0.0-beta.9 + version: 1.0.0-beta.9 '@vue/babel-plugin-jsx': specifier: ^1.4.0 version: 1.4.0(@babel/core@7.27.1) @@ -1445,6 +1448,9 @@ packages: resolution: {integrity: sha512-TvCl79Y8v18ZhFGd5mjO1kYPovSBq3+4LVCi5Nfl1JI8fS8i8kXbgQFGwBJRXczim8GlW8c2LMBKTtExYXOy/A==} engines: {node: '>=18'} + '@rolldown/pluginutils@1.0.0-beta.9': + resolution: {integrity: sha512-e9MeMtVWo186sgvFFJOPGy7/d2j2mZhLJIdVW0C/xDluuOvymEATqz6zKsP0ZmXGzQtqlyjz5sC1sYQUoJG98w==} + '@rollup/plugin-alias@5.1.1': resolution: {integrity: sha512-PR9zDb+rOzkRb2VD+EuKB7UC41vU5DIwZ5qqCpk0KJudcWAyi8rvYOhS7+L5aZCspw1stTViLgN5v6FF1p5cgQ==} engines: {node: '>=14.0.0'} @@ -5782,6 +5788,8 @@ snapshots: '@publint/pack@0.1.1': {} + '@rolldown/pluginutils@1.0.0-beta.9': {} + '@rollup/plugin-alias@5.1.1(rollup@4.40.2)': optionalDependencies: rollup: 4.40.2 @@ -6336,7 +6344,7 @@ snapshots: '@vue/compiler-core@3.5.13': dependencies: - '@babel/parser': 7.27.0 + '@babel/parser': 7.27.2 '@vue/shared': 3.5.13 entities: 4.5.0 estree-walker: 2.0.2 @@ -6349,7 +6357,7 @@ snapshots: '@vue/compiler-sfc@3.5.13': dependencies: - '@babel/parser': 7.27.0 + '@babel/parser': 7.27.2 '@vue/compiler-core': 3.5.13 '@vue/compiler-dom': 3.5.13 '@vue/compiler-ssr': 3.5.13