diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..3e795e7a --- /dev/null +++ b/.eslintignore @@ -0,0 +1,4 @@ +dist +playground-temp +temp + diff --git a/.eslintrc.cjs b/.eslintrc.cjs index d5050bd4..bb48bf9c 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -125,7 +125,7 @@ module.exports = defineConfig({ } }, { - files: ['packages/create-vite/template-*/**'], + files: ['packages/create-vite/template-*/**', '**/build.config.ts'], rules: { 'node/no-missing-import': 'off' } diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 05813fbd..7a329e3c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,14 +66,8 @@ jobs: - name: Install Playwright run: pnpm playwright install - - name: Build vite - run: pnpm run ci-build-vite - - - name: Build plugin-vue - run: pnpm run build-plugin-vue - - - name: Build plugin-react - run: pnpm run build-plugin-react + - name: Build + run: pnpm run build - name: Test unit run: pnpm run test-unit @@ -107,11 +101,8 @@ jobs: env: PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1" - - name: Prepare - run: | - pnpm run ci-build-vite - pnpm run build-plugin-vue - pnpm run build-plugin-react + - name: Build + run: pnpm run build - name: Lint run: pnpm run lint diff --git a/.prettierignore b/.prettierignore index a2c1f98c..0d548735 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,12 +1,8 @@ -docs/.vitepress/dist/ -packages/vite/dist/ -packages/vite/temp/ -packages/plugin-react/dist/ -packages/plugin-vue/dist/ packages/*/CHANGELOG.md playground-temp/ +dist/ +temp/ LICENSE.md -.prettierignore pnpm-lock.yaml pnpm-workspace.yaml playground/tsconfig-json-load-error/has-error/tsconfig.json diff --git a/package.json b/package.json index 92a5fd2e..dd19523d 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "preinstall": "npx only-allow pnpm", "format": "prettier --write .", "lint": "eslint packages/*/{src,types}/** playground/**/__tests__/** scripts/**", + "typecheck": "tsc -p scripts --noEmit && tsc -p playground --noEmit", "test": "run-s test-unit test-serve test-build", "test-serve": "vitest run -c vitest.config.e2e.ts", "test-build": "cross-env VITE_TEST_BUILD=1 vitest run -c vitest.config.e2e.ts", @@ -23,21 +24,18 @@ "debug-serve": "cross-env VITE_DEBUG_SERVE=1 vitest run -c vitest.config.e2e.ts", "debug-build": "cross-env VITE_TEST_BUILD=1 VITE_PRESERVE_BUILD_ARTIFACTS=1 vitest run -c vitest.config.e2e.ts", "docs": "vitepress dev docs", - "build-docs": "vitepress build docs", - "serve-docs": "vitepress serve docs", + "docs-build": "vitepress build docs", + "docs-serve": "vitepress serve docs", + "build": "pnpm -r --filter=./packages/* run build", + "dev": "pnpm -r --parallel --filter=./packages/* run dev", "release": "ts-node scripts/release.ts", "ci-publish": "ts-node scripts/publishCI.ts", - "typecheck": "tsc -p scripts --noEmit && tsc -p playground --noEmit", - "build": "run-s build-vite build-plugin-vue build-plugin-react", - "build-vite": "cd packages/vite && npm run build", - "build-plugin-vue": "cd packages/plugin-vue && npm run build", - "build-plugin-react": "cd packages/plugin-react && npm run build", - "ci-build-vite": "cd packages/vite && npm run ci-build", - "ci-docs": "run-s build-vite build-plugin-vue build-docs" + "ci-docs": "run-s build docs-build" }, "devDependencies": { "@microsoft/api-extractor": "^7.23.1", "@types/babel__core": "^7.1.19", + "@types/babel__standalone": "^7.1.4", "@types/convert-source-map": "^1.5.2", "@types/cross-spawn": "^6.0.2", "@types/debug": "^4.1.7", @@ -83,6 +81,7 @@ "sirv": "^2.0.2", "ts-node": "^10.7.0", "typescript": "^4.6.4", + "unbuild": "^0.7.4", "vite": "workspace:*", "vitepress": "^0.22.4", "vitest": "^0.12.4", diff --git a/packages/plugin-vue-jsx/build.config.ts b/packages/plugin-vue-jsx/build.config.ts new file mode 100644 index 00000000..6dcf1a5a --- /dev/null +++ b/packages/plugin-vue-jsx/build.config.ts @@ -0,0 +1,10 @@ +import { defineBuildConfig } from 'unbuild' + +export default defineBuildConfig({ + entries: ['src/index'], + clean: true, + declaration: true, + rollup: { + emitCJS: true + } +}) diff --git a/packages/plugin-vue-jsx/index.d.ts b/packages/plugin-vue-jsx/index.d.ts deleted file mode 100644 index a702c09b..00000000 --- a/packages/plugin-vue-jsx/index.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { Plugin } from 'vite' -import type { VueJSXPluginOptions } from '@vue/babel-plugin-jsx' -import type { FilterPattern } from '@rollup/pluginutils' - -declare interface FilterOptions { - include?: FilterPattern - exclude?: FilterPattern -} - -declare function createPlugin( - options?: VueJSXPluginOptions & FilterOptions & { babelPlugins?: any[] } -): Plugin - -export default createPlugin diff --git a/packages/plugin-vue-jsx/package.json b/packages/plugin-vue-jsx/package.json index f62a6cd9..6dbd1138 100644 --- a/packages/plugin-vue-jsx/package.json +++ b/packages/plugin-vue-jsx/package.json @@ -4,11 +4,24 @@ "license": "MIT", "author": "Evan You", "files": [ - "index.js", - "index.d.ts" + "dist" ], - "main": "index.js", - "types": "index.d.ts", + "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": "ts-node ../../scripts/patchCJS.ts", + "prepublishOnly": "npm run build" + }, "engines": { "node": ">=14.6.0" }, @@ -28,6 +41,9 @@ "@rollup/pluginutils": "^4.2.1", "@vue/babel-plugin-jsx": "^1.1.1" }, + "devDependencies": { + "vite": "workspace:*" + }, "peerDependencies": { "vite": "^2.0.0", "vue": "^3.0.0" diff --git a/packages/plugin-vue-jsx/index.js b/packages/plugin-vue-jsx/src/index.ts similarity index 81% rename from packages/plugin-vue-jsx/index.js rename to packages/plugin-vue-jsx/src/index.ts index 41fcb911..e23f0031 100644 --- a/packages/plugin-vue-jsx/index.js +++ b/packages/plugin-vue-jsx/src/index.ts @@ -1,10 +1,16 @@ -// @ts-check -const babel = require('@babel/core') -const jsx = require('@vue/babel-plugin-jsx') -const importMeta = require('@babel/plugin-syntax-import-meta') -const { createFilter, normalizePath } = require('@rollup/pluginutils') -const { createHash } = require('crypto') -const path = require('path') +import { createHash } from 'crypto' +import path from 'path' +import type { types } from '@babel/core' +import babel from '@babel/core' +import jsx from '@vue/babel-plugin-jsx' +// @ts-expect-error missing type +import importMeta from '@babel/plugin-syntax-import-meta' +import { createFilter, normalizePath } from '@rollup/pluginutils' +import type { ComponentOptions } from 'vue' +import type { Plugin } from 'vite' +import type { Options } from './types' + +export * from './types' const ssrRegisterHelperId = '/__vue-jsx-ssr-register-helper' const ssrRegisterHelperCode = @@ -14,10 +20,8 @@ const ssrRegisterHelperCode = /** * This function is serialized with toString() and evaluated as a virtual * module during SSR - * @param {import('vue').ComponentOptions} comp - * @param {string} filename */ -function ssrRegisterHelper(comp, filename) { +function ssrRegisterHelper(comp: ComponentOptions, filename: string) { const setup = comp.setup comp.setup = (props, ctx) => { // @ts-ignore @@ -29,17 +33,7 @@ function ssrRegisterHelper(comp, filename) { } } -/** - * @typedef { import('@rollup/pluginutils').FilterPattern} FilterPattern - * @typedef { { include?: FilterPattern, exclude?: FilterPattern, babelPlugins?: any[] } } CommonOptions - */ - -/** - * - * @param {import('@vue/babel-plugin-jsx').VueJSXPluginOptions & CommonOptions} options - * @returns {import('vite').Plugin} - */ -function vueJsxPlugin(options = {}) { +function vueJsxPlugin(options: Options = {}): Plugin { let root = '' let needHmr = false let needSourceMap = true @@ -110,31 +104,28 @@ function vueJsxPlugin(options = {}) { sourceMaps: needSourceMap, sourceFileName: id, configFile: false - }) + })! if (!ssr && !needHmr) { + if (!result.code) return return { code: result.code, map: result.map } } + interface HotComponent { + local: string + exported: string + id: string + } + // check for hmr injection - /** - * @type {{ name: string }[]} - */ - const declaredComponents = [] - /** - * @type {{ - * local: string, - * exported: string, - * id: string, - * }[]} - */ - const hotComponents = [] + const declaredComponents: { name: string }[] = [] + const hotComponents: HotComponent[] = [] let hasDefault = false - for (const node of result.ast.program.body) { + for (const node of result.ast!.program.body) { if (node.type === 'VariableDeclaration') { const names = parseComponentDecls(node, code) if (names.length) { @@ -204,7 +195,7 @@ function vueJsxPlugin(options = {}) { if (hotComponents.length) { if (hasDefault && (needHmr || ssr)) { result.code = - result.code.replace( + result.code!.replace( /export default defineComponent/g, `const __default__ = defineComponent` ) + `\nexport default __default__` @@ -239,6 +230,7 @@ function vueJsxPlugin(options = {}) { } } + if (!result.code) return return { code: result.code, map: result.map @@ -248,11 +240,7 @@ function vueJsxPlugin(options = {}) { } } -/** - * @param {import('@babel/core').types.VariableDeclaration} node - * @param {string} source - */ -function parseComponentDecls(node, source) { +function parseComponentDecls(node: types.VariableDeclaration, source: string) { const names = [] for (const decl of node.declarations) { if (decl.id.type === 'Identifier' && isDefineComponentCall(decl.init)) { @@ -264,10 +252,7 @@ function parseComponentDecls(node, source) { return names } -/** - * @param {import('@babel/core').types.Node} node - */ -function isDefineComponentCall(node) { +function isDefineComponentCall(node?: types.Node | null) { return ( node && node.type === 'CallExpression' && @@ -276,13 +261,8 @@ function isDefineComponentCall(node) { ) } -/** - * @param {string} text - * @returns {string} - */ -function getHash(text) { +function getHash(text: string) { return createHash('sha256').update(text).digest('hex').substring(0, 8) } -module.exports = vueJsxPlugin -vueJsxPlugin.default = vueJsxPlugin +export default vueJsxPlugin diff --git a/packages/plugin-vue-jsx/src/types.ts b/packages/plugin-vue-jsx/src/types.ts new file mode 100644 index 00000000..aa30b743 --- /dev/null +++ b/packages/plugin-vue-jsx/src/types.ts @@ -0,0 +1,10 @@ +import type { VueJSXPluginOptions } from '@vue/babel-plugin-jsx' +import type { FilterPattern } from '@rollup/pluginutils' + +export interface FilterOptions { + include?: FilterPattern + exclude?: FilterPattern +} + +export type Options = VueJSXPluginOptions & + FilterOptions & { babelPlugins?: any[] } diff --git a/packages/plugin-vue-jsx/tsconfig.json b/packages/plugin-vue-jsx/tsconfig.json new file mode 100644 index 00000000..c20adf0d --- /dev/null +++ b/packages/plugin-vue-jsx/tsconfig.json @@ -0,0 +1,19 @@ +{ + "include": ["src"], + "exclude": ["**/*.spec.ts"], + "compilerOptions": { + "outDir": "dist", + "target": "ES2018", + "module": "CommonJS", + "moduleResolution": "Node", + "strict": true, + "declaration": true, + "sourceMap": true, + "noUnusedLocals": true, + "esModuleInterop": true, + "paths": { + "types/*": ["../vite/types/*"], + "vite": ["../vite/src/node/index.js"] + } + } +} diff --git a/packages/plugin-vue/api-extractor.json b/packages/plugin-vue/api-extractor.json deleted file mode 100644 index df6d6f9a..00000000 --- a/packages/plugin-vue/api-extractor.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", - - "projectFolder": ".", - - "mainEntryPointFilePath": "./temp/index.d.ts", - - "dtsRollup": { - "enabled": true, - "untrimmedFilePath": "./dist/index.d.ts" - }, - - "apiReport": { - "enabled": false - }, - - "docModel": { - "enabled": false - }, - - "tsdocMetadata": { - "enabled": false - }, - - "messages": { - "compilerMessageReporting": { - "default": { - "logLevel": "warning" - } - }, - - "extractorMessageReporting": { - "default": { - "logLevel": "warning", - "addToApiReportFile": true - }, - - "ae-missing-release-tag": { - "logLevel": "none" - } - }, - - "tsdocMessageReporting": { - "default": { - "logLevel": "warning" - }, - - "tsdoc-undefined-tag": { - "logLevel": "none" - } - } - } -} diff --git a/packages/plugin-vue/build.config.ts b/packages/plugin-vue/build.config.ts new file mode 100644 index 00000000..4f9d811a --- /dev/null +++ b/packages/plugin-vue/build.config.ts @@ -0,0 +1,12 @@ +import { defineBuildConfig } from 'unbuild' + +export default defineBuildConfig({ + entries: ['src/index'], + externals: ['vite', 'vue/compiler-sfc', '@vue/compiler-sfc'], + clean: true, + declaration: true, + rollup: { + emitCJS: true, + inlineDependencies: true + } +}) diff --git a/packages/plugin-vue/package.json b/packages/plugin-vue/package.json index 22ce5ad5..6a08c1df 100644 --- a/packages/plugin-vue/package.json +++ b/packages/plugin-vue/package.json @@ -6,17 +6,21 @@ "files": [ "dist" ], - "main": "dist/index.js", - "types": "dist/index.d.ts", + "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": "rimraf dist && run-p dev-types dev-watch", - "dev-types": "tsc -p . -w --incremental --emitDeclarationOnly", - "dev-watch": "esbuild src/index.ts --watch --bundle --platform=node --target=node12 --external:@vue/compiler-sfc --external:vue/compiler-sfc --external:vite --outfile=dist/index.js", - "build": "rimraf dist && run-s build-bundle build-types", - "build-bundle": "esbuild src/index.ts --bundle --platform=node --target=node12 --external:@vue/compiler-sfc --external:vue/compiler-sfc --external:vite --outfile=dist/index.js & npm run patch-dist", - "patch-dist": "ts-node ../../scripts/patchEsbuildDist.ts dist/index.js vuePlugin", - "build-types": "tsc -p . --emitDeclarationOnly --outDir temp && api-extractor run && rimraf temp", - "prepublishOnly": "(cd ../vite && npm run build) && npm run build" + "dev": "unbuild --stub", + "build": "unbuild && pnpm run patch-cjs", + "patch-cjs": "ts-node ../../scripts/patchCJS.ts", + "prepublishOnly": "npm run build" }, "engines": { "node": ">=14.6.0" @@ -40,6 +44,7 @@ "rollup": "^2.72.1", "slash": "^4.0.0", "source-map": "^0.6.1", - "vue": "^3.2.33" + "vue": "^3.2.33", + "vite": "workspace:*" } } diff --git a/packages/plugin-vue/src/index.ts b/packages/plugin-vue/src/index.ts index 7bdf63b7..6526f941 100644 --- a/packages/plugin-vue/src/index.ts +++ b/packages/plugin-vue/src/index.ts @@ -18,7 +18,8 @@ import { transformTemplateAsModule } from './template' import { transformStyle } from './style' import { EXPORT_HELPER_ID, helperCode } from './helper' -export { parseVueRequest, VueQuery } from './utils/query' +export { parseVueRequest } from './utils/query' +export type { VueQuery } from './utils/query' export interface Options { include?: string | RegExp | (string | RegExp)[] @@ -250,10 +251,3 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin { } } } - -// overwrite for cjs require('...')() usage -// The following lines are inserted by scripts/patchEsbuildDist.ts, -// this doesn't bundle correctly after esbuild 0.14.4 -// -// module.exports = vuePlugin -// vuePlugin['default'] = vuePlugin diff --git a/packages/plugin-vue/tsconfig.json b/packages/plugin-vue/tsconfig.json index fa388fd0..6165780e 100644 --- a/packages/plugin-vue/tsconfig.json +++ b/packages/plugin-vue/tsconfig.json @@ -14,9 +14,8 @@ "esModuleInterop": true, "baseUrl": ".", "paths": { - // vite typings uses custom paths that is patched into relative paths during build - // this is a shim that makes even dev-time vite typings work for plugin-vue - "types/*": ["../vite/types/*"] + "types/*": ["../vite/types/*"], + "vite": ["../vite/src/node/index.js"] } } } diff --git a/scripts/patchCJS.ts b/scripts/patchCJS.ts new file mode 100644 index 00000000..76edf3fc --- /dev/null +++ b/scripts/patchCJS.ts @@ -0,0 +1,57 @@ +/** + +It converts + +```ts +exports["default"] = vuePlugin; +exports.parseVueRequest = parseVueRequest; +``` + +to + +```ts +module.exports = vuePlugin; +module.exports["default"] = vuePlugin; +module.exports.parseVueRequest = parseVueRequest; +``` +*/ + +import { readFileSync, writeFileSync } from 'fs' +import { bold, red } from 'picocolors' + +const indexPath = 'dist/index.cjs' +let code = readFileSync(indexPath, 'utf-8') + +const matchMixed = code.match(/\nexports\["default"\] = (\w+);/) +if (matchMixed) { + const name = matchMixed[1] + + const lines = code.trimEnd().split('\n') + + // search from the end to prepend `modules.` to `export[xxx]` + for (let i = lines.length - 1; i > 0; i--) { + if (lines[i].startsWith('exports')) lines[i] = 'module.' + lines[i] + else { + // at the beginning of exports, export the default function + lines[i] += `\nmodule.exports = ${name};` + break + } + } + + writeFileSync(indexPath, lines.join('\n')) + + console.log(bold(`${indexPath} CJS patched`)) + process.exit() +} + +const matchDefault = code.match(/\nmodule.exports = (\w+);/) + +if (matchDefault) { + code += `module.exports["default"] = ${matchDefault[1]};\n` + writeFileSync(indexPath, code) + console.log(bold(`${indexPath} CJS patched`)) + process.exit() +} + +console.error(red(`${indexPath} CJS patch failed`)) +process.exit(1) diff --git a/scripts/patchEsbuildDist.ts b/scripts/patchEsbuildDist.ts deleted file mode 100644 index 6ad2803c..00000000 --- a/scripts/patchEsbuildDist.ts +++ /dev/null @@ -1,31 +0,0 @@ -// esbuild 0.14.4 https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#0144 introduced a -// change that breaks the "overwrite for cjs require('...')() usage" hack used in plugin-vue -// and plugin-react. For the moment, we can remove the extra exports code added in 0.14.4 to -// continue using it. - -import { readFileSync, writeFileSync } from 'fs' -import { bold, red } from 'picocolors' - -const indexPath = process.argv[2] -const varName = process.argv[3] - -let code = readFileSync(indexPath, 'utf-8') - -const moduleExportsLine = `module.exports = __toCommonJS(src_exports);` - -if (code.includes(moduleExportsLine)) { - // overwrite for cjs require('...')() usage - code = code.replace( - moduleExportsLine, - `module.exports = ${varName}; -${varName}['default'] = ${varName};` - ) - - writeFileSync(indexPath, code) - - console.log( - bold(`${indexPath} patched with overwrite for cjs require('...')()`) - ) -} else { - console.error(red(`${indexPath} post-esbuild bundling patch failed`)) -}