diff --git a/docs/guide/build.md b/docs/guide/build.md index 41376b46e5f2f8..5b6ac8824c0aa4 100644 --- a/docs/guide/build.md +++ b/docs/guide/build.md @@ -8,7 +8,7 @@ The production bundle assumes support for modern JavaScript. By default, Vite ta - Chrome >=87 - Firefox >=78 -- Safari >=13 +- Safari >=14 - Edge >=88 You can specify custom targets via the [`build.target` config option](/config/build-options.md#build-target), where the lowest target is `es2015`. diff --git a/docs/guide/env-and-mode.md b/docs/guide/env-and-mode.md index 8c14e9d476fdcb..e3032cf92e5bfe 100644 --- a/docs/guide/env-and-mode.md +++ b/docs/guide/env-and-mode.md @@ -120,20 +120,22 @@ VITE_APP_TITLE=My App In your app, you can render the title using `import.meta.env.VITE_APP_TITLE`. -However, it is important to understand that **mode** is a wider concept than just development vs. production. A typical example is you may want to have a "staging" mode where it should have production-like behavior, but with slightly different env variables from production. - -You can overwrite the default mode used for a command by passing the `--mode` option flag. For example, if you want to build your app for our hypothetical staging mode: +In some cases, you may want to run `vite build` with a different mode to render a different title. You can overwrite the default mode used for a command by passing the `--mode` option flag. For example, if you want to build your app for a staging mode: ```bash vite build --mode staging ``` -And to get the behavior we want, we need a `.env.staging` file: +And create a `.env.staging` file: ``` # .env.staging -NODE_ENV=production VITE_APP_TITLE=My App (staging) ``` -Now your staging app should have production-like behavior, but display a different title from production. +As `vite build` runs a production build by default, you can also change this and run a development build by using a different mode and `.env` file configuration: + +``` +# .env.testing +NODE_ENV=development +``` diff --git a/package.json b/package.json index 020cb5a12cd387..1750cafe823860 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "devDependencies": { "@babel/types": "^7.20.5", "@microsoft/api-extractor": "^7.33.6", - "@rollup/plugin-typescript": "^9.0.2", + "@rollup/plugin-typescript": "^10.0.1", "@types/babel__core": "^7.1.20", "@types/babel__standalone": "^7.1.4", "@types/convert-source-map": "^1.5.2", @@ -122,6 +122,11 @@ "peerDependencies": { "postcss": "*" } + }, + "acorn-walk": { + "peerDependencies": { + "acorn": "*" + } } } } diff --git a/packages/plugin-vue/README.md b/packages/plugin-vue/README.md index 30e2cd6ceb0399..776828f8cdc96e 100644 --- a/packages/plugin-vue/README.md +++ b/packages/plugin-vue/README.md @@ -20,45 +20,48 @@ export interface Options { include?: string | RegExp | (string | RegExp)[] exclude?: string | RegExp | (string | RegExp)[] - ssr?: boolean isProduction?: boolean + // options to pass on to vue/compiler-sfc + script?: Partial> + template?: Partial< + Pick< + SFCTemplateCompileOptions, + | 'compiler' + | 'compilerOptions' + | 'preprocessOptions' + | 'preprocessCustomRequire' + | 'transformAssetUrls' + > + > + style?: Partial> + /** - * Transform Vue SFCs into custom elements (requires vue@^3.2.0) - * - `true` -> all `*.vue` imports are converted into custom elements - * - `string | RegExp` -> matched files are converted into custom elements + * Transform Vue SFCs into custom elements. + * - `true`: all `*.vue` imports are converted into custom elements + * - `string | RegExp`: matched files are converted into custom elements * * @default /\.ce\.vue$/ */ customElement?: boolean | string | RegExp | (string | RegExp)[] /** - * Enable Vue reactivity transform (experimental, requires vue@^3.2.25). - * https://github.com/vuejs/core/tree/master/packages/reactivity-transform - * + * Enable Vue reactivity transform (experimental). + * https://vuejs.org/guide/extras/reactivity-transform.html * - `true`: transform will be enabled for all vue,js(x),ts(x) files except * those inside node_modules * - `string | RegExp`: apply to vue + only matched files (will include - * node_modules, so specify directories in necessary) + * node_modules, so specify directories if necessary) * - `false`: disable in all cases * * @default false */ reactivityTransform?: boolean | string | RegExp | (string | RegExp)[] - // options to pass on to vue/compiler-sfc - script?: Partial> - template?: Partial< - Pick< - SFCTemplateCompileOptions, - | 'compiler' - | 'compilerOptions' - | 'preprocessOptions' - | 'preprocessCustomRequire' - | 'transformAssetUrls' - > - > - style?: Partial> + /** + * Use custom compiler-sfc instance. Can be used to force a specific version. + */ + compiler?: typeof _compiler } ``` diff --git a/packages/vite/LICENSE.md b/packages/vite/LICENSE.md index 6cc6a3e043c602..05c752516169fe 100644 --- a/packages/vite/LICENSE.md +++ b/packages/vite/LICENSE.md @@ -566,6 +566,35 @@ Repository: https://github.com/acornjs/acorn.git --------------------------------------- +## acorn-walk +License: MIT +By: Marijn Haverbeke, Ingvar Stepanyan, Adrian Heine +Repository: https://github.com/acornjs/acorn.git + +> MIT License +> +> Copyright (C) 2012-2020 by various contributors (see AUTHORS) +> +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in +> all copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +> THE SOFTWARE. + +--------------------------------------- + ## ansi-regex License: MIT By: Sindre Sorhus diff --git a/packages/vite/package.json b/packages/vite/package.json index 90ea1e0cd818a3..2899ada1a5511a 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -76,9 +76,10 @@ "@rollup/plugin-dynamic-import-vars": "^2.0.1", "@rollup/plugin-json": "^5.0.2", "@rollup/plugin-node-resolve": "15.0.1", - "@rollup/plugin-typescript": "^9.0.2", + "@rollup/plugin-typescript": "^10.0.1", "@rollup/pluginutils": "^5.0.2", "acorn": "^8.8.1", + "acorn-walk": "^8.2.0", "cac": "^6.7.14", "chokidar": "^3.5.3", "connect": "^3.7.0", diff --git a/packages/vite/src/node/__tests__/env.spec.ts b/packages/vite/src/node/__tests__/env.spec.ts index f1e74bc0193cfc..6482e7a261714c 100644 --- a/packages/vite/src/node/__tests__/env.spec.ts +++ b/packages/vite/src/node/__tests__/env.spec.ts @@ -15,7 +15,6 @@ describe('loadEnv', () => { "VITE_ENV1": "ENV1", "VITE_ENV2": "ENV2", "VITE_ENV3": "ENV3", - "VITE_USER_NODE_ENV": "production", } `) }) @@ -36,14 +35,13 @@ describe('loadEnv', () => { { "VITE_APP_BASE_ROUTE": "/app/", "VITE_APP_BASE_URL": "/app/", - "VITE_USER_NODE_ENV": "production", } `) }) test('VITE_USER_NODE_ENV', () => { loadEnv('development', join(__dirname, './env')) - expect(process.env.VITE_USER_NODE_ENV).toEqual('production') + expect(process.env.VITE_USER_NODE_ENV).toEqual(undefined) }) test('Already exists VITE_USER_NODE_ENV', () => { diff --git a/packages/vite/src/node/__tests__/env/.env.development b/packages/vite/src/node/__tests__/env/.env.development index f961c7335f1352..b18ba0c8928b44 100644 --- a/packages/vite/src/node/__tests__/env/.env.development +++ b/packages/vite/src/node/__tests__/env/.env.development @@ -1,4 +1,3 @@ -NODE_ENV=production VITE_ENV1=ENV1 VITE_ENV2=ENV2 VITE_ENV3=ENV3 diff --git a/packages/vite/src/node/__tests__/plugins/esbuild.spec.ts b/packages/vite/src/node/__tests__/plugins/esbuild.spec.ts index 9a350a46bee5a2..6591ecee8b9876 100644 --- a/packages/vite/src/node/__tests__/plugins/esbuild.spec.ts +++ b/packages/vite/src/node/__tests__/plugins/esbuild.spec.ts @@ -1,6 +1,10 @@ import { describe, expect, test } from 'vitest' import type { ResolvedConfig, UserConfig } from '../../config' -import { resolveEsbuildTranspileOptions } from '../../plugins/esbuild' +import { + ESBuildTransformResult, + resolveEsbuildTranspileOptions, + transformWithEsbuild +} from '../../plugins/esbuild' describe('resolveEsbuildTranspileOptions', () => { test('resolve default', () => { @@ -237,6 +241,16 @@ describe('resolveEsbuildTranspileOptions', () => { }) }) +describe('transformWithEsbuild', () => { + test('not throw on inline sourcemap', async () => { + const result = await transformWithEsbuild(`const foo = 'bar'`, '', { + sourcemap: 'inline' + }) + expect(result?.code).toBeTruthy() + expect(result?.map).toBeTruthy() + }) +}) + /** * Helper for `resolveEsbuildTranspileOptions` to created resolved config with types. * Note: The function only uses `build.target`, `build.minify` and `esbuild` options. diff --git a/packages/vite/src/node/__tests__/plugins/importGlob/__snapshots__/fixture.test.ts.snap b/packages/vite/src/node/__tests__/plugins/importGlob/__snapshots__/fixture.test.ts.snap index e638850c4ce0b0..e6584c954a78f6 100644 --- a/packages/vite/src/node/__tests__/plugins/importGlob/__snapshots__/fixture.test.ts.snap +++ b/packages/vite/src/node/__tests__/plugins/importGlob/__snapshots__/fixture.test.ts.snap @@ -3,38 +3,100 @@ exports[`fixture > transform 1`] = ` "import * as __vite_glob_1_0 from \\"./modules/a.ts\\";import * as __vite_glob_1_1 from \\"./modules/b.ts\\";import * as __vite_glob_1_2 from \\"./modules/index.ts\\";import { name as __vite_glob_3_0 } from \\"./modules/a.ts\\";import { name as __vite_glob_3_1 } from \\"./modules/b.ts\\";import { name as __vite_glob_3_2 } from \\"./modules/index.ts\\";import { default as __vite_glob_5_0 } from \\"./modules/a.ts?raw\\";import { default as __vite_glob_5_1 } from \\"./modules/b.ts?raw\\";import \\"types/importMeta\\"; export const basic = /* #__PURE__ */ Object.assign({\\"./modules/a.ts\\": () => import(\\"./modules/a.ts\\"),\\"./modules/b.ts\\": () => import(\\"./modules/b.ts\\"),\\"./modules/index.ts\\": () => import(\\"./modules/index.ts\\")}); -export const basicEager = /* #__PURE__ */ Object.assign({\\"./modules/a.ts\\": __vite_glob_1_0,\\"./modules/b.ts\\": __vite_glob_1_1,\\"./modules/index.ts\\": __vite_glob_1_2}); +export const basicEager = /* #__PURE__ */ Object.assign({\\"./modules/a.ts\\": __vite_glob_1_0,\\"./modules/b.ts\\": __vite_glob_1_1,\\"./modules/index.ts\\": __vite_glob_1_2 + +}); export const ignore = /* #__PURE__ */ Object.assign({\\"./modules/a.ts\\": () => import(\\"./modules/a.ts\\"),\\"./modules/b.ts\\": () => import(\\"./modules/b.ts\\")}); -export const namedEager = /* #__PURE__ */ Object.assign({\\"./modules/a.ts\\": __vite_glob_3_0,\\"./modules/b.ts\\": __vite_glob_3_1,\\"./modules/index.ts\\": __vite_glob_3_2}); -export const namedDefault = /* #__PURE__ */ Object.assign({\\"./modules/a.ts\\": () => import(\\"./modules/a.ts\\").then(m => m[\\"default\\"]),\\"./modules/b.ts\\": () => import(\\"./modules/b.ts\\").then(m => m[\\"default\\"]),\\"./modules/index.ts\\": () => import(\\"./modules/index.ts\\").then(m => m[\\"default\\"])}); -export const eagerAs = /* #__PURE__ */ Object.assign({\\"./modules/a.ts\\": __vite_glob_5_0,\\"./modules/b.ts\\": __vite_glob_5_1}); -export const rawImportModule = /* #__PURE__ */ Object.assign({\\"./modules/a.ts\\": () => import(\\"./modules/a.ts?raw\\"),\\"./modules/b.ts\\": () => import(\\"./modules/b.ts?raw\\")}); -export const excludeSelf = /* #__PURE__ */ Object.assign({\\"./sibling.ts\\": () => import(\\"./sibling.ts\\")}); +export const namedEager = /* #__PURE__ */ Object.assign({\\"./modules/a.ts\\": __vite_glob_3_0,\\"./modules/b.ts\\": __vite_glob_3_1,\\"./modules/index.ts\\": __vite_glob_3_2 + + +}); +export const namedDefault = /* #__PURE__ */ Object.assign({\\"./modules/a.ts\\": () => import(\\"./modules/a.ts\\").then(m => m[\\"default\\"]),\\"./modules/b.ts\\": () => import(\\"./modules/b.ts\\").then(m => m[\\"default\\"]),\\"./modules/index.ts\\": () => import(\\"./modules/index.ts\\").then(m => m[\\"default\\"]) + +}); +export const eagerAs = /* #__PURE__ */ Object.assign({\\"./modules/a.ts\\": __vite_glob_5_0,\\"./modules/b.ts\\": __vite_glob_5_1 + + +}); +export const rawImportModule = /* #__PURE__ */ Object.assign({\\"./modules/a.ts\\": () => import(\\"./modules/a.ts?raw\\"),\\"./modules/b.ts\\": () => import(\\"./modules/b.ts?raw\\") + + +}); +export const excludeSelf = /* #__PURE__ */ Object.assign({\\"./sibling.ts\\": () => import(\\"./sibling.ts\\") + +}); export const customQueryString = /* #__PURE__ */ Object.assign({\\"./sibling.ts\\": () => import(\\"./sibling.ts?custom\\")}); -export const customQueryObject = /* #__PURE__ */ Object.assign({\\"./sibling.ts\\": () => import(\\"./sibling.ts?foo=bar&raw=true\\")}); -export const parent = /* #__PURE__ */ Object.assign({}); -export const rootMixedRelative = /* #__PURE__ */ Object.assign({\\"/css.spec.ts\\": () => import(\\"../../css.spec.ts?url\\").then(m => m[\\"default\\"]),\\"/define.spec.ts\\": () => import(\\"../../define.spec.ts?url\\").then(m => m[\\"default\\"]),\\"/esbuild.spec.ts\\": () => import(\\"../../esbuild.spec.ts?url\\").then(m => m[\\"default\\"]),\\"/import.spec.ts\\": () => import(\\"../../import.spec.ts?url\\").then(m => m[\\"default\\"]),\\"/importGlob/fixture-b/a.ts\\": () => import(\\"../fixture-b/a.ts?url\\").then(m => m[\\"default\\"]),\\"/importGlob/fixture-b/b.ts\\": () => import(\\"../fixture-b/b.ts?url\\").then(m => m[\\"default\\"]),\\"/importGlob/fixture-b/index.ts\\": () => import(\\"../fixture-b/index.ts?url\\").then(m => m[\\"default\\"])}); -export const cleverCwd1 = /* #__PURE__ */ Object.assign({\\"./node_modules/framework/pages/hello.page.js\\": () => import(\\"./node_modules/framework/pages/hello.page.js\\")}); -export const cleverCwd2 = /* #__PURE__ */ Object.assign({\\"./modules/a.ts\\": () => import(\\"./modules/a.ts\\"),\\"./modules/b.ts\\": () => import(\\"./modules/b.ts\\"),\\"../fixture-b/a.ts\\": () => import(\\"../fixture-b/a.ts\\"),\\"../fixture-b/b.ts\\": () => import(\\"../fixture-b/b.ts\\")}); +export const customQueryObject = /* #__PURE__ */ Object.assign({\\"./sibling.ts\\": () => import(\\"./sibling.ts?foo=bar&raw=true\\") + + + + +}); +export const parent = /* #__PURE__ */ Object.assign({ + +}); +export const rootMixedRelative = /* #__PURE__ */ Object.assign({\\"/css.spec.ts\\": () => import(\\"../../css.spec.ts?url\\").then(m => m[\\"default\\"]),\\"/define.spec.ts\\": () => import(\\"../../define.spec.ts?url\\").then(m => m[\\"default\\"]),\\"/esbuild.spec.ts\\": () => import(\\"../../esbuild.spec.ts?url\\").then(m => m[\\"default\\"]),\\"/import.spec.ts\\": () => import(\\"../../import.spec.ts?url\\").then(m => m[\\"default\\"]),\\"/importGlob/fixture-b/a.ts\\": () => import(\\"../fixture-b/a.ts?url\\").then(m => m[\\"default\\"]),\\"/importGlob/fixture-b/b.ts\\": () => import(\\"../fixture-b/b.ts?url\\").then(m => m[\\"default\\"]),\\"/importGlob/fixture-b/index.ts\\": () => import(\\"../fixture-b/index.ts?url\\").then(m => m[\\"default\\"]) + + +}); +export const cleverCwd1 = /* #__PURE__ */ Object.assign({\\"./node_modules/framework/pages/hello.page.js\\": () => import(\\"./node_modules/framework/pages/hello.page.js\\") + +}); +export const cleverCwd2 = /* #__PURE__ */ Object.assign({\\"./modules/a.ts\\": () => import(\\"./modules/a.ts\\"),\\"./modules/b.ts\\": () => import(\\"./modules/b.ts\\"),\\"../fixture-b/a.ts\\": () => import(\\"../fixture-b/a.ts\\"),\\"../fixture-b/b.ts\\": () => import(\\"../fixture-b/b.ts\\") + + + +}); " `; exports[`fixture > transform with restoreQueryExtension 1`] = ` "import * as __vite_glob_1_0 from \\"./modules/a.ts\\";import * as __vite_glob_1_1 from \\"./modules/b.ts\\";import * as __vite_glob_1_2 from \\"./modules/index.ts\\";import { name as __vite_glob_3_0 } from \\"./modules/a.ts\\";import { name as __vite_glob_3_1 } from \\"./modules/b.ts\\";import { name as __vite_glob_3_2 } from \\"./modules/index.ts\\";import { default as __vite_glob_5_0 } from \\"./modules/a.ts?raw\\";import { default as __vite_glob_5_1 } from \\"./modules/b.ts?raw\\";import \\"types/importMeta\\"; export const basic = /* #__PURE__ */ Object.assign({\\"./modules/a.ts\\": () => import(\\"./modules/a.ts\\"),\\"./modules/b.ts\\": () => import(\\"./modules/b.ts\\"),\\"./modules/index.ts\\": () => import(\\"./modules/index.ts\\")}); -export const basicEager = /* #__PURE__ */ Object.assign({\\"./modules/a.ts\\": __vite_glob_1_0,\\"./modules/b.ts\\": __vite_glob_1_1,\\"./modules/index.ts\\": __vite_glob_1_2}); +export const basicEager = /* #__PURE__ */ Object.assign({\\"./modules/a.ts\\": __vite_glob_1_0,\\"./modules/b.ts\\": __vite_glob_1_1,\\"./modules/index.ts\\": __vite_glob_1_2 + +}); export const ignore = /* #__PURE__ */ Object.assign({\\"./modules/a.ts\\": () => import(\\"./modules/a.ts\\"),\\"./modules/b.ts\\": () => import(\\"./modules/b.ts\\")}); -export const namedEager = /* #__PURE__ */ Object.assign({\\"./modules/a.ts\\": __vite_glob_3_0,\\"./modules/b.ts\\": __vite_glob_3_1,\\"./modules/index.ts\\": __vite_glob_3_2}); -export const namedDefault = /* #__PURE__ */ Object.assign({\\"./modules/a.ts\\": () => import(\\"./modules/a.ts\\").then(m => m[\\"default\\"]),\\"./modules/b.ts\\": () => import(\\"./modules/b.ts\\").then(m => m[\\"default\\"]),\\"./modules/index.ts\\": () => import(\\"./modules/index.ts\\").then(m => m[\\"default\\"])}); -export const eagerAs = /* #__PURE__ */ Object.assign({\\"./modules/a.ts\\": __vite_glob_5_0,\\"./modules/b.ts\\": __vite_glob_5_1}); -export const rawImportModule = /* #__PURE__ */ Object.assign({\\"./modules/a.ts\\": () => import(\\"./modules/a.ts?raw\\"),\\"./modules/b.ts\\": () => import(\\"./modules/b.ts?raw\\")}); -export const excludeSelf = /* #__PURE__ */ Object.assign({\\"./sibling.ts\\": () => import(\\"./sibling.ts\\")}); +export const namedEager = /* #__PURE__ */ Object.assign({\\"./modules/a.ts\\": __vite_glob_3_0,\\"./modules/b.ts\\": __vite_glob_3_1,\\"./modules/index.ts\\": __vite_glob_3_2 + + +}); +export const namedDefault = /* #__PURE__ */ Object.assign({\\"./modules/a.ts\\": () => import(\\"./modules/a.ts\\").then(m => m[\\"default\\"]),\\"./modules/b.ts\\": () => import(\\"./modules/b.ts\\").then(m => m[\\"default\\"]),\\"./modules/index.ts\\": () => import(\\"./modules/index.ts\\").then(m => m[\\"default\\"]) + +}); +export const eagerAs = /* #__PURE__ */ Object.assign({\\"./modules/a.ts\\": __vite_glob_5_0,\\"./modules/b.ts\\": __vite_glob_5_1 + + +}); +export const rawImportModule = /* #__PURE__ */ Object.assign({\\"./modules/a.ts\\": () => import(\\"./modules/a.ts?raw\\"),\\"./modules/b.ts\\": () => import(\\"./modules/b.ts?raw\\") + + +}); +export const excludeSelf = /* #__PURE__ */ Object.assign({\\"./sibling.ts\\": () => import(\\"./sibling.ts\\") + +}); export const customQueryString = /* #__PURE__ */ Object.assign({\\"./sibling.ts\\": () => import(\\"./sibling.ts?custom&lang.ts\\")}); -export const customQueryObject = /* #__PURE__ */ Object.assign({\\"./sibling.ts\\": () => import(\\"./sibling.ts?foo=bar&raw=true&lang.ts\\")}); -export const parent = /* #__PURE__ */ Object.assign({}); -export const rootMixedRelative = /* #__PURE__ */ Object.assign({\\"/css.spec.ts\\": () => import(\\"../../css.spec.ts?url&lang.ts\\").then(m => m[\\"default\\"]),\\"/define.spec.ts\\": () => import(\\"../../define.spec.ts?url&lang.ts\\").then(m => m[\\"default\\"]),\\"/esbuild.spec.ts\\": () => import(\\"../../esbuild.spec.ts?url&lang.ts\\").then(m => m[\\"default\\"]),\\"/import.spec.ts\\": () => import(\\"../../import.spec.ts?url&lang.ts\\").then(m => m[\\"default\\"]),\\"/importGlob/fixture-b/a.ts\\": () => import(\\"../fixture-b/a.ts?url&lang.ts\\").then(m => m[\\"default\\"]),\\"/importGlob/fixture-b/b.ts\\": () => import(\\"../fixture-b/b.ts?url&lang.ts\\").then(m => m[\\"default\\"]),\\"/importGlob/fixture-b/index.ts\\": () => import(\\"../fixture-b/index.ts?url&lang.ts\\").then(m => m[\\"default\\"])}); -export const cleverCwd1 = /* #__PURE__ */ Object.assign({\\"./node_modules/framework/pages/hello.page.js\\": () => import(\\"./node_modules/framework/pages/hello.page.js\\")}); -export const cleverCwd2 = /* #__PURE__ */ Object.assign({\\"./modules/a.ts\\": () => import(\\"./modules/a.ts\\"),\\"./modules/b.ts\\": () => import(\\"./modules/b.ts\\"),\\"../fixture-b/a.ts\\": () => import(\\"../fixture-b/a.ts\\"),\\"../fixture-b/b.ts\\": () => import(\\"../fixture-b/b.ts\\")}); +export const customQueryObject = /* #__PURE__ */ Object.assign({\\"./sibling.ts\\": () => import(\\"./sibling.ts?foo=bar&raw=true&lang.ts\\") + + + + +}); +export const parent = /* #__PURE__ */ Object.assign({ + +}); +export const rootMixedRelative = /* #__PURE__ */ Object.assign({\\"/css.spec.ts\\": () => import(\\"../../css.spec.ts?url&lang.ts\\").then(m => m[\\"default\\"]),\\"/define.spec.ts\\": () => import(\\"../../define.spec.ts?url&lang.ts\\").then(m => m[\\"default\\"]),\\"/esbuild.spec.ts\\": () => import(\\"../../esbuild.spec.ts?url&lang.ts\\").then(m => m[\\"default\\"]),\\"/import.spec.ts\\": () => import(\\"../../import.spec.ts?url&lang.ts\\").then(m => m[\\"default\\"]),\\"/importGlob/fixture-b/a.ts\\": () => import(\\"../fixture-b/a.ts?url&lang.ts\\").then(m => m[\\"default\\"]),\\"/importGlob/fixture-b/b.ts\\": () => import(\\"../fixture-b/b.ts?url&lang.ts\\").then(m => m[\\"default\\"]),\\"/importGlob/fixture-b/index.ts\\": () => import(\\"../fixture-b/index.ts?url&lang.ts\\").then(m => m[\\"default\\"]) + + +}); +export const cleverCwd1 = /* #__PURE__ */ Object.assign({\\"./node_modules/framework/pages/hello.page.js\\": () => import(\\"./node_modules/framework/pages/hello.page.js\\") + +}); +export const cleverCwd2 = /* #__PURE__ */ Object.assign({\\"./modules/a.ts\\": () => import(\\"./modules/a.ts\\"),\\"./modules/b.ts\\": () => import(\\"./modules/b.ts\\"),\\"../fixture-b/a.ts\\": () => import(\\"../fixture-b/a.ts\\"),\\"../fixture-b/b.ts\\": () => import(\\"../fixture-b/b.ts\\") + + + +}); " `; diff --git a/packages/vite/src/node/__tests__/plugins/importGlob/fixture-c/a.css b/packages/vite/src/node/__tests__/plugins/importGlob/fixture-c/a.css new file mode 100644 index 00000000000000..5451a331f9cea4 --- /dev/null +++ b/packages/vite/src/node/__tests__/plugins/importGlob/fixture-c/a.css @@ -0,0 +1,3 @@ +.a { + color: red; +} diff --git a/packages/vite/src/node/__tests__/plugins/importGlob/fixture-c/b.css b/packages/vite/src/node/__tests__/plugins/importGlob/fixture-c/b.css new file mode 100644 index 00000000000000..b28d1ab0817412 --- /dev/null +++ b/packages/vite/src/node/__tests__/plugins/importGlob/fixture-c/b.css @@ -0,0 +1,3 @@ +.b { + color: red; +} diff --git a/packages/vite/src/node/__tests__/plugins/importGlob/fixture-c/c.module.css b/packages/vite/src/node/__tests__/plugins/importGlob/fixture-c/c.module.css new file mode 100644 index 00000000000000..b28d1ab0817412 --- /dev/null +++ b/packages/vite/src/node/__tests__/plugins/importGlob/fixture-c/c.module.css @@ -0,0 +1,3 @@ +.b { + color: red; +} diff --git a/packages/vite/src/node/__tests__/plugins/importGlob/fixture.test.ts b/packages/vite/src/node/__tests__/plugins/importGlob/fixture.test.ts index 580423b8bf53e1..4aef33bfc4fe6b 100644 --- a/packages/vite/src/node/__tests__/plugins/importGlob/fixture.test.ts +++ b/packages/vite/src/node/__tests__/plugins/importGlob/fixture.test.ts @@ -4,6 +4,7 @@ import { fileURLToPath } from 'node:url' import { describe, expect, it } from 'vitest' import { transformGlobImport } from '../../../plugins/importMetaGlob' import { transformWithEsbuild } from '../../../plugins/esbuild' +import type { Logger } from '../../../logger' import { createLogger } from '../../../logger' const __dirname = resolve(fileURLToPath(import.meta.url), '..') @@ -26,6 +27,32 @@ describe('fixture', async () => { ).toMatchSnapshot() }) + it('preserve line count', async () => { + const getTransformedLineCount = async (code: string) => + ( + await transformGlobImport( + code, + 'virtual:module', + root, + resolveId, + logger + ) + )?.s + .toString() + .split('\n').length + + expect(await getTransformedLineCount("import.meta.glob('./*.js')")).toBe(1) + expect( + await getTransformedLineCount( + ` + import.meta.glob( + './*.js' + ) + `.trim() + ) + ).toBe(3) + }) + it('virtual modules', async () => { const root = resolve(__dirname, './fixture-a') const code = [ @@ -72,4 +99,43 @@ describe('fixture', async () => { )?.s.toString() ).toMatchSnapshot() }) + + it('warn when glob css without ?inline', async () => { + const logs: string[] = [] + const logger = { + warn(msg: string) { + logs.push(msg) + } + } as Logger + + await transformGlobImport( + "import.meta.glob('./fixture-c/*.css', { query: '?inline' })", + fileURLToPath(import.meta.url), + root, + resolveId, + logger + ) + expect(logs).toHaveLength(0) + + await transformGlobImport( + "import.meta.glob('./fixture-c/*.module.css')", + fileURLToPath(import.meta.url), + root, + resolveId, + logger + ) + expect(logs).toHaveLength(0) + + await transformGlobImport( + "import.meta.glob('./fixture-c/*.css')", + fileURLToPath(import.meta.url), + root, + resolveId, + logger + ) + expect(logs).toHaveLength(1) + expect(logs[0]).to.include( + 'Globbing CSS files without the ?inline query is deprecated' + ) + }) }) diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index 2a269090f88c1f..780c43bfadca2c 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -378,10 +378,11 @@ export async function resolveConfig( let config = inlineConfig let configFileDependencies: string[] = [] let mode = inlineConfig.mode || defaultMode + const isNodeEnvSet = !!process.env.NODE_ENV // some dependencies e.g. @vue/compiler-* relies on NODE_ENV for getting // production-specific behavior, so set it early on - if (!process.env.NODE_ENV) { + if (!isNodeEnvSet) { process.env.NODE_ENV = defaultNodeEnv } @@ -494,16 +495,24 @@ export async function resolveConfig( loadEnv(mode, envDir, resolveEnvPrefix(config)) // Note it is possible for user to have a custom mode, e.g. `staging` where - // production-like behavior is expected. This is indicated by NODE_ENV=production + // development-like behavior is expected. This is indicated by NODE_ENV=development // loaded from `.staging.env` and set by us as VITE_USER_NODE_ENV - const isProduction = - (process.env.NODE_ENV || process.env.VITE_USER_NODE_ENV || mode) === - 'production' - if (isProduction) { - // in case default mode was not production and is overwritten - process.env.NODE_ENV = 'production' + const userNodeEnv = process.env.VITE_USER_NODE_ENV + if (!isNodeEnvSet && userNodeEnv) { + if (userNodeEnv === 'development') { + process.env.NODE_ENV = 'development' + } else { + // NODE_ENV=production is not supported as it could break HMR in dev for frameworks like Vue + logger.warn( + `NODE_ENV=${userNodeEnv} is not supported in the .env file. ` + + `Only NODE_ENV=development is supported to create a development build of your project. ` + + `If you need to set process.env.NODE_ENV, you can set it in the Vite config instead.` + ) + } } + const isProduction = process.env.NODE_ENV === 'production' + // resolve public base url const isBuild = command === 'build' const relativeBaseShortcut = config.base === '' || config.base === './' diff --git a/packages/vite/src/node/constants.ts b/packages/vite/src/node/constants.ts index 72504abc1cdabf..eb2d2ae7e13d0b 100644 --- a/packages/vite/src/node/constants.ts +++ b/packages/vite/src/node/constants.ts @@ -22,7 +22,7 @@ export const ESBUILD_MODULES_TARGET = [ 'edge88', 'firefox78', 'chrome87', - 'safari13' // transpile nullish coalescing + 'safari14' ] export const DEFAULT_EXTENSIONS = [ diff --git a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts index 2def9336e8a3d6..ef6bdc57ffa1fb 100644 --- a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts +++ b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts @@ -263,7 +263,10 @@ module.exports = Object.create(new Proxy({}, { // esbuild doesn't transpile `require('foo')` into `import` statements if 'foo' is externalized // https://github.com/evanw/esbuild/issues/566#issuecomment-735551834 -export function esbuildCjsExternalPlugin(externals: string[]): Plugin { +export function esbuildCjsExternalPlugin( + externals: string[], + platform: 'node' | 'browser' +): Plugin { return { name: 'cjs-external', setup(build) { @@ -279,7 +282,8 @@ export function esbuildCjsExternalPlugin(externals: string[]): Plugin { }) build.onResolve({ filter }, (args) => { - if (args.kind === 'require-call') { + // preserve `require` for node because it's more accurate than converting it to import + if (args.kind === 'require-call' && platform !== 'node') { return { path: args.path, namespace: cjsExternalFacadeNamespace diff --git a/packages/vite/src/node/optimizer/index.ts b/packages/vite/src/node/optimizer/index.ts index 7f904bc17bccc6..2e99f42bfcd406 100644 --- a/packages/vite/src/node/optimizer/index.ts +++ b/packages/vite/src/node/optimizer/index.ts @@ -578,7 +578,7 @@ export async function runOptimizeDeps( const plugins = [...pluginsFromConfig] if (external.length) { - plugins.push(esbuildCjsExternalPlugin(external)) + plugins.push(esbuildCjsExternalPlugin(external, platform)) } plugins.push(esbuildDepPlugin(flatIdDeps, external, config, ssr)) @@ -707,7 +707,8 @@ export async function addManuallyIncludedOptimizeDeps( const resolve = config.createResolver({ asSrc: false, scan: true, - ssrOptimizeCheck: ssr + ssrOptimizeCheck: ssr, + ssrConfig: config.ssr }) for (const id of [...optimizeDepsInclude, ...extra]) { // normalize 'foo >bar` as 'foo > bar' to prevent same id being added diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 3af635a5111c71..0277a6594a3445 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -1499,6 +1499,42 @@ function loadPreprocessor( } } +// in unix, scss might append `location.href` in environments that shim `location` +// see https://github.com/sass/dart-sass/issues/710 +function cleanScssBugUrl(url: string) { + if ( + // check bug via `window` and `location` global + typeof window !== 'undefined' && + typeof location !== 'undefined' + ) { + const prefix = location.href.replace(/\/$/, '') + return url.replace(prefix, '') + } else { + return url + } +} + +function fixScssBugImportValue( + data: Sass.ImporterReturnType +): Sass.ImporterReturnType { + // the scss bug doesn't load files properly so we have to load it ourselves + // to prevent internal error when it loads itself + if ( + // check bug via `window` and `location` global + typeof window !== 'undefined' && + typeof location !== 'undefined' && + data && + // @ts-expect-error + data.file && + // @ts-expect-error + data.contents == null + ) { + // @ts-expect-error + data.contents = fs.readFileSync(data.file, 'utf-8') + } + return data +} + // .scss/.sass processor const scss: SassStylePreprocessor = async ( source, @@ -1507,11 +1543,14 @@ const scss: SassStylePreprocessor = async ( resolvers ) => { const render = loadPreprocessor(PreprocessLang.sass, root).render + // NOTE: `sass` always runs it's own importer first, and only falls back to + // the `importer` option when it can't resolve a path const internalImporter: Sass.Importer = (url, importer, done) => { + importer = cleanScssBugUrl(importer) resolvers.sass(url, importer).then((resolved) => { if (resolved) { rebaseUrls(resolved, options.filename, options.alias, '$') - .then((data) => done?.(data)) + .then((data) => done?.(fixScssBugImportValue(data))) .catch((data) => done?.(data)) } else { done?.(null) @@ -1556,7 +1595,7 @@ const scss: SassStylePreprocessor = async ( } }) }) - const deps = result.stats.includedFiles + const deps = result.stats.includedFiles.map((f) => cleanScssBugUrl(f)) const map: ExistingRawSourceMap | undefined = result.map ? JSON.parse(result.map.toString()) : undefined diff --git a/packages/vite/src/node/plugins/define.ts b/packages/vite/src/node/plugins/define.ts index 47394b7f14e635..8743b6d8b4e7c5 100644 --- a/packages/vite/src/node/plugins/define.ts +++ b/packages/vite/src/node/plugins/define.ts @@ -85,7 +85,7 @@ export function definePlugin(config: ResolvedConfig): Plugin { .join('|') + // Mustn't be followed by a char that can be part of an identifier // or an assignment (but allow equality operators) - ')(?![\\p{L}\\p{N}_$]|\\s*?=[^=])', + ')(?:(?<=\\.)|(?![\\p{L}\\p{N}_$]|\\s*?=[^=]))', 'gu' ) : null diff --git a/packages/vite/src/node/plugins/esbuild.ts b/packages/vite/src/node/plugins/esbuild.ts index 129ec41abca7e0..d7844a0f1b3f63 100644 --- a/packages/vite/src/node/plugins/esbuild.ts +++ b/packages/vite/src/node/plugins/esbuild.ts @@ -145,9 +145,10 @@ export async function transformWithEsbuild( inMap as RawSourceMap ]) as SourceMap } else { - map = resolvedOptions.sourcemap - ? JSON.parse(result.map) - : { mappings: '' } + map = + resolvedOptions.sourcemap && resolvedOptions.sourcemap !== 'inline' + ? JSON.parse(result.map) + : { mappings: '' } } if (Array.isArray(map.sources)) { map.sources = map.sources.map((it) => toUpperCaseDriveLetter(it)) diff --git a/packages/vite/src/node/plugins/importAnalysis.ts b/packages/vite/src/node/plugins/importAnalysis.ts index 1d08cceb00f3fb..e5e0f37a6b9737 100644 --- a/packages/vite/src/node/plugins/importAnalysis.ts +++ b/packages/vite/src/node/plugins/importAnalysis.ts @@ -439,9 +439,9 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { if ( !isDynamicImport && specifier && + !specifier.includes('?') && // ignore custom queries isCSSRequest(specifier) && - !isModuleCSSRequest(specifier) && - !specifier.includes('?') // ignore custom queries + !isModuleCSSRequest(specifier) ) { const sourceExp = source.slice(expStart, start) if ( diff --git a/packages/vite/src/node/plugins/importMetaGlob.ts b/packages/vite/src/node/plugins/importMetaGlob.ts index 17419ed5acee35..3f986f87ca49d7 100644 --- a/packages/vite/src/node/plugins/importMetaGlob.ts +++ b/packages/vite/src/node/plugins/importMetaGlob.ts @@ -15,6 +15,7 @@ import type { } from 'estree' import { parseExpressionAt } from 'acorn' import type { RollupError } from 'rollup' +import { findNodeAt } from 'acorn-walk' import MagicString from 'magic-string' import fg from 'fast-glob' import { stringifyQuery } from 'ufo' @@ -31,7 +32,7 @@ import { transformStableResult } from '../utils' import type { Logger } from '../logger' -import { isCSSRequest } from './css' +import { isCSSRequest, isModuleCSSRequest } from './css' const { isMatch, scan } = micromatch @@ -238,15 +239,9 @@ export async function parseImportGlob( } } - if (ast.type === 'SequenceExpression') - ast = ast.expressions[0] as CallExpression - - // immediate property access, call expression is nested - // import.meta.glob(...)['prop'] - if (ast.type === 'MemberExpression') ast = ast.object as CallExpression - - if (ast.type !== 'CallExpression') - throw err(`Expect CallExpression, got ${ast.type}`) + const found = findNodeAt(ast as any, start, undefined, 'CallExpression') + if (!found) throw err(`Expect CallExpression, got ${ast.type}`) + ast = found.node as unknown as CallExpression if (ast.arguments.length < 1 || ast.arguments.length > 2) throw err(`Expected 1-2 arguments, but got ${ast.arguments.length}`) @@ -397,8 +392,10 @@ export async function transformGlobImport( if (query && !query.startsWith('?')) query = `?${query}` if ( - !query.match(/(?:\?|&)inline\b/) && - files.some((file) => isCSSRequest(file)) + !query && // ignore custom queries + files.some( + (file) => isCSSRequest(file) && !isModuleCSSRequest(file) + ) ) { logger.warn( `\n` + @@ -477,9 +474,15 @@ export async function transformGlobImport( files.forEach((i) => matchedFiles.add(i)) + const originalLineBreakCount = + code.slice(start, end).match(/\n/g)?.length ?? 0 + const lineBreaks = + originalLineBreakCount > 0 + ? '\n'.repeat(originalLineBreakCount) + : '' const replacement = `/* #__PURE__ */ Object.assign({${objectProps.join( ',' - )}})` + )}${lineBreaks}})` s.overwrite(start, end, replacement) return staticImports diff --git a/packages/vite/src/node/plugins/index.ts b/packages/vite/src/node/plugins/index.ts index 057f4f1faa5271..17fccd864d0229 100644 --- a/packages/vite/src/node/plugins/index.ts +++ b/packages/vite/src/node/plugins/index.ts @@ -18,7 +18,6 @@ import { modulePreloadPolyfillPlugin } from './modulePreloadPolyfill' import { webWorkerPlugin } from './worker' import { preAliasPlugin } from './preAlias' import { definePlugin } from './define' -import { ssrRequireHookPlugin } from './ssrRequireHook' import { workerImportMetaUrlPlugin } from './workerImportMetaUrl' import { assetImportMetaUrlPlugin } from './assetImportMetaUrl' import { ensureWatchPlugin } from './ensureWatch' @@ -88,7 +87,6 @@ export async function resolvePlugins( wasmFallbackPlugin(), definePlugin(config), cssPostPlugin(config), - isBuild && config.build.ssr ? ssrRequireHookPlugin(config) : null, isBuild && buildHtmlPlugin(config), workerImportMetaUrlPlugin(config), assetImportMetaUrlPlugin(config), diff --git a/packages/vite/src/node/plugins/resolve.ts b/packages/vite/src/node/plugins/resolve.ts index 1c1c80f573e84d..ae612148769960 100644 --- a/packages/vite/src/node/plugins/resolve.ts +++ b/packages/vite/src/node/plugins/resolve.ts @@ -105,8 +105,6 @@ export interface InternalResolveOptions extends Required { // Resolve using esbuild deps optimization getDepsOptimizer?: (ssr: boolean) => DepsOptimizer | undefined shouldExternalize?: (id: string) => boolean | undefined - // Check this resolve is called from `hookNodeResolve` in SSR - isHookNodeResolve?: boolean } export function resolvePlugin(resolveOptions: InternalResolveOptions): Plugin { @@ -689,7 +687,6 @@ export function tryNodeResolve( // if import can't be found, check if it's an optional peer dep. // if so, we can resolve to a special id that errors only when imported. if ( - !options.isHookNodeResolve && basedir !== root && // root has no peer dep !isBuiltin(nestedPath) && !nestedPath.includes('\0') && @@ -803,11 +800,11 @@ export function tryNodeResolve( : OPTIMIZABLE_ENTRY_RE.test(resolved) let exclude = depsOptimizer?.options.exclude - let include = depsOptimizer?.options.exclude + let include = depsOptimizer?.options.include if (options.ssrOptimizeCheck) { // we don't have the depsOptimizer exclude = options.ssrConfig?.optimizeDeps?.exclude - include = options.ssrConfig?.optimizeDeps?.exclude + include = options.ssrConfig?.optimizeDeps?.include } const skipOptimization = @@ -816,7 +813,10 @@ export function tryNodeResolve( exclude?.includes(pkgId) || exclude?.includes(nestedPath) || SPECIAL_QUERY_RE.test(resolved) || - (!isBuild && ssr) || + // During dev SSR, we don't have a way to reload the module graph if + // a non-optimized dep is found. So we need to skip optimization here. + // The only optimized deps are the ones explicitly listed in the config. + (!options.ssrOptimizeCheck && !isBuild && ssr) || // Only optimize non-external CJS deps during SSR by default (ssr && !isCJS && diff --git a/packages/vite/src/node/plugins/ssrRequireHook.ts b/packages/vite/src/node/plugins/ssrRequireHook.ts deleted file mode 100644 index e02f9b48e6b6c7..00000000000000 --- a/packages/vite/src/node/plugins/ssrRequireHook.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { createRequire } from 'node:module' -import MagicString from 'magic-string' -import type { ResolvedConfig } from '..' -import type { Plugin } from '../plugin' -import { arraify } from '../utils' - -/** - * This plugin hooks into Node's module resolution algorithm at runtime, - * so that SSR builds can benefit from `resolve.dedupe` like they do - * in development. - */ -export function ssrRequireHookPlugin(config: ResolvedConfig): Plugin | null { - if ( - config.command !== 'build' || - !config.build.ssr || - !config.resolve.dedupe.length || - config.ssr?.noExternal === true || - config.ssr?.format !== 'cjs' || - isBuildOutputEsm(config) - ) { - return null - } - return { - name: 'vite:ssr-require-hook', - transform(code, id) { - const moduleInfo = this.getModuleInfo(id) - if (moduleInfo?.isEntry) { - const s = new MagicString(code) - s.prepend( - `;(${dedupeRequire.toString()})(${JSON.stringify( - config.resolve.dedupe - )});\n` - ) - return { - code: s.toString(), - map: s.generateMap({ - source: id, - hires: true - }) - } - } - } - } -} - -type NodeResolveFilename = ( - request: string, - parent: NodeModule, - isMain: boolean, - options?: Record -) => string - -/** Respect the `resolve.dedupe` option in production SSR. */ -function dedupeRequire(dedupe: string[]) { - // eslint-disable-next-line no-restricted-globals - const Module = require('node:module') as { - _resolveFilename: NodeResolveFilename - } - const resolveFilename = Module._resolveFilename - Module._resolveFilename = function (request, parent, isMain, options) { - if (request[0] !== '.' && request[0] !== '/') { - const parts = request.split('/') - const pkgName = parts[0][0] === '@' ? parts[0] + '/' + parts[1] : parts[0] - if (dedupe.includes(pkgName)) { - // Use this module as the parent. - parent = module - } - } - return resolveFilename!(request, parent, isMain, options) - } -} - -const _require = createRequire(import.meta.url) -export function hookNodeResolve( - getResolver: (resolveFilename: NodeResolveFilename) => NodeResolveFilename -): () => void { - const Module = _require('module') as { _resolveFilename: NodeResolveFilename } - const prevResolver = Module._resolveFilename - Module._resolveFilename = getResolver(prevResolver) - return () => { - Module._resolveFilename = prevResolver - } -} - -function isBuildOutputEsm(config: ResolvedConfig) { - const outputs = arraify(config.build.rollupOptions?.output) - return outputs.some( - (output) => output?.format === 'es' || output?.format === 'esm' - ) -} diff --git a/packages/vite/src/node/ssr/ssrModuleLoader.ts b/packages/vite/src/node/ssr/ssrModuleLoader.ts index eaae5e73015b46..84b05ac7841973 100644 --- a/packages/vite/src/node/ssr/ssrModuleLoader.ts +++ b/packages/vite/src/node/ssr/ssrModuleLoader.ts @@ -2,7 +2,6 @@ import path from 'node:path' import { pathToFileURL } from 'node:url' import type { ViteDevServer } from '../server' import { - bareImportRE, dynamicImport, isBuiltin, unwrapId, @@ -11,7 +10,6 @@ import { import { transformRequest } from '../server/transformRequest' import type { InternalResolveOptions } from '../plugins/resolve' import { tryNodeResolve } from '../plugins/resolve' -import { hookNodeResolve } from '../plugins/ssrRequireHook' import { ssrDynamicImportKey, ssrExportAllKey, @@ -114,9 +112,6 @@ async function instantiateModule( root } = server.config - // The `extensions` and `mainFields` options are used to ensure that - // CommonJS modules are preferred. We want to avoid ESM->ESM imports - // whenever possible, because `hookNodeResolve` can't intercept them. const resolveOptions: InternalResolveOptions = { mainFields: ['main'], browserField: true, @@ -124,11 +119,9 @@ async function instantiateModule( extensions: ['.js', '.cjs', '.json'], dedupe, preserveSymlinks, - isBuild: true, + isBuild: false, isProduction, - isRequire: true, - root, - isHookNodeResolve: true + root } // Since dynamic imports can happen in parallel, we need to @@ -227,72 +220,17 @@ async function instantiateModule( return Object.freeze(ssrModule) } -// `nodeImport` may run in parallel on multiple `ssrLoadModule` calls. -// We keep track of the current importing count so that the first import -// would `hookNodeResolve`, and the last import would `unhookNodeResolve`. -let importingCount = 0 -let unhookNodeResolve: ReturnType | undefined - // In node@12+ we can use dynamic import to load CJS and ESM async function nodeImport( id: string, importer: string, resolveOptions: InternalResolveOptions ) { - // Node's module resolution is hi-jacked so Vite can ensure the - // configured `resolve.dedupe` and `mode` options are respected. - const viteResolve = ( - id: string, - importer: string, - options = resolveOptions - ) => { - const resolved = tryNodeResolve(id, importer, options, false) - if (!resolved) { - const err: any = new Error( - `Cannot find module '${id}' imported from '${importer}'` - ) - err.code = 'ERR_MODULE_NOT_FOUND' - throw err - } - return resolved.id - } - - if (importingCount === 0) { - // When an ESM module imports an ESM dependency, this hook is *not* used. - unhookNodeResolve = hookNodeResolve( - (nodeResolve) => (id, parent, isMain, options) => { - // Use the Vite resolver only for bare imports while skipping - // any absolute paths, built-in modules and binary modules. - if ( - !bareImportRE.test(id) || - path.isAbsolute(id) || - isBuiltin(id) || - id.endsWith('.node') - ) { - return nodeResolve(id, parent, isMain, options) - } - if (parent) { - let resolved = viteResolve(id, parent.id) - if (resolved) { - // hookNodeResolve must use platform-specific path.normalize - // to be compatible with dynamicImport (#6080) - resolved = path.normalize(resolved) - } - return resolved - } - // Importing a CJS module from an ESM module. In this case, the import - // specifier is already an absolute path, so this is a no-op. - // Options like `resolve.dedupe` and `mode` are not respected. - return id - } - ) - } - let url: string if (id.startsWith('node:') || isBuiltin(id)) { url = id } else { - url = viteResolve( + const resolved = tryNodeResolve( id, importer, // Non-external modules can import ESM-only modules, but only outside @@ -300,23 +238,26 @@ async function nodeImport( // @ts-expect-error typeof jest === 'undefined' ? { ...resolveOptions, tryEsmOnly: true } - : resolveOptions + : resolveOptions, + false ) + if (!resolved) { + const err: any = new Error( + `Cannot find module '${id}' imported from '${importer}'` + ) + err.code = 'ERR_MODULE_NOT_FOUND' + throw err + } + url = resolved.id if (usingDynamicImport) { url = pathToFileURL(url).toString() } } try { - importingCount++ const mod = await dynamicImport(url) return proxyESM(mod) - } finally { - importingCount-- - if (importingCount === 0) { - unhookNodeResolve?.() - } - } + } catch {} } // rollup-style default import interop for cjs diff --git a/playground/css/vite.config.js b/playground/css/vite.config.js index 40ca1a186e58da..2435f7c0772bcd 100644 --- a/playground/css/vite.config.js +++ b/playground/css/vite.config.js @@ -1,5 +1,10 @@ const path = require('node:path') +// trigger scss bug: https://github.com/sass/dart-sass/issues/710 +// make sure Vite handles safely +globalThis.window = {} +globalThis.location = new URL('http://localhost/') + /** * @type {import('vite').UserConfig} */ diff --git a/playground/define/__tests__/define.spec.ts b/playground/define/__tests__/define.spec.ts index 43787ef0adb112..8c27686bce1703 100644 --- a/playground/define/__tests__/define.spec.ts +++ b/playground/define/__tests__/define.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from 'vitest' import viteConfig from '../vite.config' -import { page } from '~utils' +import { isBuild, page } from '~utils' test('string', async () => { const defines = viteConfig.define @@ -44,4 +44,10 @@ test('string', async () => { expect(await page.textContent('.define-in-dep')).toBe( defines.__STRINGIFIED_OBJ__ ) + expect(await page.textContent('.import-meta-env-undefined')).toBe( + isBuild ? '({}).UNDEFINED' : 'import.meta.env.UNDEFINED' + ) + expect(await page.textContent('.process-env-undefined')).toBe( + isBuild ? '({}).UNDEFINED' : 'process.env.UNDEFINED' + ) }) diff --git a/playground/define/commonjs-dep/index.js b/playground/define/commonjs-dep/index.js index 23e0bf1b32e32f..9be5641e04b844 100644 --- a/playground/define/commonjs-dep/index.js +++ b/playground/define/commonjs-dep/index.js @@ -1 +1,5 @@ -module.exports = { defined: __STRINGIFIED_OBJ__ } +module.exports = { + defined: __STRINGIFIED_OBJ__, + importMetaEnvUndefined: 'import.meta.env.UNDEFINED', + processEnvUndefined: 'process.env.UNDEFINED' +} diff --git a/playground/define/index.html b/playground/define/index.html index c4f4c598aba563..06b2f4f9479d38 100644 --- a/playground/define/index.html +++ b/playground/define/index.html @@ -17,6 +17,10 @@

Define

define variable in html: __EXP__

import json:

define in dep:

+

+ import.meta.env.UNDEFINED: +

+

process.env.UNDEFINED: