diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9b604a7d..e0a47703 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,13 +37,13 @@ jobs: strategy: matrix: os: [ubuntu-latest] - node_version: [18, 20] + node_version: [20, 22, 24] include: # Active LTS + other OS - os: macos-latest - node_version: 20 + node_version: 22 - os: windows-latest - node_version: 20 + node_version: 22 fail-fast: false name: "Build&Test: node-${{ matrix.node_version }}, ${{ matrix.os }}" diff --git a/eslint.config.js b/eslint.config.js index c0c7d262..688137a1 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -120,13 +120,13 @@ export default tseslint.config( 'n/no-unsupported-features/es-builtins': [ 'error', { - version: '^18.0.0 || >=20.0.0', + version: '^20.19.0 || >=22.12.0', }, ], 'n/no-unsupported-features/node-builtins': [ 'error', { - version: '^18.0.0 || >=20.0.0', + version: '^20.19.0 || >=22.12.0', }, ], '@typescript-eslint/explicit-module-boundary-types': 'off', @@ -150,7 +150,7 @@ export default tseslint.config( 'n/no-unsupported-features/node-builtins': [ 'error', { - version: '^18.0.0 || >=20.0.0', + version: '^20.19.0 || >=22.12.0', allowExperimental: true, }, ], diff --git a/package.json b/package.json index a01f44cc..a5106ad6 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "private": true, "type": "module", "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^20.19.0 || >=22.12.0" }, "homepage": "https://github.com/vitejs/vite-plugin-vue/", "repository": { @@ -36,34 +36,34 @@ "ci-publish": "tsx scripts/publishCI.ts" }, "devDependencies": { - "@babel/types": "^7.27.1", - "@eslint/js": "^9.26.0", + "@babel/types": "^7.27.6", + "@eslint/js": "^9.28.0", "@types/babel__core": "^7.20.5", "@types/convert-source-map": "^2.0.3", "@types/debug": "^4.1.12", "@types/fs-extra": "^11.0.4", - "@types/node": "^22.15.17", + "@types/node": "^22.15.31", "@vitejs/release-scripts": "^1.5.0", "conventional-changelog-cli": "^5.0.0", - "eslint": "^9.26.0", - "eslint-import-resolver-typescript": "^4.3.4", - "eslint-plugin-import-x": "^4.11.0", - "eslint-plugin-n": "^17.18.0", - "eslint-plugin-regexp": "^2.7.0", - "execa": "^9.5.3", + "eslint": "^9.28.0", + "eslint-import-resolver-typescript": "^4.4.3", + "eslint-plugin-import-x": "^4.15.1", + "eslint-plugin-n": "^17.19.0", + "eslint-plugin-regexp": "^2.9.0", + "execa": "^9.6.0", "fs-extra": "^11.3.0", - "lint-staged": "^15.5.2", + "lint-staged": "^16.1.0", "picocolors": "^1.1.1", - "playwright-chromium": "^1.52.0", + "playwright-chromium": "^1.53.0", "prettier": "3.5.3", - "rollup": "^4.40.2", + "rollup": "^4.43.0", "simple-git-hooks": "^2.13.0", - "tsx": "^4.19.4", + "tsx": "^4.20.1", "typescript": "^5.8.3", - "typescript-eslint": "^8.32.0", + "typescript-eslint": "^8.34.0", "unbuild": "3.5.0", "vite": "catalog:", - "vitest": "^3.1.3", + "vitest": "^3.2.3", "vue": "catalog:" }, "simple-git-hooks": { @@ -83,7 +83,7 @@ "eslint --cache --fix" ] }, - "packageManager": "pnpm@10.10.0", + "packageManager": "pnpm@10.12.1", "pnpm": { "overrides": { "@vitejs/plugin-vue": "workspace:*" diff --git a/packages/plugin-vue-jsx/CHANGELOG.md b/packages/plugin-vue-jsx/CHANGELOG.md index 9141429d..2b7b3e68 100644 --- a/packages/plugin-vue-jsx/CHANGELOG.md +++ b/packages/plugin-vue-jsx/CHANGELOG.md @@ -1,3 +1,18 @@ +## 5.0.0-beta.0 (2025-06-06) + +* refactor!: bump required node version to 20.19+, 22.12+ and drop CJS build (#596) ([56df545](https://github.com/vitejs/vite-plugin-vue/commit/56df545)), closes [#596](https://github.com/vitejs/vite-plugin-vue/issues/596) +* feat: add Vite 7 support (#597) ([12f2881](https://github.com/vitejs/vite-plugin-vue/commit/12f2881)), closes [#597](https://github.com/vitejs/vite-plugin-vue/issues/597) + + + +## 4.2.0 (2025-05-20) + +* feat(vue-jsx): add filter (#581) ([f66a009](https://github.com/vitejs/vite-plugin-vue/commit/f66a009)), closes [#581](https://github.com/vitejs/vite-plugin-vue/issues/581) +* fix(deps): update all non-major dependencies (#527) ([8495d12](https://github.com/vitejs/vite-plugin-vue/commit/8495d12)), closes [#527](https://github.com/vitejs/vite-plugin-vue/issues/527) +* fix(deps): update all non-major dependencies (#578) ([405647f](https://github.com/vitejs/vite-plugin-vue/commit/405647f)), closes [#578](https://github.com/vitejs/vite-plugin-vue/issues/578) + + + ## 4.1.2 (2025-03-17) * fix: properly interpret boolean values in `define` (#545) ([46d3d65](https://github.com/vitejs/vite-plugin-vue/commit/46d3d65)), closes [#545](https://github.com/vitejs/vite-plugin-vue/issues/545) diff --git a/packages/plugin-vue-jsx/build.config.ts b/packages/plugin-vue-jsx/build.config.ts index 360c2e7c..a74f5d85 100644 --- a/packages/plugin-vue-jsx/build.config.ts +++ b/packages/plugin-vue-jsx/build.config.ts @@ -4,7 +4,4 @@ export default defineBuildConfig({ entries: ['src/index'], clean: true, declaration: true, - rollup: { - emitCJS: true, - }, }) diff --git a/packages/plugin-vue-jsx/package.json b/packages/plugin-vue-jsx/package.json index 2b29d9f0..818c534b 100644 --- a/packages/plugin-vue-jsx/package.json +++ b/packages/plugin-vue-jsx/package.json @@ -1,29 +1,23 @@ { "name": "@vitejs/plugin-vue-jsx", - "version": "4.1.2", - "type": "commonjs", + "version": "5.0.0-beta.0", + "type": "module", "license": "MIT", "author": "Evan You", "files": [ "dist" ], - "main": "./dist/index.cjs", - "module": "./dist/index.mjs", - "types": "./dist/index.d.ts", "exports": { - ".": { - "import": "./dist/index.mjs", - "require": "./dist/index.cjs" - } + ".": "./dist/index.mjs", + "./package.json": "./package.json" }, "scripts": { "dev": "unbuild --stub", - "build": "unbuild && pnpm run patch-cjs", - "patch-cjs": "tsx ../../scripts/patchCJS.ts", + "build": "unbuild", "prepublishOnly": "npm run build" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^20.19.0 || >=22.12.0" }, "repository": { "type": "git", @@ -35,15 +29,16 @@ }, "homepage": "https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue-jsx#readme", "dependencies": { - "@babel/core": "^7.27.1", + "@babel/core": "^7.27.4", "@babel/plugin-transform-typescript": "^7.27.1", + "@rolldown/pluginutils": "^1.0.0-beta.15", "@vue/babel-plugin-jsx": "^1.4.0" }, "devDependencies": { "vite": "catalog:" }, "peerDependencies": { - "vite": "^5.0.0 || ^6.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vue": "^3.0.0" } } diff --git a/packages/plugin-vue-jsx/src/index.ts b/packages/plugin-vue-jsx/src/index.ts index 7234bd39..2bfb06fb 100644 --- a/packages/plugin-vue-jsx/src/index.ts +++ b/packages/plugin-vue-jsx/src/index.ts @@ -6,9 +6,13 @@ 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' +export type * from './types' const ssrRegisterHelperId = '/__vue-jsx-ssr-register-helper' const ssrRegisterHelperCode = @@ -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(',') + + 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)` + 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, + } } - } + }, }, } } @@ -308,17 +328,17 @@ function isDefineComponentCall( ) } -const hash = - // eslint-disable-next-line n/no-unsupported-features/node-builtins -- crypto.hash is supported in Node 21.7.0+, 20.12.0+ - crypto.hash ?? - (( - algorithm: string, - data: crypto.BinaryLike, - outputEncoding: crypto.BinaryToTextEncoding, - ) => crypto.createHash(algorithm).update(data).digest(outputEncoding)) - function getHash(text: string) { - return hash('sha256', text, 'hex').substring(0, 8) + return crypto.hash('sha256', text, 'hex').substring(0, 8) } export default vueJsxPlugin + +// Compat for require +function vueJsxPluginCjs(this: unknown, options: Options): Plugin { + return vueJsxPlugin.call(this, options) +} +Object.assign(vueJsxPluginCjs, { + default: vueJsxPluginCjs, +}) +export { vueJsxPluginCjs as 'module.exports' } diff --git a/packages/plugin-vue-jsx/tsconfig.json b/packages/plugin-vue-jsx/tsconfig.json index 3a3117f2..4f4926b9 100644 --- a/packages/plugin-vue-jsx/tsconfig.json +++ b/packages/plugin-vue-jsx/tsconfig.json @@ -3,9 +3,9 @@ "exclude": ["**/*.spec.ts"], "compilerOptions": { "outDir": "dist", - "target": "ES2020", - "module": "ES2020", - "moduleResolution": "Node", + "target": "es2023", + "module": "preserve", + "moduleResolution": "bundler", "strict": true, "declaration": true, "sourceMap": true, diff --git a/packages/plugin-vue/CHANGELOG.md b/packages/plugin-vue/CHANGELOG.md index a0fa4cc0..54ea8a58 100644 --- a/packages/plugin-vue/CHANGELOG.md +++ b/packages/plugin-vue/CHANGELOG.md @@ -1,3 +1,24 @@ +## 6.0.0-beta.2 (2025-06-06) + +* refactor!: bump required node version to 20.19+, 22.12+ and drop CJS build (#596) ([56df545](https://github.com/vitejs/vite-plugin-vue/commit/56df545)), closes [#596](https://github.com/vitejs/vite-plugin-vue/issues/596) +* feat: add Vite 7 support (#597) ([12f2881](https://github.com/vitejs/vite-plugin-vue/commit/12f2881)), closes [#597](https://github.com/vitejs/vite-plugin-vue/issues/597) +* fix: template src sourcemap source (#267) ([de18693](https://github.com/vitejs/vite-plugin-vue/commit/de18693)), closes [#267](https://github.com/vitejs/vite-plugin-vue/issues/267) + + + +## 6.0.0-beta.1 (2025-06-02) + +* fix(vue): import with query (#592) ([b0400f3](https://github.com/vitejs/vite-plugin-vue/commit/b0400f3)), closes [#592](https://github.com/vitejs/vite-plugin-vue/issues/592) + + + +## 6.0.0-beta.0 (2025-05-21) + +* feat(vue)!: separate include and exclude from `api.options` and add filter (#582) ([e3beac8](https://github.com/vitejs/vite-plugin-vue/commit/e3beac8)), closes [#582](https://github.com/vitejs/vite-plugin-vue/issues/582) +* fix(deps): update all non-major dependencies (#587) ([d5ea412](https://github.com/vitejs/vite-plugin-vue/commit/d5ea412)), closes [#587](https://github.com/vitejs/vite-plugin-vue/issues/587) + + + ## 5.2.4 (2025-05-09) * chore: fix types with Vite 6.3 (#559) ([8002511](https://github.com/vitejs/vite-plugin-vue/commit/8002511)), closes [#559](https://github.com/vitejs/vite-plugin-vue/issues/559) diff --git a/packages/plugin-vue/build.config.ts b/packages/plugin-vue/build.config.ts index 33fef7f6..12d46d0e 100644 --- a/packages/plugin-vue/build.config.ts +++ b/packages/plugin-vue/build.config.ts @@ -6,7 +6,6 @@ export default defineBuildConfig({ clean: true, declaration: 'compatible', rollup: { - emitCJS: true, inlineDependencies: true, }, }) diff --git a/packages/plugin-vue/package.json b/packages/plugin-vue/package.json index 77e903dd..bb64b2f8 100644 --- a/packages/plugin-vue/package.json +++ b/packages/plugin-vue/package.json @@ -1,29 +1,23 @@ { "name": "@vitejs/plugin-vue", - "version": "5.2.4", - "type": "commonjs", + "version": "6.0.0-beta.2", + "type": "module", "license": "MIT", "author": "Evan You", "files": [ "dist" ], - "main": "./dist/index.cjs", - "module": "./dist/index.mjs", - "types": "./dist/index.d.ts", "exports": { - ".": { - "import": "./dist/index.mjs", - "require": "./dist/index.cjs" - } + ".": "./dist/index.mjs", + "./package.json": "./package.json" }, "scripts": { "dev": "unbuild --stub", - "build": "unbuild && pnpm run patch-cjs", - "patch-cjs": "tsx ../../scripts/patchCJS.ts", + "build": "unbuild", "prepublishOnly": "npm run build" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^20.19.0 || >=22.12.0" }, "repository": { "type": "git", @@ -35,17 +29,20 @@ }, "homepage": "https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue#readme", "peerDependencies": { - "vite": "^5.0.0 || ^6.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vue": "^3.2.25" }, "devDependencies": { "@jridgewell/gen-mapping": "^0.3.8", "@jridgewell/trace-mapping": "^0.3.25", - "debug": "^4.4.0", - "rollup": "^4.40.2", + "debug": "^4.4.1", + "rollup": "^4.43.0", "slash": "^5.1.0", "source-map-js": "^1.2.1", "vite": "catalog:", "vue": "catalog:" + }, + "dependencies": { + "@rolldown/pluginutils": "1.0.0-beta.15" } } diff --git a/packages/plugin-vue/src/index.ts b/packages/plugin-vue/src/index.ts index 4606b8ba..c1af1840 100644 --- a/packages/plugin-vue/src/index.ts +++ b/packages/plugin-vue/src/index.ts @@ -9,6 +9,10 @@ import type { } from 'vue/compiler-sfc' import type * as _compiler from 'vue/compiler-sfc' import { computed, shallowRef } from 'vue' +import { + exactRegex, + makeIdFiltersToMatchWithQuery, +} from '@rolldown/pluginutils' import { version } from '../package.json' import { resolveCompiler } from './compiler' import { parseVueRequest } from './utils/query' @@ -162,7 +166,7 @@ export interface Options { customElement?: boolean | string | RegExp | (string | RegExp)[] } -export interface ResolvedOptions extends Options { +export interface ResolvedOptions extends Omit { compiler: typeof _compiler root: string sourceMap: boolean @@ -174,6 +178,14 @@ export interface ResolvedOptions extends Options { export interface Api { get options(): ResolvedOptions set options(value: ResolvedOptions) + + get include(): string | RegExp | (string | RegExp)[] | undefined + /** include cannot be updated after `options` hook is called */ + set include(value: string | RegExp | (string | RegExp)[] | undefined) + get exclude(): string | RegExp | (string | RegExp)[] | undefined + /** exclude cannot be updated after `options` hook is called */ + set exclude(value: string | RegExp | (string | RegExp)[] | undefined) + version: string } @@ -183,17 +195,19 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin { const options = shallowRef({ isProduction: process.env.NODE_ENV === 'production', compiler: null as any, // to be set in buildStart - include: /\.vue$/, customElement: /\.ce\.vue$/, ...rawOptions, root: process.cwd(), sourceMap: true, cssDevSourcemap: false, }) - - const filter = computed(() => - createFilter(options.value.include, options.value.exclude), + const include = shallowRef>( + rawOptions.include ?? /\.vue$/, ) + const exclude = shallowRef(rawOptions.exclude) + let optionsHookIsCalled = false + + const filter = computed(() => createFilter(include.value, exclude.value)) const customElementFilter = computed(() => { const customElement = options.value.features?.customElement || options.value.customElement @@ -204,7 +218,7 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin { let transformCachedModule = false - return { + const plugin: Plugin = { name: 'vite:vue', api: { @@ -214,6 +228,28 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin { set options(value) { options.value = value }, + get include() { + return include.value + }, + set include(value) { + if (optionsHookIsCalled) { + throw new Error( + 'include cannot be updated after `options` hook is called', + ) + } + include.value = value + }, + get exclude() { + return exclude.value + }, + set exclude(value) { + if (optionsHookIsCalled) { + throw new Error( + 'exclude cannot be updated after `options` hook is called', + ) + } + exclude.value = value + }, version, }, @@ -317,6 +353,23 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin { config.build.watch != null }, + options() { + type TransformObjectHook = Extract< + typeof plugin.transform, + { filter?: unknown } + > + optionsHookIsCalled = true + ;(plugin.transform as TransformObjectHook).filter = { + id: { + include: [ + ...makeIdFiltersToMatchWithQuery(ensureArray(include.value)), + /[?&]vue\b/, + ], + exclude: exclude.value, + }, + } + }, + shouldTransformCachedModule({ id }) { if (transformCachedModule && parseVueRequest(id).query.vue) { return true @@ -338,110 +391,139 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin { } }, - async resolveId(id) { - // component export helper - if (id === EXPORT_HELPER_ID) { - return id - } - // serve sub-part requests (*?vue) as virtual modules - if (parseVueRequest(id).query.vue) { - return id - } + resolveId: { + filter: { + id: [exactRegex(EXPORT_HELPER_ID), /[?&]vue\b/], + }, + handler(id) { + // component export helper + if (id === EXPORT_HELPER_ID) { + return id + } + // serve sub-part requests (*?vue) as virtual modules + if (parseVueRequest(id).query.vue) { + return id + } + }, }, - load(id, opt) { - if (id === EXPORT_HELPER_ID) { - return helperCode - } + load: { + filter: { + id: [exactRegex(EXPORT_HELPER_ID), /[?&]vue\b/], + }, + handler(id, opt) { + if (id === EXPORT_HELPER_ID) { + return helperCode + } - const ssr = opt?.ssr === true + const ssr = opt?.ssr === true - const { filename, query } = parseVueRequest(id) + const { filename, query } = parseVueRequest(id) - // select corresponding block for sub-part virtual modules - if (query.vue) { - if (query.src) { - return fs.readFileSync(filename, 'utf-8') - } - const descriptor = getDescriptor(filename, options.value)! - let block: SFCBlock | null | undefined - if (query.type === 'script') { - // handle diff --git a/playground/vue-sourcemap/__tests__/__snapshots__/vue-sourcemap.spec.ts.snap b/playground/vue-sourcemap/__tests__/__snapshots__/vue-sourcemap.spec.ts.snap index 6708af56..5c648978 100644 --- a/playground/vue-sourcemap/__tests__/__snapshots__/vue-sourcemap.spec.ts.snap +++ b/playground/vue-sourcemap/__tests__/__snapshots__/vue-sourcemap.spec.ts.snap @@ -315,6 +315,20 @@ exports[`serve:vue-sourcemap > src imported > serve-src-imported 1`] = ` } `; +exports[`serve:vue-sourcemap > src imported html > serve-html 1`] = ` +{ + "mappings": ";;;wBAAA,oBAA8B,WAA3B,mBAAuB", + "sources": [ + "src-import.html", + ], + "sourcesContent": [ + "

<src-import-html>

+", + ], + "version": 3, +} +`; + exports[`serve:vue-sourcemap > src imported sass > serve-src-imported-sass 1`] = ` { "ignoreList": [], diff --git a/playground/vue-sourcemap/__tests__/vue-sourcemap.spec.ts b/playground/vue-sourcemap/__tests__/vue-sourcemap.spec.ts index 6cd0586d..1f9a0987 100644 --- a/playground/vue-sourcemap/__tests__/vue-sourcemap.spec.ts +++ b/playground/vue-sourcemap/__tests__/vue-sourcemap.spec.ts @@ -1,5 +1,6 @@ import { URL } from 'node:url' import { describe, expect, test } from 'vitest' +import * as vite from 'vite' import { extractSourcemap, formatSourcemapForSnapshot, @@ -9,6 +10,8 @@ import { serverLogs, } from '~utils' +const isRolldownVite = 'rolldownVersion' in vite + describe.runIf(isServe)('serve:vue-sourcemap', () => { const getStyleTagContentIncluding = async (content: string) => { const styles = await page.$$('style') @@ -28,7 +31,8 @@ describe.runIf(isServe)('serve:vue-sourcemap', () => { expect(formatSourcemapForSnapshot(map)).toMatchSnapshot('serve-js') }) - test('ts', async () => { + // skip this test for now with rolldown-vite as the snapshot is slightly different + test.skipIf(isRolldownVite)('ts', async () => { const res = await page.request.get(new URL('./Ts.vue', page.url()).href) const js = await res.text() const map = extractSourcemap(js) @@ -91,6 +95,18 @@ describe.runIf(isServe)('serve:vue-sourcemap', () => { ) }) + test('src imported html', async () => { + const res = await page.request.get( + new URL( + './src-import-html/src-import.html?import&vue&type=template&src=true&lang.js', + page.url(), + ).href, + ) + const js = await res.text() + const map = extractSourcemap(js) + expect(formatSourcemapForSnapshot(map)).toMatchSnapshot('serve-html') + }) + test('no script', async () => { const res = await page.request.get( new URL('./NoScript.vue', page.url()).href, diff --git a/playground/vue-sourcemap/package.json b/playground/vue-sourcemap/package.json index 22ed3ba4..4427b5b0 100644 --- a/playground/vue-sourcemap/package.json +++ b/playground/vue-sourcemap/package.json @@ -13,7 +13,7 @@ "@vitejs/plugin-vue": "workspace:*", "less": "^4.3.0", "postcss-nested": "^7.0.2", - "sass": "^1.87.0" + "sass": "^1.89.2" }, "dependencies": { "vue": "catalog:" diff --git a/playground/vue-sourcemap/src-import-html/SrcImportHtml.vue b/playground/vue-sourcemap/src-import-html/SrcImportHtml.vue new file mode 100644 index 00000000..79e3f7b0 --- /dev/null +++ b/playground/vue-sourcemap/src-import-html/SrcImportHtml.vue @@ -0,0 +1,5 @@ +