From ed2581778baff3201f47866799f006a490a7e35b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Tue, 31 May 2022 16:08:34 +0900 Subject: [PATCH 01/29] fix(plugin-legacy): modern polyfill latest features (fixes #8399) (#8408) --- packages/plugin-legacy/src/index.ts | 53 ++++++++++++---------- playground/legacy/__tests__/legacy.spec.ts | 1 + 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/packages/plugin-legacy/src/index.ts b/packages/plugin-legacy/src/index.ts index bc21d4d917d8df..ff6f30685e199a 100644 --- a/packages/plugin-legacy/src/index.ts +++ b/packages/plugin-legacy/src/index.ts @@ -47,6 +47,8 @@ const forceDynamicImportUsage = `export function __vite_legacy_guard(){import('d const legacyEnvVarMarker = `__VITE_IS_LEGACY__` +const _require = createRequire(import.meta.url) + function viteLegacyPlugin(options: Options = {}): Plugin[] { let config: ResolvedConfig const targets = options.targets || 'defaults' @@ -175,7 +177,6 @@ function viteLegacyPlugin(options: Options = {}): Plugin[] { } } - const _require = createRequire(import.meta.url) const legacyPostPlugin: Plugin = { name: 'vite:legacy-post-process', enforce: 'post', @@ -328,21 +329,10 @@ function viteLegacyPlugin(options: Options = {}): Plugin[] { ], [ 'env', - { - targets, - modules: false, - bugfixes: true, - loose: false, - useBuiltIns: needPolyfills ? 'usage' : false, - corejs: needPolyfills - ? { - version: _require('core-js/package.json').version, - proposals: false - } - : undefined, - shippedProposals: true, + createBabelPresetEnvOptions(targets, { + needPolyfills, ignoreBrowserslistConfig: options.ignoreBrowserslistConfig - } + }) ] ] }) @@ -528,14 +518,7 @@ export async function detectPolyfills( presets: [ [ 'env', - { - targets, - modules: false, - useBuiltIns: 'usage', - corejs: { version: 3, proposals: false }, - shippedProposals: true, - ignoreBrowserslistConfig: true - } + createBabelPresetEnvOptions(targets, { ignoreBrowserslistConfig: true }) ] ] }) @@ -552,6 +535,30 @@ export async function detectPolyfills( } } +function createBabelPresetEnvOptions( + targets: any, + { + needPolyfills = true, + ignoreBrowserslistConfig + }: { needPolyfills?: boolean; ignoreBrowserslistConfig?: boolean } +) { + return { + targets, + bugfixes: true, + loose: false, + modules: false, + useBuiltIns: needPolyfills ? 'usage' : false, + corejs: needPolyfills + ? { + version: _require('core-js/package.json').version, + proposals: false + } + : undefined, + shippedProposals: true, + ignoreBrowserslistConfig + } +} + async function buildPolyfillChunk( imports: Set, bundle: OutputBundle, diff --git a/playground/legacy/__tests__/legacy.spec.ts b/playground/legacy/__tests__/legacy.spec.ts index cbf44112b122c1..f68371b1df7438 100644 --- a/playground/legacy/__tests__/legacy.spec.ts +++ b/playground/legacy/__tests__/legacy.spec.ts @@ -104,5 +104,6 @@ describe.runIf(isBuild)('build', () => { test('includes structuredClone polyfill which is supported after core-js v3', () => { expect(findAssetFile(/polyfills-legacy/)).toMatch('"structuredClone"') + expect(findAssetFile(/polyfills\./)).toMatch('"structuredClone"') }) }) From e7843657ac81a94af07bec3c290ee579cc19aa0f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 31 May 2022 09:58:35 +0200 Subject: [PATCH 02/29] chore(deps): update dependency eslint-define-config to ^1.5.0 (#8409) --- package.json | 2 +- pnpm-lock.yaml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 6fe896eccb3662..aafd42d91f7cf6 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "cross-env": "^7.0.3", "esbuild": "^0.14.38", "eslint": "^8.16.0", - "eslint-define-config": "^1.4.1", + "eslint-define-config": "^1.5.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-node": "^11.1.0", "esno": "^0.16.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2f01ebb7dbeab9..acef1fe7331aef 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -39,7 +39,7 @@ importers: cross-env: ^7.0.3 esbuild: ^0.14.38 eslint: ^8.16.0 - eslint-define-config: ^1.4.1 + eslint-define-config: ^1.5.0 eslint-plugin-import: ^2.26.0 eslint-plugin-node: ^11.1.0 esno: ^0.16.3 @@ -97,7 +97,7 @@ importers: cross-env: 7.0.3 esbuild: 0.14.38 eslint: 8.16.0 - eslint-define-config: 1.4.1 + eslint-define-config: 1.5.0 eslint-plugin-import: 2.26.0_xsmuhwqsfrjm7m3kqio7zoeziq eslint-plugin-node: 11.1.0_eslint@8.16.0 esno: 0.16.3 @@ -4427,8 +4427,8 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - /eslint-define-config/1.4.1: - resolution: {integrity: sha512-3kjqLpYgAjr8XtI0ufInSX7cIWe1rDbiG1AjyOHY+00BgWhce8LZ/xd2BMKpgSsDI6bqd59eMmRcxOZSLme+Mg==} + /eslint-define-config/1.5.0: + resolution: {integrity: sha512-2C6tmeMRAXQ+y5vxbXsOjn/CTmMseJ0eRXsK+ThFEjeibSpMmurPq2SjBGzl5Ha4pTpVSMJQc0lzE8Y3hPYbHQ==} engines: {node: '>= 14.6.0', npm: '>= 6.0.0', pnpm: '>= 7.0.0'} dev: true From 6a5a5b5273c83a9f1958a1a2f753fd74be1ad2b0 Mon Sep 17 00:00:00 2001 From: patak Date: Tue, 31 May 2022 10:45:56 +0200 Subject: [PATCH 03/29] feat: ssr build using optimized deps (#8403) --- packages/vite/src/node/build.ts | 4 ++-- packages/vite/src/node/optimizer/index.ts | 14 ++++++++++-- packages/vite/src/node/plugins/resolve.ts | 6 ++--- packages/vite/src/node/server/index.ts | 28 +++++++++++++---------- 4 files changed, 33 insertions(+), 19 deletions(-) diff --git a/packages/vite/src/node/build.ts b/packages/vite/src/node/build.ts index 25b752d7512df6..5e2da01695a0a1 100644 --- a/packages/vite/src/node/build.ts +++ b/packages/vite/src/node/build.ts @@ -306,7 +306,7 @@ export function resolveBuildPlugins(config: ResolvedConfig): { pre: [ ...(options.watch ? [ensureWatchPlugin()] : []), watchPackageDataPlugin(config), - ...(!isDepsOptimizerEnabled(config) || options.ssr + ...(!isDepsOptimizerEnabled(config) ? [commonjsPlugin(options.commonjsOptions)] : []), dataURIPlugin(), @@ -402,7 +402,7 @@ async function doBuild( external = await cjsSsrResolveExternal(config, userExternal) } - if (isDepsOptimizerEnabled(config) && !ssr) { + if (isDepsOptimizerEnabled(config)) { await initDepsOptimizer(config) } diff --git a/packages/vite/src/node/optimizer/index.ts b/packages/vite/src/node/optimizer/index.ts index 4f4231354e0a6a..ea291675ccc8cf 100644 --- a/packages/vite/src/node/optimizer/index.ts +++ b/packages/vite/src/node/optimizer/index.ts @@ -610,13 +610,23 @@ export function getOptimizedDepPath( ) } +function getDepsCacheSuffix(config: ResolvedConfig): string { + let suffix = '' + if (config.command === 'build') { + suffix += '_build' + if (config.build.ssr) { + suffix += '_ssr' + } + } + return suffix +} export function getDepsCacheDir(config: ResolvedConfig): string { - const dirName = config.command === 'build' ? 'depsBuild' : 'deps' + const dirName = 'deps' + getDepsCacheSuffix(config) return normalizePath(path.resolve(config.cacheDir, dirName)) } function getProcessingDepsCacheDir(config: ResolvedConfig) { - const dirName = config.command === 'build' ? 'processingBuild' : 'processing' + const dirName = 'deps' + getDepsCacheSuffix(config) + '_temp' return normalizePath(path.resolve(config.cacheDir, dirName)) } diff --git a/packages/vite/src/node/plugins/resolve.ts b/packages/vite/src/node/plugins/resolve.ts index f1f940c218716a..538389828ed2c2 100644 --- a/packages/vite/src/node/plugins/resolve.ts +++ b/packages/vite/src/node/plugins/resolve.ts @@ -89,6 +89,7 @@ export interface InternalResolveOptions extends ResolveOptions { export function resolvePlugin(baseOptions: InternalResolveOptions): Plugin { const { root, + isBuild, isProduction, asSrc, ssrConfig, @@ -266,7 +267,7 @@ export function resolvePlugin(baseOptions: InternalResolveOptions): Plugin { !external && asSrc && depsOptimizer && - !ssr && + (isBuild || !ssr) && !options.scan && (res = await tryOptimizedResolve(depsOptimizer, id, importer)) ) { @@ -668,7 +669,7 @@ export function tryNodeResolve( exclude?.includes(pkgId) || exclude?.includes(nestedPath) || SPECIAL_QUERY_RE.test(resolved) || - ssr + (!isBuild && ssr) ) { // excluded from optimization // Inject a version query to npm deps so that the browser @@ -682,7 +683,6 @@ export function tryNodeResolve( } } } else { - // TODO: depsBuild // this is a missing import, queue optimize-deps re-run and // get a resolved its optimized info const optimizedInfo = depsOptimizer.registerMissingImport(id, resolved) diff --git a/packages/vite/src/node/server/index.ts b/packages/vite/src/node/server/index.ts index 983fa05efec6e0..095c7027c68558 100644 --- a/packages/vite/src/node/server/index.ts +++ b/packages/vite/src/node/server/index.ts @@ -320,18 +320,7 @@ export async function createServer( }, transformIndexHtml: null!, // to be immediately set async ssrLoadModule(url, opts?: { fixStacktrace?: boolean }) { - if (!server._ssrExternals) { - let knownImports: string[] = [] - const depsOptimizer = getDepsOptimizer(config) - if (depsOptimizer) { - await depsOptimizer.scanProcessing - knownImports = [ - ...Object.keys(depsOptimizer.metadata.optimized), - ...Object.keys(depsOptimizer.metadata.discovered) - ] - } - server._ssrExternals = cjsSsrResolveExternals(config, knownImports) - } + await updateCjsSsrExternals(server) return ssrLoadModule( url, server, @@ -755,3 +744,18 @@ async function restartServer(server: ViteDevServer) { // new server (the current server) can restart now newServer._restartPromise = null } + +async function updateCjsSsrExternals(server: ViteDevServer) { + if (!server._ssrExternals) { + let knownImports: string[] = [] + const depsOptimizer = getDepsOptimizer(server.config) + if (depsOptimizer) { + await depsOptimizer.scanProcessing + knownImports = [ + ...Object.keys(depsOptimizer.metadata.optimized), + ...Object.keys(depsOptimizer.metadata.discovered) + ] + } + server._ssrExternals = cjsSsrResolveExternals(server.config, knownImports) + } +} From 9af61b593baeb446c024bd48698d70b1390cfc6d Mon Sep 17 00:00:00 2001 From: patak-dev Date: Tue, 31 May 2022 11:16:44 +0200 Subject: [PATCH 04/29] release: v3.0.0-alpha.8 --- packages/vite/CHANGELOG.md | 19 +++++++++++++++++++ packages/vite/package.json | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/vite/CHANGELOG.md b/packages/vite/CHANGELOG.md index c8bea7c58ca913..5d8403b38dd72e 100644 --- a/packages/vite/CHANGELOG.md +++ b/packages/vite/CHANGELOG.md @@ -1,3 +1,22 @@ +## 3.0.0-alpha.8 (2022-05-31) + +* feat: add ssr.format to force esm output for ssr (#6812) ([337b197](https://github.com/vitejs/vite/commit/337b197)), closes [#6812](https://github.com/vitejs/vite/issues/6812) +* feat: default esm SSR build, simplified externalization (#8348) ([f8c92d1](https://github.com/vitejs/vite/commit/f8c92d1)), closes [#8348](https://github.com/vitejs/vite/issues/8348) +* feat: derive proper js extension from package type (#8382) ([95cdd81](https://github.com/vitejs/vite/commit/95cdd81)), closes [#8382](https://github.com/vitejs/vite/issues/8382) +* feat: ssr build using optimized deps (#8403) ([6a5a5b5](https://github.com/vitejs/vite/commit/6a5a5b5)), closes [#8403](https://github.com/vitejs/vite/issues/8403) +* fix: `import.meta.accept()` -> `import.meta.hot.accept()` (#8361) ([c5185cf](https://github.com/vitejs/vite/commit/c5185cf)), closes [#8361](https://github.com/vitejs/vite/issues/8361) +* fix: return type of `handleHMRUpdate` (#8367) ([79d5ce1](https://github.com/vitejs/vite/commit/79d5ce1)), closes [#8367](https://github.com/vitejs/vite/issues/8367) +* fix: sourcemap source point to null (#8299) ([356b896](https://github.com/vitejs/vite/commit/356b896)), closes [#8299](https://github.com/vitejs/vite/issues/8299) +* fix: ssr-manifest no base (#8371) ([37eb5b3](https://github.com/vitejs/vite/commit/37eb5b3)), closes [#8371](https://github.com/vitejs/vite/issues/8371) +* fix(deps): update all non-major dependencies (#8391) ([842f995](https://github.com/vitejs/vite/commit/842f995)), closes [#8391](https://github.com/vitejs/vite/issues/8391) +* chore: enable `@typescript-eslint/explicit-module-boundary-types` (#8372) ([104caf9](https://github.com/vitejs/vite/commit/104caf9)), closes [#8372](https://github.com/vitejs/vite/issues/8372) +* chore: enable reportUnusedDisableDirectives (#8384) ([9a99bc4](https://github.com/vitejs/vite/commit/9a99bc4)), closes [#8384](https://github.com/vitejs/vite/issues/8384) +* chore: improve jsdoc of library options (#8381) ([44dc27d](https://github.com/vitejs/vite/commit/44dc27d)), closes [#8381](https://github.com/vitejs/vite/issues/8381) +* chore: improve public assets warning message (#6738) ([f6bd317](https://github.com/vitejs/vite/commit/f6bd317)), closes [#6738](https://github.com/vitejs/vite/issues/6738) +* chore: update comments (#8394) ([3d14372](https://github.com/vitejs/vite/commit/3d14372)), closes [#8394](https://github.com/vitejs/vite/issues/8394) + + + ## 3.0.0-alpha.7 (2022-05-27) * fix: preserve annotations during build deps optimization (#8358) ([334cd9f](https://github.com/vitejs/vite/commit/334cd9f)), closes [#8358](https://github.com/vitejs/vite/issues/8358) diff --git a/packages/vite/package.json b/packages/vite/package.json index cca4b07d3c289b..850ce7111e07ec 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -1,6 +1,6 @@ { "name": "vite", - "version": "3.0.0-alpha.7", + "version": "3.0.0-alpha.8", "type": "module", "license": "MIT", "author": "Evan You", From cfc37acb410ad8d2ad13589f1213fc80c9ea7c7f Mon Sep 17 00:00:00 2001 From: patak Date: Tue, 31 May 2022 11:21:45 +0200 Subject: [PATCH 05/29] docs: migration from v2 guide (#8390) Co-authored-by: Shinigami Co-authored-by: Tony Trinh --- docs/.vitepress/config.ts | 2 +- docs/guide/migration.md | 141 +++++++++----------------------------- 2 files changed, 32 insertions(+), 111 deletions(-) diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 1a729dc3237553..9c3150eb1d0c1b 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -150,7 +150,7 @@ export default defineConfig({ link: '/guide/comparisons' }, { - text: 'Migration from v1', + text: 'Migration from v2', link: '/guide/migration' } ] diff --git a/docs/guide/migration.md b/docs/guide/migration.md index 23d80795ce9599..fa52babe69c6da 100644 --- a/docs/guide/migration.md +++ b/docs/guide/migration.md @@ -1,133 +1,54 @@ -# Migration from v1 +# Migration from v2 -## Config Options Change +## Node Support -- The following options have been removed and should be implemented via [plugins](./api-plugin): +Vite no longer supports Node v12, which reached its EOL. Node 14.6+ is now required. - - `resolvers` - - `transforms` - - `indexHtmlTransforms` +## Modern Browser Baseline change -- `jsx` and `enableEsbuild` have been removed; Use the new [`esbuild`](/config/#esbuild) option instead. +The production bundle assumes support for modern JavaScript. By default, Vite targets browsers which support the [native ES Modules](https://caniuse.com/es6-module) and [native ESM dynamic import](https://caniuse.com/es6-module-dynamic-import) and [`import.meta`](https://caniuse.com/mdn-javascript_statements_import_meta): -- [CSS related options](/config/#css-modules) are now nested under `css`. +- Chrome >=87 +- Firefox >=78 +- Safari >=13 +- Edge >=88 -- All [build-specific options](/config/#build-options) are now nested under `build`. +A small fraction of users will now require using [@vitejs/plugin-legacy](https://github.com/vitejs/vite/tree/main/packages/plugin-legacy), which will automatically generate legacy chunks and corresponding ES language feature polyfills. - - `rollupInputOptions` and `rollupOutputOptions` are replaced by [`build.rollupOptions`](/config/#build-rollupoptions). - - `esbuildTarget` is now [`build.target`](/config/#build-target). - - `emitManifest` is now [`build.manifest`](/config/#build-manifest). - - The following build options have been removed since they can be achieved via plugin hooks or other options: - - `entry` - - `rollupDedupe` - - `emitAssets` - - `emitIndex` - - `shouldPreload` - - `configureBuild` +## Config Options Changes -- All [server-specific options](/config/#server-options) are now nested under - `server`. +- The following options that were already deprecated in v2 have been removed: - - `hostname` is now [`server.host`](/config/#server-host). - - `httpsOptions` has been removed. [`server.https`](/config/#server-https) can directly accept the options object. - - `chokidarWatchOptions` is now [`server.watch`](/config/#server-watch). + - `optimizeDeps.keepNames` (switch to [`optimizeDeps.esbuildOptions.keepNames`](../config/dep-optimization-options.md#optimizedepsesbuildoptions)) + - `build.base` (switch to [`base`](../config/shared-options.md#base)) + - `alias` (switch to [`resolve.alias`](../config/shared-options.md#resolvealias)) + - `dedupe` (switch to [`resolve.dedupe`](../config/shared-options.md#resolvededupe)) + - `polyfillDynamicImport` (use [`@vitejs/plugin-legacy](https://github.com/vitejs/vite/tree/main/packages/plugin-legacy) for browsers without dynamic import support) -- [`assetsInclude`](/config/#assetsinclude) now expects `string | RegExp | (string | RegExp)[]` instead of a function. +## Dev Server Changes -- All Vue specific options are removed; Pass options to the Vue plugin instead. +Vite optimizes dependencies with esbuild to both convert CJS-only deps to ESM and to reduce the number of modules the browser needs to request. In v3, the default strategy to discover and batch dependencies has changed. Vite no longer pre-scans user code with esbuild to get an initial list of dependencies on cold start. Instead, it delays the first dependency optimization run until every imported user module on load is processed. -## Alias Behavior Change +To get back the v2 strategy, you can use [`optimizeDeps.devScan`](../config/dep-optimization-options.md#optimizedepsdevscan). -[`alias`](/config/#resolve-alias) is now being passed to `@rollup/plugin-alias` and no longer require start/ending slashes. The behavior is now a direct replacement, so 1.0-style directory alias key should remove the ending slash: +## Build Changes -```diff -- alias: { '/@foo/': path.resolve(__dirname, 'some-special-dir') } -+ alias: { '/@foo': path.resolve(__dirname, 'some-special-dir') } -``` +In v3, Vite uses esbuild to optimize dependencies by default. Doing so, it removes one of the most significant differences between dev and prod present in v2. Because esbuild converts CJS-only dependencies to ESM, [`@rollupjs/plugin-commonjs`] is no longer used. -Alternatively, you can use the `[{ find: RegExp, replacement: string }]` option format for more precise control. +If you need to get back to the v2 strategy, you can use [`optimizeDeps.disabled: 'build'`](../config/dep-optimization-options.md#optimizedepsdisabled). -## Vue Support +## SSR Changes -Vite 2.0 core is now framework agnostic. Vue support is now provided via [`@vitejs/plugin-vue`](https://github.com/vitejs/vite/tree/main/packages/plugin-vue). Simply install it and add it in the Vite config: +Vite v3 uses ESM for the SSR build by default. When using ESM, the [SSR externalization heuristics](https://vitejs.dev/guide/ssr.html#ssr-externals) are no longer needed. By default, all dependencies are externalized. You can use [`ssr.noExternal`](../config/ssr-options.md#ssrnoexternal) to control what dependencies to include in the SSR bundle. -```js -import vue from '@vitejs/plugin-vue' -import { defineConfig } from 'vite' +If using ESM for SSR isn't possible in your project, you can set `ssr.format: 'cjs'` to generate a CJS bundle. In this case, the same externalization strategy of Vite v2 will be used. -export default defineConfig({ - plugins: [vue()] -}) -``` +## General Changes -### Custom Blocks Transforms +- [Raw `import.meta.glob`](features.md#glob-import-as) switched from `{ assert: { type: 'raw' }}` to `{ as: 'raw' }` -A custom plugin can be used to transform Vue custom blocks like the one below: +- JS file extensions in SSR and lib mode now use a valid extension (`js`, `mjs`, or `cjs`) for output JS entries and chunks based on their format and the package type. -```ts -// vite.config.js -import vue from '@vitejs/plugin-vue' -import { defineConfig } from 'vite' +## Migration from v1 -const vueI18nPlugin = { - name: 'vue-i18n', - transform(code, id) { - if (!/vue&type=i18n/.test(id)) { - return - } - if (/\.ya?ml$/.test(id)) { - code = JSON.stringify(require('js-yaml').load(code.trim())) - } - return `export default Comp => { - Comp.i18n = ${code} - }` - } -} - -export default defineConfig({ - plugins: [vue(), vueI18nPlugin] -}) -``` - -## React Support - -React Fast Refresh support is now provided via [`@vitejs/plugin-react`](https://github.com/vitejs/vite/tree/main/packages/plugin-react). - -## HMR API Change - -`import.meta.hot.acceptDeps()` have been deprecated. [`import.meta.hot.accept()`](./api-hmr#hot-accept-deps-cb) can now accept single or multiple deps. - -## Manifest Format Change - -The build manifest now uses the following format: - -```json -{ - "index.js": { - "file": "assets/index.acaf2b48.js", - "imports": [...] - }, - "index.css": { - "file": "assets/index.7b7dbd85.css" - } - "asset.png": { - "file": "assets/asset.0ab0f9cd.png" - } -} -``` - -For entry JS chunks, it also lists its imported chunks which can be used to render preload directives. - -## For Plugin Authors - -Vite 2 uses a completely redesigned plugin interface which extends Rollup plugins. Please read the new [Plugin Development Guide](./api-plugin). - -Some general pointers on migrating a v1 plugin to v2: - -- `resolvers` -> use the [`resolveId`](https://rollupjs.org/guide/en/#resolveid) hook -- `transforms` -> use the [`transform`](https://rollupjs.org/guide/en/#transform) hook -- `indexHtmlTransforms` -> use the [`transformIndexHtml`](./api-plugin#transformindexhtml) hook -- Serving virtual files -> use [`resolveId`](https://rollupjs.org/guide/en/#resolveid) + [`load`](https://rollupjs.org/guide/en/#load) hooks -- Adding `alias`, `define` or other config options -> use the [`config`](./api-plugin#config) hook - -Since most of the logic should be done via plugin hooks instead of middlewares, the need for middlewares is greatly reduced. The internal server app is now a good old [connect](https://github.com/senchalabs/connect) instance instead of Koa. +Check the [Migration from v1 Guide](https://v2.vitejs.dev/guide/migration.html) in the Vite v2 docs first to see the needed changes to port your app to Vite v2, and then proceed with the changes on this page. From f2c869d1dd0bfcaa41c60505ca21c48ec7da7308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Wed, 1 Jun 2022 02:11:57 +0900 Subject: [PATCH 06/29] docs: improve migration from v2 guide (#8417) --- docs/guide/migration.md | 44 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/docs/guide/migration.md b/docs/guide/migration.md index fa52babe69c6da..6021f3c26c1e40 100644 --- a/docs/guide/migration.md +++ b/docs/guide/migration.md @@ -19,14 +19,18 @@ A small fraction of users will now require using [@vitejs/plugin-legacy](https:/ - The following options that were already deprecated in v2 have been removed: - - `optimizeDeps.keepNames` (switch to [`optimizeDeps.esbuildOptions.keepNames`](../config/dep-optimization-options.md#optimizedepsesbuildoptions)) - - `build.base` (switch to [`base`](../config/shared-options.md#base)) - `alias` (switch to [`resolve.alias`](../config/shared-options.md#resolvealias)) - `dedupe` (switch to [`resolve.dedupe`](../config/shared-options.md#resolvededupe)) - - `polyfillDynamicImport` (use [`@vitejs/plugin-legacy](https://github.com/vitejs/vite/tree/main/packages/plugin-legacy) for browsers without dynamic import support) + - `build.base` (switch to [`base`](../config/shared-options.md#base)) + - `build.brotliSize` (switch to [`build.reportCompressedSize`](../config/build-options.md#build-reportcompressedsize)) + - `build.cleanCssOptions` (Vite now uses esbuild for CSS minification) + - `build.polyfillDynamicImport` (use [`@vitejs/plugin-legacy](https://github.com/vitejs/vite/tree/main/packages/plugin-legacy) for browsers without dynamic import support) + - `optimizeDeps.keepNames` (switch to [`optimizeDeps.esbuildOptions.keepNames`](../config/dep-optimization-options.md#optimizedepsesbuildoptions)) ## Dev Server Changes +Vite's default dev server port is now 5173. You can use [`server.port`](../config/server-options.md#server-port) to set it to 3000. + Vite optimizes dependencies with esbuild to both convert CJS-only deps to ESM and to reduce the number of modules the browser needs to request. In v3, the default strategy to discover and batch dependencies has changed. Vite no longer pre-scans user code with esbuild to get an initial list of dependencies on cold start. Instead, it delays the first dependency optimization run until every imported user module on load is processed. To get back the v2 strategy, you can use [`optimizeDeps.devScan`](../config/dep-optimization-options.md#optimizedepsdevscan). @@ -45,9 +49,41 @@ If using ESM for SSR isn't possible in your project, you can set `ssr.format: 'c ## General Changes +- JS file extensions in SSR and lib mode now use a valid extension (`js`, `mjs`, or `cjs`) for output JS entries and chunks based on their format and the package type. + +### `import.meta.glob` + - [Raw `import.meta.glob`](features.md#glob-import-as) switched from `{ assert: { type: 'raw' }}` to `{ as: 'raw' }` +- Keys of `import.meta.glob` are now relative to the current module. -- JS file extensions in SSR and lib mode now use a valid extension (`js`, `mjs`, or `cjs`) for output JS entries and chunks based on their format and the package type. + ```diff + // file: /foo/index.js + const modules = import.meta.glob('../foo/*.js') + + // transformed: + const modules = { + - '../foo/bar.js': () => {} + + './bar.js': () => {} + } + ``` + +- When using an alias with `import.meta.glob`, the keys are always absolute. +- `import.meta.globEager` is now deprecated. Use `import.meta.glob('*', { eager: true })` instead. + +### WebAssembly support + +`import init from 'example.wasm'` syntax is dropped to prevent future collision with ["ESM integration for Wasm"](https://github.com/WebAssembly/esm-integration). +You can use `?init` which is similar to the previous behavior. + +```diff +-import init from 'example.wasm' ++import init from 'example.wasm?init' + +-init().then((instance) => { ++init().then(({ exports }) => { + exports.test() +}) +``` ## Migration from v1 From 08d594b823d2c82b7eb5301e5564a1d6aad79d66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Wed, 1 Jun 2022 04:52:01 +0900 Subject: [PATCH 07/29] fix: make array `acornInjectPlugins` work (fixes #8410) (#8415) --- packages/vite/src/node/server/pluginContainer.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/vite/src/node/server/pluginContainer.ts b/packages/vite/src/node/server/pluginContainer.ts index 1b7e2e421960ad..ec051205eee379 100644 --- a/packages/vite/src/node/server/pluginContainer.ts +++ b/packages/vite/src/node/server/pluginContainer.ts @@ -58,6 +58,7 @@ import colors from 'picocolors' import type * as postcss from 'postcss' import type { Plugin } from '../plugin' import { + arraify, cleanUrl, combineSourcemaps, createDebugger, @@ -496,7 +497,9 @@ export async function createPluginContainer( (await plugin.options.call(minimalContext, options)) || options } if (options.acornInjectPlugins) { - parser = acorn.Parser.extend(options.acornInjectPlugins as any) + parser = acorn.Parser.extend( + ...(arraify(options.acornInjectPlugins) as any) + ) } return { acorn, From ab23e6e7b490cf610a4465cc533f671a729fdfa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Wed, 1 Jun 2022 13:46:41 +0900 Subject: [PATCH 08/29] chore: reapply #5930 (#8423) --- packages/vite/src/node/build.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/vite/src/node/build.ts b/packages/vite/src/node/build.ts index 5e2da01695a0a1..705465400ca5ca 100644 --- a/packages/vite/src/node/build.ts +++ b/packages/vite/src/node/build.ts @@ -437,6 +437,16 @@ async function doBuild( try { const buildOutputOptions = (output: OutputOptions = {}): OutputOptions => { + // See https://github.com/vitejs/vite/issues/5812#issuecomment-984345618 + // @ts-ignore + if (output.output) { + config.logger.warn( + `You've set "rollupOptions.output.output" in your config. ` + + `This is deprecated and will override all Vite.js default output options. ` + + `Please use "rollupOptions.output" instead.` + ) + } + const cjsSsrBuild = ssr && config.ssr?.format === 'cjs' const format = output.format || (cjsSsrBuild ? 'cjs' : 'es') const jsExt = From 89d6711cd97c66d63eb20e6aad72459a0324e7a7 Mon Sep 17 00:00:00 2001 From: patak Date: Wed, 1 Jun 2022 07:18:55 +0200 Subject: [PATCH 09/29] fix: SSR deep imports externalization (fixes #8420) (#8421) * fix: respect package.exports when resolving SSR deep imports * fix: don't externalize .css deep imports --- packages/vite/src/node/plugins/resolve.ts | 14 +++++++--- packages/vite/src/node/ssr/ssrExternal.ts | 32 ++++++++--------------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/packages/vite/src/node/plugins/resolve.ts b/packages/vite/src/node/plugins/resolve.ts index 538389828ed2c2..33a5ad31efe5b4 100644 --- a/packages/vite/src/node/plugins/resolve.ts +++ b/packages/vite/src/node/plugins/resolve.ts @@ -262,7 +262,6 @@ export function resolvePlugin(baseOptions: InternalResolveOptions): Plugin { // bare package imports, perform node resolve if (bareImportRE.test(id)) { const external = options.shouldExternalize?.(id) - if ( !external && asSrc && @@ -636,8 +635,17 @@ export function tryNodeResolve( return resolved } const resolvedExt = path.extname(resolved.id) - const resolvedId = - isDeepImport && path.extname(id) !== resolvedExt ? id + resolvedExt : id + let resolvedId = id + if (isDeepImport) { + // check ext before externalizing - only externalize + // extension-less imports and explicit .js imports + if (resolvedExt && !resolved.id.match(/(.js|.mjs|.cjs)$/)) { + return + } + if (!pkg?.data.exports && path.extname(id) !== resolvedExt) { + resolvedId += resolvedExt + } + } return { ...resolved, id: resolvedId, external: true } } diff --git a/packages/vite/src/node/ssr/ssrExternal.ts b/packages/vite/src/node/ssr/ssrExternal.ts index 512ec038a21244..fbba0e735f7df1 100644 --- a/packages/vite/src/node/ssr/ssrExternal.ts +++ b/packages/vite/src/node/ssr/ssrExternal.ts @@ -139,29 +139,19 @@ function createIsSsrExternal( isBuild: true } - const isPackageEntry = (id: string) => { + const isValidPackageEntry = (id: string) => { if (!bareImportRE.test(id) || id.includes('\0')) { return false } - if ( - tryNodeResolve( - id, - undefined, - resolveOptions, - ssr?.target === 'webworker', - undefined, - true - ) - ) { - return true - } - try { - // no main entry, but deep imports may be allowed - if (resolveFrom(`${id}/package.json`, root)) { - return true - } - } catch {} - return false + return !!tryNodeResolve( + id, + undefined, + resolveOptions, + ssr?.target === 'webworker', + undefined, + true, + true // try to externalize, will return undefined if not possible + ) } return (id: string) => { @@ -171,7 +161,7 @@ function createIsSsrExternal( const external = !id.startsWith('.') && !path.isAbsolute(id) && - (isBuiltin(id) || (isConfiguredAsExternal(id) && isPackageEntry(id))) + (isBuiltin(id) || (isConfiguredAsExternal(id) && isValidPackageEntry(id))) processedIds.set(id, external) return external } From 12f8d28bb6d1342a82d585bc54f249a6a41ea6c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Wed, 1 Jun 2022 14:20:11 +0900 Subject: [PATCH 10/29] docs: add advanced section to migration from v2 guide (#8422) --- docs/guide/migration.md | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/docs/guide/migration.md b/docs/guide/migration.md index 6021f3c26c1e40..78b738afb82ddf 100644 --- a/docs/guide/migration.md +++ b/docs/guide/migration.md @@ -24,7 +24,7 @@ A small fraction of users will now require using [@vitejs/plugin-legacy](https:/ - `build.base` (switch to [`base`](../config/shared-options.md#base)) - `build.brotliSize` (switch to [`build.reportCompressedSize`](../config/build-options.md#build-reportcompressedsize)) - `build.cleanCssOptions` (Vite now uses esbuild for CSS minification) - - `build.polyfillDynamicImport` (use [`@vitejs/plugin-legacy](https://github.com/vitejs/vite/tree/main/packages/plugin-legacy) for browsers without dynamic import support) + - `build.polyfillDynamicImport` (use [`@vitejs/plugin-legacy`](https://github.com/vitejs/vite/tree/main/packages/plugin-legacy) for browsers without dynamic import support) - `optimizeDeps.keepNames` (switch to [`optimizeDeps.esbuildOptions.keepNames`](../config/dep-optimization-options.md#optimizedepsesbuildoptions)) ## Dev Server Changes @@ -37,7 +37,7 @@ To get back the v2 strategy, you can use [`optimizeDeps.devScan`](../config/dep- ## Build Changes -In v3, Vite uses esbuild to optimize dependencies by default. Doing so, it removes one of the most significant differences between dev and prod present in v2. Because esbuild converts CJS-only dependencies to ESM, [`@rollupjs/plugin-commonjs`] is no longer used. +In v3, Vite uses esbuild to optimize dependencies by default. Doing so, it removes one of the most significant differences between dev and prod present in v2. Because esbuild converts CJS-only dependencies to ESM, [`@rollupjs/plugin-commonjs`](https://github.com/rollup/plugins/tree/master/packages/commonjs) is no longer used. If you need to get back to the v2 strategy, you can use [`optimizeDeps.disabled: 'build'`](../config/dep-optimization-options.md#optimizedepsdisabled). @@ -85,6 +85,29 @@ You can use `?init` which is similar to the previous behavior. }) ``` +## Advanced + +There are some changes which only affects plugin/tool creators. + +- [[#5868] refactor: remove deprecated api for 3.0](https://github.com/vitejs/vite/pull/5868) + - `printHttpServerUrls` is removed + - `server.app`, `server.transformWithEsbuild` are removed + - `import.meta.hot.acceptDeps` is removed +- [[#7995] chore: do not fixStacktrace](https://github.com/vitejs/vite/pull/7995) + - `ssrLoadModule`'s `fixStacktrace` option's default is now `false` +- [[#8178] feat!: migrate to ESM](https://github.com/vitejs/vite/pull/8178) + - `formatPostcssSourceMap` is now async + - `resolvePackageEntry`, `resolvePackageData` are no longer available from CJS build (dynamic import is needed to use in CJS) + +Also there are other breaking changes which only affect few users. + +- [[#5018] feat: enable `generatedCode: 'es2015'` for rollup build](https://github.com/vitejs/vite/pull/5018) + - Transpile to ES5 is now necessary even if the user code only includes ES5. +- [[#7877] fix: vite client types](https://github.com/vitejs/vite/pull/7877) + - `/// ` is removed from `vite/client.d.ts`. `{ "lib": ["dom"] }` or `{ "lib": ["webworker"] }` is necessary in `tsconfig.json`. +- [[#8280] feat: non-blocking esbuild optimization at build time](https://github.com/vitejs/vite/pull/8280) + - `server.force` option was removed in favor of `force` option. + ## Migration from v1 Check the [Migration from v1 Guide](https://v2.vitejs.dev/guide/migration.html) in the Vite v2 docs first to see the needed changes to port your app to Vite v2, and then proceed with the changes on this page. From 5ab336ac9075a55f742179fe5c412e0c4d940668 Mon Sep 17 00:00:00 2001 From: patak Date: Wed, 1 Jun 2022 09:48:34 +0200 Subject: [PATCH 11/29] test: guard against ssr-vue hmr in CI flakiness (#8426) --- playground/ssr-vue/__tests__/ssr-vue.spec.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/playground/ssr-vue/__tests__/ssr-vue.spec.ts b/playground/ssr-vue/__tests__/ssr-vue.spec.ts index a70586a5427df5..47dc436b45d72e 100644 --- a/playground/ssr-vue/__tests__/ssr-vue.spec.ts +++ b/playground/ssr-vue/__tests__/ssr-vue.spec.ts @@ -159,7 +159,10 @@ test('hydration', async () => { }) test('hmr', async () => { - await page.goto(url) + // This is test is flaky in Mac CI, but can't be reproduced locally. Wait until + // network idle to avoid the issue. TODO: This may be caused by a bug when + // modifying a file while loading, we should remove this guard + await page.goto(url, { waitUntil: 'networkidle' }) editFile('src/pages/Home.vue', (code) => code.replace('Home', 'changed')) await untilUpdated(() => page.textContent('h1'), 'changed') }) From 01b5ebf8cb8a0fed0be58edd1a6acb185ff3aa9b Mon Sep 17 00:00:00 2001 From: Jack Franklin Date: Wed, 1 Jun 2022 11:36:04 +0100 Subject: [PATCH 12/29] docs: CSS `?inline` query parameter (#8050) Co-authored-by: patak Co-authored-by: Bjorn Lu --- docs/guide/features.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/guide/features.md b/docs/guide/features.md index 2006b5850fd4a2..d58a09e4cbf2cb 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -210,6 +210,15 @@ Vite improves `@import` resolving for Sass and Less so that Vite aliases are als You can also use CSS modules combined with pre-processors by prepending `.module` to the file extension, for example `style.module.scss`. +### Disabling CSS injection into the page + +The automatic injection of CSS contents can be turned off via the `?inline` query parameter. In this case, the processed CSS string is returned as the module's default export as usual, but the styles aren't injected to the page. + +```js +import styles from './foo.css' // will be injected into the page +import otherStyles from './bar.css?inline' // will not be injected into the page +``` + ## Static Assets Importing a static asset will return the resolved public URL when it is served: From 4370d9123da20c586938753d9f606d84907334c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Wed, 1 Jun 2022 20:28:15 +0900 Subject: [PATCH 13/29] refactor(plugin-legacy): improve default polyfill (#8312) --- packages/plugin-legacy/src/index.ts | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/packages/plugin-legacy/src/index.ts b/packages/plugin-legacy/src/index.ts index ff6f30685e199a..05e276ca3b1ff8 100644 --- a/packages/plugin-legacy/src/index.ts +++ b/packages/plugin-legacy/src/index.ts @@ -63,12 +63,7 @@ function viteLegacyPlugin(options: Options = {}): Plugin[] { const facadeToLegacyPolyfillMap = new Map() const facadeToModernPolyfillMap = new Map() const modernPolyfills = new Set() - // System JS relies on the Promise interface. It needs to be polyfilled for IE 11. (array.iterator is mandatory for supporting Promise.all) - const DEFAULT_LEGACY_POLYFILL = [ - 'core-js/modules/es.promise', - 'core-js/modules/es.array.iterator' - ] - const legacyPolyfills = new Set(DEFAULT_LEGACY_POLYFILL) + const legacyPolyfills = new Set() if (Array.isArray(options.modernPolyfills)) { options.modernPolyfills.forEach((i) => { @@ -150,11 +145,13 @@ function viteLegacyPlugin(options: Options = {}): Plugin[] { // legacy bundle if (legacyPolyfills.size || genDynamicFallback) { - if (!legacyPolyfills.has('es.promise')) { - // check if the target needs Promise polyfill because SystemJS relies - // on it - await detectPolyfills(`Promise.resolve()`, targets, legacyPolyfills) - } + // check if the target needs Promise polyfill because SystemJS relies on it + // https://github.com/systemjs/systemjs#ie11-support + await detectPolyfills( + `Promise.resolve(); Promise.all();`, + targets, + legacyPolyfills + ) isDebug && console.log( From 8a68fbda68f3b41aa0a7e744ae0b3bfbdcb7bd9b Mon Sep 17 00:00:00 2001 From: patak-dev Date: Wed, 1 Jun 2022 14:19:26 +0200 Subject: [PATCH 14/29] release: v3.0.0-alpha.9 --- packages/vite/CHANGELOG.md | 8 ++++++++ packages/vite/package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/vite/CHANGELOG.md b/packages/vite/CHANGELOG.md index 5d8403b38dd72e..7711aee6dc3b1f 100644 --- a/packages/vite/CHANGELOG.md +++ b/packages/vite/CHANGELOG.md @@ -1,3 +1,11 @@ +## 3.0.0-alpha.9 (2022-06-01) + +* fix: make array `acornInjectPlugins` work (fixes #8410) (#8415) ([08d594b](https://github.com/vitejs/vite/commit/08d594b)), closes [#8410](https://github.com/vitejs/vite/issues/8410) [#8415](https://github.com/vitejs/vite/issues/8415) +* fix: SSR deep imports externalization (fixes #8420) (#8421) ([89d6711](https://github.com/vitejs/vite/commit/89d6711)), closes [#8420](https://github.com/vitejs/vite/issues/8420) [#8421](https://github.com/vitejs/vite/issues/8421) +* chore: reapply #5930 (#8423) ([ab23e6e](https://github.com/vitejs/vite/commit/ab23e6e)), closes [#5930](https://github.com/vitejs/vite/issues/5930) [#8423](https://github.com/vitejs/vite/issues/8423) + + + ## 3.0.0-alpha.8 (2022-05-31) * feat: add ssr.format to force esm output for ssr (#6812) ([337b197](https://github.com/vitejs/vite/commit/337b197)), closes [#6812](https://github.com/vitejs/vite/issues/6812) diff --git a/packages/vite/package.json b/packages/vite/package.json index 850ce7111e07ec..3b20f0e566d28b 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -1,6 +1,6 @@ { "name": "vite", - "version": "3.0.0-alpha.8", + "version": "3.0.0-alpha.9", "type": "module", "license": "MIT", "author": "Evan You", From 5a57626fcb4bd86f05d01e2aaca486d22d04f51b Mon Sep 17 00:00:00 2001 From: moyinzi <632378816@qq.com> Date: Wed, 1 Jun 2022 20:58:02 +0800 Subject: [PATCH 15/29] fix: not match \n when injecting esbuild helpers (#8414) --- packages/vite/src/node/plugins/esbuild.ts | 4 ++-- playground/lib/vite.config.js | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/vite/src/node/plugins/esbuild.ts b/packages/vite/src/node/plugins/esbuild.ts index 22fabf27099c00..11f5ea4dd6cec0 100644 --- a/packages/vite/src/node/plugins/esbuild.ts +++ b/packages/vite/src/node/plugins/esbuild.ts @@ -27,9 +27,9 @@ import { searchForWorkspaceRoot } from '..' const debug = createDebugger('vite:esbuild') const INJECT_HELPERS_IIFE_RE = - /(.*)((?:const|var) [^\s]+=function\([^)]*?\){"use strict";)(.*)/ + /(.*)((?:const|var) [^\s]+=function\([^)]*?\){"use strict";)(.*)/s const INJECT_HELPERS_UMD_RE = - /(.*)(\(function\([^)]*?\){.+amd.+function\([^)]*?\){"use strict";)(.*)/ + /(.*)(\(function\([^)]*?\){.+amd.+function\([^)]*?\){"use strict";)(.*)/s let server: ViteDevServer diff --git a/playground/lib/vite.config.js b/playground/lib/vite.config.js index 72c040d38d6dcf..f4cacb9d73d3cc 100644 --- a/playground/lib/vite.config.js +++ b/playground/lib/vite.config.js @@ -6,6 +6,11 @@ const path = require('path') */ module.exports = { build: { + rollupOptions: { + output: { + banner: `/*!\nMayLib\n*/` + } + }, lib: { entry: path.resolve(__dirname, 'src/main.js'), name: 'MyLib', From b3d965277741f03890d2dc6881a2c8605c6b5ac3 Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Wed, 1 Jun 2022 09:00:47 -0400 Subject: [PATCH 16/29] fix: remove empty chunk css imports when using esnext (#8345) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 翠 / green --- packages/vite/src/node/plugins/css.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index c177e5a665f731..1fa30f6a272ddd 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -558,8 +558,8 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { .replace(/\./g, '\\.') const emptyChunkRE = new RegExp( opts.format === 'es' || opts.format === 'system' - ? `\\bimport\\s*"[^"]*(?:${emptyChunkFiles})";\n?` - : `\\brequire\\(\\s*"[^"]*(?:${emptyChunkFiles})"\\);\n?`, + ? `\\bimport\\s*["'][^"']*(?:${emptyChunkFiles})["'];\n?` + : `\\brequire\\(\\s*["'][^"']*(?:${emptyChunkFiles})["']\\);\n?`, 'g' ) for (const file in bundle) { From 7f59989ec1fee7f8b71d297169589e010d3b84e3 Mon Sep 17 00:00:00 2001 From: ZuoChenxue <276657773@qq.com> Date: Thu, 2 Jun 2022 03:55:52 +0800 Subject: [PATCH 17/29] fix(build): use crossorigin for nomodule (#8322) Co-authored-by: sapphi-red --- packages/plugin-legacy/src/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/plugin-legacy/src/index.ts b/packages/plugin-legacy/src/index.ts index 05e276ca3b1ff8..4aaab1aab2412d 100644 --- a/packages/plugin-legacy/src/index.ts +++ b/packages/plugin-legacy/src/index.ts @@ -360,6 +360,7 @@ function viteLegacyPlugin(options: Options = {}): Plugin[] { tag: 'script', attrs: { type: 'module', + crossorigin: true, src: `${config.base}${modernPolyfillFilename}` } }) @@ -390,6 +391,7 @@ function viteLegacyPlugin(options: Options = {}): Plugin[] { tag: 'script', attrs: { nomodule: true, + crossorigin: true, id: legacyPolyfillId, src: `${config.base}${legacyPolyfillFilename}` }, @@ -412,6 +414,7 @@ function viteLegacyPlugin(options: Options = {}): Plugin[] { tag: 'script', attrs: { nomodule: true, + crossorigin: true, // we set the entry path on the element as an attribute so that the // script content will stay consistent - which allows using a constant // hash value for CSP. From a7ffd7ddaf1849d9d2dc7317cdee6e6a524f53fc Mon Sep 17 00:00:00 2001 From: shj Date: Thu, 2 Jun 2022 04:56:47 +0900 Subject: [PATCH 18/29] docs: fix hero link (#8434) --- docs/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/index.md b/docs/index.md index 40e6df844db31a..bdf716bc3d20a7 100644 --- a/docs/index.md +++ b/docs/index.md @@ -14,10 +14,10 @@ hero: actions: - theme: brand text: Get Started - link: /guide/why + link: /guide/ - theme: alt text: Why Vite? - link: /guide/ + link: /guide/why - theme: alt text: View on GitHub link: https://github.com/vitejs/vite From 80e44a8fbb2677396c7ef557460e54979dc04f59 Mon Sep 17 00:00:00 2001 From: Roman Imankulov Date: Thu, 2 Jun 2022 08:15:38 +0100 Subject: [PATCH 19/29] docs: update Heroku deployment instructions (#7910) --- docs/guide/static-deploy.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/guide/static-deploy.md b/docs/guide/static-deploy.md index 7b732cb990cbf6..97ebae14372340 100644 --- a/docs/guide/static-deploy.md +++ b/docs/guide/static-deploy.md @@ -266,12 +266,17 @@ You can also deploy to a [custom domain](http://surge.sh/help/adding-a-custom-do # creates a new app with a specified name $ heroku apps:create example + ``` + +6. Set buildpacks. We use `heroku/nodejs` to build the project and `heroku-buildpack-static` to serve it. - # set buildpack for static sites - $ heroku buildpacks:set https://github.com/heroku/heroku-buildpack-static.git + ```bash + # set buildpacks + $ heroku buildpacks:set heroku/nodejs + $ heroku buildpacks:add https://github.com/heroku/heroku-buildpack-static.git ``` -6. Deploy your site: +7. Deploy your site: ```bash # publish site From e9925948fd4e199625bbb25ee931bed1d50e9618 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Fri, 3 Jun 2022 22:20:03 +0800 Subject: [PATCH 20/29] feat: expose `version` (#8456) --- packages/vite/src/node/publicUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/vite/src/node/publicUtils.ts b/packages/vite/src/node/publicUtils.ts index e24db763814dec..31c7135965a3d8 100644 --- a/packages/vite/src/node/publicUtils.ts +++ b/packages/vite/src/node/publicUtils.ts @@ -3,6 +3,7 @@ * This file will be bundled to ESM and CJS and redirected by ../index.cjs * Please control the side-effects by checking the ./dist/node-cjs/publicUtils.cjs bundle */ +export { VERSION as version } from './constants' export { splitVendorChunkPlugin, splitVendorChunk From d97e4022c4253c42e44483b9c88a7c2b76a713a8 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Fri, 3 Jun 2022 22:22:50 +0800 Subject: [PATCH 21/29] chore: resolve ssr options (#8455) --- packages/vite/src/node/config.ts | 28 ++++----------------- packages/vite/src/node/index.ts | 6 +++++ packages/vite/src/node/ssr/index.ts | 39 +++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 23 deletions(-) create mode 100644 packages/vite/src/node/ssr/index.ts diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index d80f5a60583abc..a9e6ba33f1d0ba 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -41,6 +41,8 @@ import type { PluginContainer } from './server/pluginContainer' import { createPluginContainer } from './server/pluginContainer' import type { PackageCache } from './packages' import { loadEnv, resolveEnvPrefix } from './env' +import type { ResolvedSSROptions, SSROptions } from './ssr' +import { resolveSSROptions } from './ssr' const debug = createDebugger('vite:config') @@ -228,29 +230,6 @@ export interface ExperimentalOptions { importGlobRestoreExtension?: boolean } -export type SSRTarget = 'node' | 'webworker' - -export type SSRFormat = 'esm' | 'cjs' - -export interface SSROptions { - external?: string[] - noExternal?: string | RegExp | (string | RegExp)[] | true - /** - * Define the target for the ssr build. The browser field in package.json - * is ignored for node but used if webworker is the target - * Default: 'node' - */ - target?: SSRTarget - /** - * Define the format for the ssr build. Since Vite v3 the SSR build generates ESM by default. - * `'cjs'` can be selected to generate a CJS build, but it isn't recommended. This option is - * left marked as experimental to give users more time to update to ESM. CJS builds requires - * complex externalization heuristics that aren't present in the ESM format. - * @experimental - */ - format?: SSRFormat -} - export interface ResolveWorkerOptions { format: 'es' | 'iife' plugins: Plugin[] @@ -285,6 +264,7 @@ export type ResolvedConfig = Readonly< server: ResolvedServerOptions build: ResolvedBuildOptions preview: ResolvedPreviewOptions + ssr: ResolvedSSROptions | undefined assetsInclude: (file: string) => boolean logger: Logger createResolver: (options?: Partial) => ResolveFn @@ -491,6 +471,7 @@ export async function resolveConfig( : '' const server = resolveServerOptions(resolvedRoot, config.server, logger) + const ssr = resolveSSROptions(config.ssr) const optimizeDeps = config.optimizeDeps || {} @@ -508,6 +489,7 @@ export async function resolveConfig( cacheDir, command, mode, + ssr, isWorker: false, mainConfig: null, isProduction, diff --git a/packages/vite/src/node/index.ts b/packages/vite/src/node/index.ts index ff8c0ebdf6abab..2eb69befb4ee2a 100644 --- a/packages/vite/src/node/index.ts +++ b/packages/vite/src/node/index.ts @@ -39,6 +39,12 @@ export type { DepsOptimizer, ExportsData } from './optimizer' +export type { + ResolvedSSROptions, + SSROptions, + SSRFormat, + SSRTarget +} from './ssr' export type { Plugin } from './plugin' export type { PackageCache, PackageData } from './packages' export type { diff --git a/packages/vite/src/node/ssr/index.ts b/packages/vite/src/node/ssr/index.ts new file mode 100644 index 00000000000000..84f050ff8e41d4 --- /dev/null +++ b/packages/vite/src/node/ssr/index.ts @@ -0,0 +1,39 @@ +export type SSRTarget = 'node' | 'webworker' +export type SSRFormat = 'esm' | 'cjs' + +export interface SSROptions { + external?: string[] + noExternal?: string | RegExp | (string | RegExp)[] | true + /** + * Define the target for the ssr build. The browser field in package.json + * is ignored for node but used if webworker is the target + * Default: 'node' + */ + target?: SSRTarget + /** + * Define the format for the ssr build. Since Vite v3 the SSR build generates ESM by default. + * `'cjs'` can be selected to generate a CJS build, but it isn't recommended. This option is + * left marked as experimental to give users more time to update to ESM. CJS builds requires + * complex externalization heuristics that aren't present in the ESM format. + * @experimental + */ + format?: SSRFormat +} + +export interface ResolvedSSROptions extends SSROptions { + target: SSRTarget + format: SSRFormat +} + +export function resolveSSROptions( + ssr: SSROptions | undefined +): ResolvedSSROptions | undefined { + if (ssr === undefined) { + return undefined + } + return { + format: 'esm', + target: 'node', + ...ssr + } +} From 64fc61cf76cc62b1cafc2c3dab738c22cb7f2d2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Sun, 5 Jun 2022 15:04:04 +0900 Subject: [PATCH 22/29] perf: disable postcss sourcemap when unused (#8451) --- packages/vite/src/node/plugins/css.ts | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 1fa30f6a272ddd..89bbfd6c8c46f4 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -837,15 +837,19 @@ async function compileCSS( ...postcssOptions, to: id, from: id, - map: { - inline: false, - annotation: false, - // postcss may return virtual files - // we cannot obtain content of them, so this needs to be enabled - sourcesContent: true - // when "prev: preprocessorMap", the result map may include duplicate filename in `postcssResult.map.sources` - // prev: preprocessorMap, - } + ...(devSourcemap + ? { + map: { + inline: false, + annotation: false, + // postcss may return virtual files + // we cannot obtain content of them, so this needs to be enabled + sourcesContent: true + // when "prev: preprocessorMap", the result map may include duplicate filename in `postcssResult.map.sources` + // prev: preprocessorMap, + } + } + : {}) }) // record CSS dependencies from @imports From 129b4997859e3e17bcfd40d422c4dfaecccc15cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Sun, 5 Jun 2022 15:04:32 +0900 Subject: [PATCH 23/29] chore: generate vite sourcemap when not production (#8453) --- packages/vite/rollup.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vite/rollup.config.ts b/packages/vite/rollup.config.ts index 6759ede01d14c2..7232fa2484fff5 100644 --- a/packages/vite/rollup.config.ts +++ b/packages/vite/rollup.config.ts @@ -183,7 +183,7 @@ function createNodeConfig(isProduction: boolean) { ], plugins: createNodePlugins( isProduction, - false, + !isProduction, // in production we use api-extractor for dts generation // in development we need to rely on the rollup ts plugin isProduction ? false : path.resolve(__dirname, 'dist/node') From 1061bbdaf81c2808ed8a86946cf0e07a0d8e5b13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Sun, 5 Jun 2022 15:28:10 +0900 Subject: [PATCH 24/29] fix(optimizer): external require-import conversion (fixes #2492, #3409) (#8459) --- .../src/node/optimizer/esbuildDepPlugin.ts | 28 ++++++++++++++++++- .../__tests__/optimize-deps.spec.ts | 6 +++- .../optimize-deps/dep-cjs-with-assets/foo.css | 3 ++ .../dep-cjs-with-assets/index.js | 3 ++ .../dep-cjs-with-assets/package.json | 6 ++++ .../optimize-deps/dep-linked-include/test.css | 2 +- playground/optimize-deps/index.html | 8 ++++++ playground/optimize-deps/package.json | 1 + pnpm-lock.yaml | 11 ++++++++ 9 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 playground/optimize-deps/dep-cjs-with-assets/foo.css create mode 100644 playground/optimize-deps/dep-cjs-with-assets/index.js create mode 100644 playground/optimize-deps/dep-cjs-with-assets/package.json diff --git a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts index 0c32af75219438..ee2a6e18cf04c6 100644 --- a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts +++ b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts @@ -13,6 +13,10 @@ import { import { browserExternalId } from '../plugins/resolve' import type { ExportsData } from '.' +const externalWithConversionNamespace = + 'vite:dep-pre-bundle:external-conversion' +const convertedExternalPrefix = 'vite-dep-pre-bundle-external:' + const externalTypes = [ 'css', // supported pre-processor types @@ -80,20 +84,42 @@ export function esbuildDepPlugin( name: 'vite:dep-pre-bundle', setup(build) { // externalize assets and commonly known non-js file types + // See #8459 for more details about this require-import conversion build.onResolve( { filter: new RegExp(`\\.(` + allExternalTypes.join('|') + `)(\\?.*)?$`) }, async ({ path: id, importer, kind }) => { + // if the prefix exist, it is already converted to `import`, so set `external: true` + if (id.startsWith(convertedExternalPrefix)) { + return { + path: id.slice(convertedExternalPrefix.length), + external: true + } + } + const resolved = await resolve(id, importer, kind) if (resolved) { + // here it is not set to `external: true` to convert `require` to `import` return { path: resolved, - external: true + namespace: externalWithConversionNamespace } } } ) + build.onLoad( + { filter: /./, namespace: externalWithConversionNamespace }, + (args) => { + // import itself with prefix (this is the actual part of require-import conversion) + return { + contents: + `export { default } from "${convertedExternalPrefix}${args.path}";` + + `export * from "${convertedExternalPrefix}${args.path}";`, + loader: 'js' + } + } + ) function resolveEntry(id: string) { const flatId = flattenId(id) diff --git a/playground/optimize-deps/__tests__/optimize-deps.spec.ts b/playground/optimize-deps/__tests__/optimize-deps.spec.ts index a32274dca25e0c..929455300ba555 100644 --- a/playground/optimize-deps/__tests__/optimize-deps.spec.ts +++ b/playground/optimize-deps/__tests__/optimize-deps.spec.ts @@ -77,7 +77,11 @@ test('dep with dynamic import', async () => { }) test('dep with css import', async () => { - expect(await getColor('h1')).toBe('red') + expect(await getColor('.dep-linked-include')).toBe('red') +}) + +test('CJS dep with css import', async () => { + expect(await getColor('.cjs-with-assets')).toBe('blue') }) test('dep w/ non-js files handled via plugin', async () => { diff --git a/playground/optimize-deps/dep-cjs-with-assets/foo.css b/playground/optimize-deps/dep-cjs-with-assets/foo.css new file mode 100644 index 00000000000000..8347f9fb0c358e --- /dev/null +++ b/playground/optimize-deps/dep-cjs-with-assets/foo.css @@ -0,0 +1,3 @@ +.cjs-with-assets { + color: blue; +} diff --git a/playground/optimize-deps/dep-cjs-with-assets/index.js b/playground/optimize-deps/dep-cjs-with-assets/index.js new file mode 100644 index 00000000000000..26b296af650d88 --- /dev/null +++ b/playground/optimize-deps/dep-cjs-with-assets/index.js @@ -0,0 +1,3 @@ +require('./foo.css') + +exports.a = 11 diff --git a/playground/optimize-deps/dep-cjs-with-assets/package.json b/playground/optimize-deps/dep-cjs-with-assets/package.json new file mode 100644 index 00000000000000..2e4f6e68c7510c --- /dev/null +++ b/playground/optimize-deps/dep-cjs-with-assets/package.json @@ -0,0 +1,6 @@ +{ + "name": "dep-cjs-with-assets", + "private": true, + "version": "0.0.0", + "main": "index.js" +} diff --git a/playground/optimize-deps/dep-linked-include/test.css b/playground/optimize-deps/dep-linked-include/test.css index 60f1eab97137f7..20ae3263941d1e 100644 --- a/playground/optimize-deps/dep-linked-include/test.css +++ b/playground/optimize-deps/dep-linked-include/test.css @@ -1,3 +1,3 @@ -body { +.dep-linked-include { color: red; } diff --git a/playground/optimize-deps/index.html b/playground/optimize-deps/index.html index 3cd619f9ce9236..c70ab5b2f09322 100644 --- a/playground/optimize-deps/index.html +++ b/playground/optimize-deps/index.html @@ -35,6 +35,12 @@

Detecting linked src package and optimizing its deps (lodash-es)

Optimizing force included dep even when it's linked

+

Dep with CSS

+
This should be red
+ +

CJS Dep with CSS

+
This should be blue
+

import * as ...

@@ -91,6 +97,8 @@

Reused variable names

text('.import-star', `[success] ${keys.join(', ')}`) } + import 'dep-cjs-with-assets' + import { env } from 'dep-node-env' text('.node-env', env) diff --git a/playground/optimize-deps/package.json b/playground/optimize-deps/package.json index 055d23bc9ce597..d323ca77af0a29 100644 --- a/playground/optimize-deps/package.json +++ b/playground/optimize-deps/package.json @@ -13,6 +13,7 @@ "clipboard": "^2.0.11", "dep-cjs-compiled-from-cjs": "file:./dep-cjs-compiled-from-cjs", "dep-cjs-compiled-from-esm": "file:./dep-cjs-compiled-from-esm", + "dep-cjs-with-assets": "file:./dep-cjs-with-assets", "dep-esbuild-plugin-transform": "file:./dep-esbuild-plugin-transform", "dep-linked": "link:./dep-linked", "dep-linked-include": "link:./dep-linked-include", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index acef1fe7331aef..aa07fd3c5ae874 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -576,6 +576,7 @@ importers: clipboard: ^2.0.11 dep-cjs-compiled-from-cjs: file:./dep-cjs-compiled-from-cjs dep-cjs-compiled-from-esm: file:./dep-cjs-compiled-from-esm + dep-cjs-with-assets: file:./dep-cjs-with-assets dep-esbuild-plugin-transform: file:./dep-esbuild-plugin-transform dep-linked: link:./dep-linked dep-linked-include: link:./dep-linked-include @@ -596,6 +597,7 @@ importers: clipboard: 2.0.11 dep-cjs-compiled-from-cjs: file:playground/optimize-deps/dep-cjs-compiled-from-cjs dep-cjs-compiled-from-esm: file:playground/optimize-deps/dep-cjs-compiled-from-esm + dep-cjs-with-assets: file:playground/optimize-deps/dep-cjs-with-assets dep-esbuild-plugin-transform: file:playground/optimize-deps/dep-esbuild-plugin-transform dep-linked: link:dep-linked dep-linked-include: link:dep-linked-include @@ -620,6 +622,9 @@ importers: playground/optimize-deps/dep-cjs-compiled-from-esm: specifiers: {} + playground/optimize-deps/dep-cjs-with-assets: + specifiers: {} + playground/optimize-deps/dep-esbuild-plugin-transform: specifiers: {} @@ -8810,6 +8815,12 @@ packages: version: 0.0.0 dev: false + file:playground/optimize-deps/dep-cjs-with-assets: + resolution: {directory: playground/optimize-deps/dep-cjs-with-assets, type: directory} + name: dep-cjs-with-assets + version: 0.0.0 + dev: false + file:playground/optimize-deps/dep-esbuild-plugin-transform: resolution: {directory: playground/optimize-deps/dep-esbuild-plugin-transform, type: directory} name: dep-esbuild-plugin-transform From e2e44ff32b7b2bcbcae793cdcda263d1ad495fc0 Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Sun, 5 Jun 2022 18:55:45 +0800 Subject: [PATCH 25/29] feat: handle named imports of builtin modules (#8338) --- .../src/node/optimizer/esbuildDepPlugin.ts | 36 ++++++++-- .../vite/src/node/plugins/importAnalysis.ts | 69 +++++++++++++------ packages/vite/src/node/plugins/resolve.ts | 16 +++-- playground/optimize-deps/.env | 1 - .../__tests__/optimize-deps.spec.ts | 41 ++++++++++- .../dep-with-builtin-module-cjs/index.js | 18 +++++ .../dep-with-builtin-module-cjs/package.json | 6 ++ .../dep-with-builtin-module-esm/index.js | 21 ++++++ .../dep-with-builtin-module-esm/package.json | 7 ++ playground/optimize-deps/index.html | 31 +++++++++ playground/optimize-deps/package.json | 2 + playground/optimize-deps/vite.config.js | 13 ++++ pnpm-lock.yaml | 22 ++++++ 13 files changed, 244 insertions(+), 39 deletions(-) delete mode 100644 playground/optimize-deps/.env create mode 100644 playground/optimize-deps/dep-with-builtin-module-cjs/index.js create mode 100644 playground/optimize-deps/dep-with-builtin-module-cjs/package.json create mode 100644 playground/optimize-deps/dep-with-builtin-module-esm/index.js create mode 100644 playground/optimize-deps/dep-with-builtin-module-esm/package.json diff --git a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts index ee2a6e18cf04c6..fbfb658e9f7505 100644 --- a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts +++ b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts @@ -219,15 +219,37 @@ export function esbuildDepPlugin( build.onLoad( { filter: /.*/, namespace: 'browser-external' }, - ({ path: id }) => { + ({ path }) => { return { - contents: - `export default new Proxy({}, { - get() { - throw new Error('Module "${id}" has been externalized for ` + - `browser compatibility and cannot be accessed in client code.') + // Return in CJS to intercept named imports. Use `Object.create` to + // create the Proxy in the prototype to workaround esbuild issue. Why? + // + // In short, esbuild cjs->esm flow: + // 1. Create empty object using `Object.create(Object.getPrototypeOf(module.exports))`. + // 2. Assign props of `module.exports` to the object. + // 3. Return object for ESM use. + // + // If we do `module.exports = new Proxy({}, {})`, step 1 returns empty object, + // step 2 does nothing as there's no props for `module.exports`. The final object + // is just an empty object. + // + // Creating the Proxy in the prototype satisfies step 1 immediately, which means + // the returned object is a Proxy that we can intercept. + // + // Note: Skip keys that are accessed by esbuild and browser devtools. + contents: `\ +module.exports = Object.create(new Proxy({}, { + get(_, key) { + if ( + key !== '__esModule' && + key !== '__proto__' && + key !== 'constructor' && + key !== 'splice' + ) { + throw new Error(\`Module "${path}" has been externalized for browser compatibility. Cannot access "${path}.\${key}" in client code.\`) + } } -})` +}))` } } ) diff --git a/packages/vite/src/node/plugins/importAnalysis.ts b/packages/vite/src/node/plugins/importAnalysis.ts index ba221b4b1d5283..0c279da0601727 100644 --- a/packages/vite/src/node/plugins/importAnalysis.ts +++ b/packages/vite/src/node/plugins/importAnalysis.ts @@ -58,6 +58,7 @@ import { delayDepsOptimizerUntil } from './optimizedDeps' import { isCSSRequest, isDirectCSSRequest } from './css' +import { browserExternalId } from './resolve' const isDebug = !!process.env.DEBUG const debug = createDebugger('vite:import-analysis') @@ -322,11 +323,9 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { s: start, e: end, ss: expStart, - se: expEnd, d: dynamicIndex, // #2083 User may use escape path, // so use imports[index].n to get the unescaped string - // @ts-ignore n: specifier } = imports[index] @@ -434,29 +433,20 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { } } else if (needsInterop) { debug(`${url} needs interop`) - if (isDynamicImport) { - // rewrite `import('package')` to expose the default directly - str().overwrite( - expStart, - expEnd, - `import('${url}').then(m => m.default && m.default.__esModule ? m.default : ({ ...m.default, default: m.default }))`, - { contentOnly: true } - ) - } else { - const exp = source.slice(expStart, expEnd) - const rewritten = transformCjsImport(exp, url, rawUrl, index) - if (rewritten) { - str().overwrite(expStart, expEnd, rewritten, { - contentOnly: true - }) - } else { - // #1439 export * from '...' - str().overwrite(start, end, url, { contentOnly: true }) - } - } + interopNamedImports(str(), imports[index], url, index) rewriteDone = true } } + // If source code imports builtin modules via named imports, the stub proxy export + // would fail as it's `export default` only. Apply interop for builtin modules to + // correctly throw the error message. + else if ( + url.includes(browserExternalId) && + source.slice(expStart, start).includes('{') + ) { + interopNamedImports(str(), imports[index], url, index) + rewriteDone = true + } if (!rewriteDone) { str().overwrite(start, end, isDynamicImport ? `'${url}'` : url, { contentOnly: true @@ -639,6 +629,41 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { } } +function interopNamedImports( + str: MagicString, + importSpecifier: ImportSpecifier, + rewrittenUrl: string, + importIndex: number +) { + const source = str.original + const { + s: start, + e: end, + ss: expStart, + se: expEnd, + d: dynamicIndex + } = importSpecifier + if (dynamicIndex > -1) { + // rewrite `import('package')` to expose the default directly + str.overwrite( + expStart, + expEnd, + `import('${rewrittenUrl}').then(m => m.default && m.default.__esModule ? m.default : ({ ...m.default, default: m.default }))`, + { contentOnly: true } + ) + } else { + const exp = source.slice(expStart, expEnd) + const rawUrl = source.slice(start, end) + const rewritten = transformCjsImport(exp, rewrittenUrl, rawUrl, importIndex) + if (rewritten) { + str.overwrite(expStart, expEnd, rewritten, { contentOnly: true }) + } else { + // #1439 export * from '...' + str.overwrite(start, end, rewrittenUrl, { contentOnly: true }) + } + } +} + type ImportNameSpecifier = { importedName: string; localName: string } /** diff --git a/packages/vite/src/node/plugins/resolve.ts b/packages/vite/src/node/plugins/resolve.ts index 33a5ad31efe5b4..7ccc3a7eb5cb7a 100644 --- a/packages/vite/src/node/plugins/resolve.ts +++ b/packages/vite/src/node/plugins/resolve.ts @@ -339,15 +339,17 @@ export function resolvePlugin(baseOptions: InternalResolveOptions): Plugin { load(id) { if (id.startsWith(browserExternalId)) { - return isProduction - ? `export default {}` - : `export default new Proxy({}, { - get() { - throw new Error('Module "${id.slice( - browserExternalId.length + 1 - )}" has been externalized for browser compatibility and cannot be accessed in client code.') + if (isProduction) { + return `export default {}` + } else { + id = id.slice(browserExternalId.length + 1) + return `\ +export default new Proxy({}, { + get(_, key) { + throw new Error(\`Module "${id}" has been externalized for browser compatibility. Cannot access "${id}.\${key}" in client code.\`) } })` + } } } } diff --git a/playground/optimize-deps/.env b/playground/optimize-deps/.env deleted file mode 100644 index 995fca4af2ee24..00000000000000 --- a/playground/optimize-deps/.env +++ /dev/null @@ -1 +0,0 @@ -NODE_ENV=production \ No newline at end of file diff --git a/playground/optimize-deps/__tests__/optimize-deps.spec.ts b/playground/optimize-deps/__tests__/optimize-deps.spec.ts index 929455300ba555..bf4fee61e4e74c 100644 --- a/playground/optimize-deps/__tests__/optimize-deps.spec.ts +++ b/playground/optimize-deps/__tests__/optimize-deps.spec.ts @@ -1,4 +1,11 @@ -import { getColor, page } from '~utils' +import { + browserErrors, + browserLogs, + getColor, + isBuild, + isServe, + page +} from '~utils' test('default + named imports from cjs dep (react)', async () => { expect(await page.textContent('.cjs button')).toBe('count is 0') @@ -63,7 +70,7 @@ test('import * from optimized dep', async () => { }) test('import from dep with process.env.NODE_ENV', async () => { - expect(await page.textContent('.node-env')).toMatch(`prod`) + expect(await page.textContent('.node-env')).toMatch(isBuild ? 'prod' : 'dev') }) test('import from dep with .notjs files', async () => { @@ -113,3 +120,33 @@ test('import aliased package with colon', async () => { test('variable names are reused in different scripts', async () => { expect(await page.textContent('.reused-variable-names')).toBe('reused') }) + +test.runIf(isServe)('error on builtin modules usage', () => { + expect(browserLogs).toEqual( + expect.arrayContaining([ + // from dep-with-builtin-module-esm top-level try-catch + expect.stringContaining( + 'dep-with-builtin-module-esm Error: Module "fs" has been externalized for browser compatibility. Cannot access "fs.readFileSync" in client code.' + ), + expect.stringContaining( + 'dep-with-builtin-module-esm Error: Module "path" has been externalized for browser compatibility. Cannot access "path.join" in client code.' + ), + // from dep-with-builtin-module-cjs top-level try-catch + expect.stringContaining( + 'dep-with-builtin-module-cjs Error: Module "path" has been externalized for browser compatibility. Cannot access "path.join" in client code.' + ) + ]) + ) + + expect(browserErrors.map((error) => error.message)).toEqual( + expect.arrayContaining([ + // from user source code + 'Module "buffer" has been externalized for browser compatibility. Cannot access "buffer.Buffer" in client code.', + 'Module "child_process" has been externalized for browser compatibility. Cannot access "child_process.execSync" in client code.', + // from dep-with-builtin-module-esm read() + 'Module "fs" has been externalized for browser compatibility. Cannot access "fs.readFileSync" in client code.', + // from dep-with-builtin-module-esm read() + 'Module "fs" has been externalized for browser compatibility. Cannot access "fs.readFileSync" in client code.' + ]) + ) +}) diff --git a/playground/optimize-deps/dep-with-builtin-module-cjs/index.js b/playground/optimize-deps/dep-with-builtin-module-cjs/index.js new file mode 100644 index 00000000000000..920a0da0d97dda --- /dev/null +++ b/playground/optimize-deps/dep-with-builtin-module-cjs/index.js @@ -0,0 +1,18 @@ +const fs = require('fs') +const path = require('path') + +// NOTE: require destructure would error immediately because of how esbuild +// compiles it. There's no way around it as it's direct property access, which +// triggers the Proxy get trap. + +// access from default import +try { + path.join() +} catch (e) { + console.log('dep-with-builtin-module-cjs', e) +} + +// access from function +module.exports.read = () => { + return fs.readFileSync('test') +} diff --git a/playground/optimize-deps/dep-with-builtin-module-cjs/package.json b/playground/optimize-deps/dep-with-builtin-module-cjs/package.json new file mode 100644 index 00000000000000..8b39306140e804 --- /dev/null +++ b/playground/optimize-deps/dep-with-builtin-module-cjs/package.json @@ -0,0 +1,6 @@ +{ + "name": "dep-with-builtin-module-cjs", + "private": true, + "version": "0.0.0", + "main": "index.js" +} diff --git a/playground/optimize-deps/dep-with-builtin-module-esm/index.js b/playground/optimize-deps/dep-with-builtin-module-esm/index.js new file mode 100644 index 00000000000000..e3734720051aca --- /dev/null +++ b/playground/optimize-deps/dep-with-builtin-module-esm/index.js @@ -0,0 +1,21 @@ +import { readFileSync } from 'fs' +import path from 'path' + +// access from named import +try { + readFileSync() +} catch (e) { + console.log('dep-with-builtin-module-esm', e) +} + +// access from default import +try { + path.join() +} catch (e) { + console.log('dep-with-builtin-module-esm', e) +} + +// access from function +export function read() { + return readFileSync('test') +} diff --git a/playground/optimize-deps/dep-with-builtin-module-esm/package.json b/playground/optimize-deps/dep-with-builtin-module-esm/package.json new file mode 100644 index 00000000000000..e01897e5a51850 --- /dev/null +++ b/playground/optimize-deps/dep-with-builtin-module-esm/package.json @@ -0,0 +1,7 @@ +{ + "name": "dep-with-builtin-module-esm", + "private": true, + "version": "0.0.0", + "main": "index.js", + "type": "module" +} diff --git a/playground/optimize-deps/index.html b/playground/optimize-deps/index.html index c70ab5b2f09322..39d76fd83d0609 100644 --- a/playground/optimize-deps/index.html +++ b/playground/optimize-deps/index.html @@ -138,3 +138,34 @@

Reused variable names

const reusedName = 'reused' text('.reused-variable-names', reusedName) + + + + + + + + diff --git a/playground/optimize-deps/package.json b/playground/optimize-deps/package.json index d323ca77af0a29..7509b8284b9bc1 100644 --- a/playground/optimize-deps/package.json +++ b/playground/optimize-deps/package.json @@ -19,6 +19,8 @@ "dep-linked-include": "link:./dep-linked-include", "dep-node-env": "file:./dep-node-env", "dep-not-js": "file:./dep-not-js", + "dep-with-builtin-module-cjs": "file:./dep-with-builtin-module-cjs", + "dep-with-builtin-module-esm": "file:./dep-with-builtin-module-esm", "dep-with-dynamic-import": "file:./dep-with-dynamic-import", "lodash-es": "^4.17.21", "nested-exclude": "file:./nested-exclude", diff --git a/playground/optimize-deps/vite.config.js b/playground/optimize-deps/vite.config.js index fb3bbfc4a33eb5..d5da62f3331fc2 100644 --- a/playground/optimize-deps/vite.config.js +++ b/playground/optimize-deps/vite.config.js @@ -62,6 +62,19 @@ module.exports = { return { code } } } + }, + // TODO: Remove this one support for prebundling in build lands. + // It is expected that named importing in build doesn't work + // as it incurs a lot of overhead in build. + { + name: 'polyfill-named-fs-build', + apply: 'build', + enforce: 'pre', + load(id) { + if (id === '__vite-browser-external:fs') { + return `export default {}; export function readFileSync() {}` + } + } } ] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aa07fd3c5ae874..5e2530f0dafc6b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -582,6 +582,8 @@ importers: dep-linked-include: link:./dep-linked-include dep-node-env: file:./dep-node-env dep-not-js: file:./dep-not-js + dep-with-builtin-module-cjs: file:./dep-with-builtin-module-cjs + dep-with-builtin-module-esm: file:./dep-with-builtin-module-esm dep-with-dynamic-import: file:./dep-with-dynamic-import lodash-es: ^4.17.21 nested-exclude: file:./nested-exclude @@ -603,6 +605,8 @@ importers: dep-linked-include: link:dep-linked-include dep-node-env: file:playground/optimize-deps/dep-node-env dep-not-js: file:playground/optimize-deps/dep-not-js + dep-with-builtin-module-cjs: file:playground/optimize-deps/dep-with-builtin-module-cjs + dep-with-builtin-module-esm: file:playground/optimize-deps/dep-with-builtin-module-esm dep-with-dynamic-import: file:playground/optimize-deps/dep-with-dynamic-import lodash-es: 4.17.21 nested-exclude: file:playground/optimize-deps/nested-exclude @@ -646,6 +650,12 @@ importers: playground/optimize-deps/dep-not-js: specifiers: {} + playground/optimize-deps/dep-with-builtin-module-cjs: + specifiers: {} + + playground/optimize-deps/dep-with-builtin-module-esm: + specifiers: {} + playground/optimize-deps/dep-with-dynamic-import: specifiers: {} @@ -8839,6 +8849,18 @@ packages: version: 1.0.0 dev: false + file:playground/optimize-deps/dep-with-builtin-module-cjs: + resolution: {directory: playground/optimize-deps/dep-with-builtin-module-cjs, type: directory} + name: dep-with-builtin-module-cjs + version: 0.0.0 + dev: false + + file:playground/optimize-deps/dep-with-builtin-module-esm: + resolution: {directory: playground/optimize-deps/dep-with-builtin-module-esm, type: directory} + name: dep-with-builtin-module-esm + version: 0.0.0 + dev: false + file:playground/optimize-deps/dep-with-dynamic-import: resolution: {directory: playground/optimize-deps/dep-with-dynamic-import, type: directory} name: dep-with-dynamic-import From 908c9e4cdd2cceb0f01495e38066ffe33c21ddb8 Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Sun, 5 Jun 2022 18:58:48 +0800 Subject: [PATCH 26/29] feat: preserve process env vars in lib build (#8090) --- packages/vite/src/node/plugins/define.ts | 44 ++++++++++++------------ playground/lib/__tests__/lib.spec.ts | 28 +++++++-------- playground/lib/index.dist.html | 9 +++++ playground/lib/src/main.js | 3 ++ 4 files changed, 47 insertions(+), 37 deletions(-) diff --git a/packages/vite/src/node/plugins/define.ts b/packages/vite/src/node/plugins/define.ts index ca0f446cc1f58e..6edb1bd8858a11 100644 --- a/packages/vite/src/node/plugins/define.ts +++ b/packages/vite/src/node/plugins/define.ts @@ -10,15 +10,23 @@ const isNonJsRequest = (request: string): boolean => nonJsRe.test(request) export function definePlugin(config: ResolvedConfig): Plugin { const isBuild = config.command === 'build' - - const processNodeEnv: Record = { - 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || config.mode), - 'global.process.env.NODE_ENV': JSON.stringify( - process.env.NODE_ENV || config.mode - ), - 'globalThis.process.env.NODE_ENV': JSON.stringify( - process.env.NODE_ENV || config.mode - ) + const isBuildLib = isBuild && config.build.lib + + // ignore replace process.env in lib build + const processEnv: Record = {} + const processNodeEnv: Record = {} + if (!isBuildLib) { + const nodeEnv = process.env.NODE_ENV || config.mode + Object.assign(processEnv, { + 'process.env.': `({}).`, + 'global.process.env.': `({}).`, + 'globalThis.process.env.': `({}).` + }) + Object.assign(processNodeEnv, { + 'process.env.NODE_ENV': JSON.stringify(nodeEnv), + 'global.process.env.NODE_ENV': JSON.stringify(nodeEnv), + 'globalThis.process.env.NODE_ENV': JSON.stringify(nodeEnv) + }) } const userDefine: Record = {} @@ -27,7 +35,8 @@ export function definePlugin(config: ResolvedConfig): Plugin { userDefine[key] = typeof val === 'string' ? val : JSON.stringify(val) } - // during dev, import.meta properties are handled by importAnalysis plugin + // during dev, import.meta properties are handled by importAnalysis plugin. + // ignore replace import.meta.env in lib build const importMetaKeys: Record = {} if (isBuild) { const env: Record = { @@ -47,22 +56,13 @@ export function definePlugin(config: ResolvedConfig): Plugin { function generatePattern( ssr: boolean ): [Record, RegExp | null] { - const processEnv: Record = {} - const isNeedProcessEnv = !ssr || config.ssr?.target === 'webworker' - - if (isNeedProcessEnv) { - Object.assign(processEnv, { - 'process.env.': `({}).`, - 'global.process.env.': `({}).`, - 'globalThis.process.env.': `({}).` - }) - } + const replaceProcessEnv = !ssr || config.ssr?.target === 'webworker' const replacements: Record = { - ...(isNeedProcessEnv ? processNodeEnv : {}), + ...(replaceProcessEnv ? processNodeEnv : {}), ...userDefine, ...importMetaKeys, - ...processEnv + ...(replaceProcessEnv ? processEnv : {}) } const replacementsKeys = Object.keys(replacements) diff --git a/playground/lib/__tests__/lib.spec.ts b/playground/lib/__tests__/lib.spec.ts index 32c6edba1bdeb9..9046097f7e8375 100644 --- a/playground/lib/__tests__/lib.spec.ts +++ b/playground/lib/__tests__/lib.spec.ts @@ -1,11 +1,9 @@ -import path from 'path' -import fs from 'fs' import { isBuild, isServe, page, + readFile, serverLogs, - testDir, untilUpdated } from '~utils' @@ -16,20 +14,14 @@ describe.runIf(isBuild)('build', () => { test('umd', async () => { expect(await page.textContent('.umd')).toBe('It works') - const code = fs.readFileSync( - path.join(testDir, 'dist/my-lib-custom-filename.umd.js'), - 'utf-8' - ) + const code = readFile('dist/my-lib-custom-filename.umd.js') // esbuild helpers are injected inside of the UMD wrapper expect(code).toMatch(/^\(function\(/) }) test('iife', async () => { expect(await page.textContent('.iife')).toBe('It works') - const code = fs.readFileSync( - path.join(testDir, 'dist/my-lib-custom-filename.iife.js'), - 'utf-8' - ) + const code = readFile('dist/my-lib-custom-filename.iife.js') // esbuild helpers are injected inside of the IIFE wrapper expect(code).toMatch(/^const MyLib=function\(\){"use strict";/) }) @@ -39,10 +31,7 @@ describe.runIf(isBuild)('build', () => { () => page.textContent('.dynamic-import-message'), 'hello vite' ) - const code = fs.readFileSync( - path.join(testDir, 'dist/lib/dynamic-import-message.es.mjs'), - 'utf-8' - ) + const code = readFile('dist/lib/dynamic-import-message.es.mjs') expect(code).not.toMatch('__vitePreload') // Test that library chunks are hashed @@ -55,6 +44,15 @@ describe.runIf(isBuild)('build', () => { expect(log).not.toMatch('All "@import" rules must come first') }) }) + + test('preserve process.env', () => { + const es = readFile('dist/my-lib-custom-filename.mjs') + const iife = readFile('dist/my-lib-custom-filename.iife.js') + const umd = readFile('dist/my-lib-custom-filename.umd.js') + expect(es).toMatch('process.env.NODE_ENV') + expect(iife).toMatch('process.env.NODE_ENV') + expect(umd).toMatch('process.env.NODE_ENV') + }) }) test.runIf(isServe)('dev', async () => { diff --git a/playground/lib/index.dist.html b/playground/lib/index.dist.html index 02362a9c111e5e..99f08c73396fea 100644 --- a/playground/lib/index.dist.html +++ b/playground/lib/index.dist.html @@ -4,6 +4,15 @@
+ +