diff --git a/docs/config/shared-options.md b/docs/config/shared-options.md index f375d060e9ace3..8d4c36ff67d90d 100644 --- a/docs/config/shared-options.md +++ b/docs/config/shared-options.md @@ -117,7 +117,7 @@ For SSR builds, deduplication does not work for ESM build outputs configured fro ## resolve.conditions - **Type:** `string[]` -- **Default:** `['module', 'browser', 'development|production']` +- **Default:** `['module', 'browser', 'development|production']` (`defaultClientConditions`) Additional allowed conditions when resolving [Conditional Exports](https://nodejs.org/api/packages.html#packages_conditional_exports) from a package. @@ -147,7 +147,7 @@ Export keys ending with "/" is deprecated by Node and may not work well. Please ## resolve.mainFields - **Type:** `string[]` -- **Default:** `['browser', 'module', 'jsnext:main', 'jsnext']` +- **Default:** `['browser', 'module', 'jsnext:main', 'jsnext']` (`defaultClientMainFields`) List of fields in `package.json` to try when resolving a package's entry point. Note this takes lower precedence than conditional exports resolved from the `exports` field: if an entry point is successfully resolved from `exports`, the main field will be ignored. diff --git a/docs/config/ssr-options.md b/docs/config/ssr-options.md index 28a9775b8bbdc2..0d64c4d712b0bc 100644 --- a/docs/config/ssr-options.md +++ b/docs/config/ssr-options.md @@ -34,7 +34,7 @@ Build target for the SSR server. ## ssr.resolve.conditions - **Type:** `string[]` -- **Default:** `['module', 'node', 'development|production']` (`['module', 'browser', 'development|production']` for `ssr.target === 'webworker'`) +- **Default:** `['module', 'node', 'development|production']` (`defaultServerConditions`) (`['module', 'browser', 'development|production']` (`defaultClientConditions`) for `ssr.target === 'webworker'`) - **Related:** [Resolve Conditions](./shared-options.md#resolve-conditions) These conditions are used in the plugin pipeline, and only affect non-externalized dependencies during the SSR build. Use `ssr.resolve.externalConditions` to affect externalized imports. diff --git a/docs/guide/migration.md b/docs/guide/migration.md index de0da8317095ac..fb161a73e30beb 100644 --- a/docs/guide/migration.md +++ b/docs/guide/migration.md @@ -24,10 +24,10 @@ The conditions that are no longer added internally for - `resolve.conditions` are `['module', 'browser', 'development|production']` - `ssr.resolve.conditions` are `['module', 'node', 'development|production']` -The default values for those options are updated to the corresponding values and `ssr.resolve.conditions` no longer uses `resolve.conditions` as the default value. Note that `development|production` is a special variable that is replaced with `production` or `development` depending on the value of `process.env.NODE_ENV`. +The default values for those options are updated to the corresponding values and `ssr.resolve.conditions` no longer uses `resolve.conditions` as the default value. Note that `development|production` is a special variable that is replaced with `production` or `development` depending on the value of `process.env.NODE_ENV`. These default values are exported from `vite` as `defaultClientConditions` and `defaultServerConditions`. If you specified a custom value for `resolve.conditions` or `ssr.resolve.conditions`, you need to update it to include the new conditions. -For example, if you previously specified `['custom']` for `resolve.conditions`, you need to specify `['custom', 'module', 'browser', 'development|production']` instead. +For example, if you previously specified `['custom']` for `resolve.conditions`, you need to specify `['custom', ...defaultClientConditions]` instead. ### JSON stringify diff --git a/packages/vite/src/node/constants.ts b/packages/vite/src/node/constants.ts index 0b10a990350817..b251eddb6f03c9 100644 --- a/packages/vite/src/node/constants.ts +++ b/packages/vite/src/node/constants.ts @@ -45,9 +45,9 @@ const DEFAULT_MAIN_FIELDS = [ 'jsnext:main', // moment still uses this... 'jsnext', ] -export const DEFAULT_CLIENT_MAIN_FIELDS = DEFAULT_MAIN_FIELDS -export const DEFAULT_SERVER_MAIN_FIELDS = DEFAULT_MAIN_FIELDS.filter( - (f) => f !== 'browser', +export const DEFAULT_CLIENT_MAIN_FIELDS = Object.freeze(DEFAULT_MAIN_FIELDS) +export const DEFAULT_SERVER_MAIN_FIELDS = Object.freeze( + DEFAULT_MAIN_FIELDS.filter((f) => f !== 'browser'), ) /** @@ -57,11 +57,11 @@ export const DEFAULT_SERVER_MAIN_FIELDS = DEFAULT_MAIN_FIELDS.filter( export const DEV_PROD_CONDITION = `development|production` as const const DEFAULT_CONDITIONS = ['module', 'browser', 'node', DEV_PROD_CONDITION] -export const DEFAULT_CLIENT_CONDITIONS = DEFAULT_CONDITIONS.filter( - (c) => c !== 'node', +export const DEFAULT_CLIENT_CONDITIONS = Object.freeze( + DEFAULT_CONDITIONS.filter((c) => c !== 'node'), ) -export const DEFAULT_SERVER_CONDITIONS = DEFAULT_CONDITIONS.filter( - (c) => c !== 'browser', +export const DEFAULT_SERVER_CONDITIONS = Object.freeze( + DEFAULT_CONDITIONS.filter((c) => c !== 'browser'), ) // Baseline support browserslist diff --git a/packages/vite/src/node/publicUtils.ts b/packages/vite/src/node/publicUtils.ts index 61786e487655c6..d27fed085e0890 100644 --- a/packages/vite/src/node/publicUtils.ts +++ b/packages/vite/src/node/publicUtils.ts @@ -3,7 +3,13 @@ * 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 { + VERSION as version, + DEFAULT_CLIENT_CONDITIONS as defaultClientConditions, + DEFAULT_CLIENT_MAIN_FIELDS as defaultClientMainFields, + DEFAULT_SERVER_CONDITIONS as defaultServerConditions, + DEFAULT_SERVER_MAIN_FIELDS as defaultServerMainFields, +} from './constants' export { version as esbuildVersion } from 'esbuild' export { splitVendorChunkPlugin, diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 60b78f869cfffa..0a9d1e37e3f31b 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -1070,27 +1070,36 @@ function backwardCompatibleWorkerPlugins(plugins: any) { return [] } -function deepClone(value: T): T { +type DeepWritable = + T extends ReadonlyArray + ? { -readonly [P in keyof T]: DeepWritable } + : T extends RegExp + ? RegExp + : T[keyof T] extends Function + ? T + : { -readonly [P in keyof T]: DeepWritable } + +function deepClone(value: T): DeepWritable { if (Array.isArray(value)) { - return value.map((v) => deepClone(v)) as T + return value.map((v) => deepClone(v)) as DeepWritable } if (isObject(value)) { const cloned: Record = {} for (const key in value) { cloned[key] = deepClone(value[key]) } - return cloned as T + return cloned as DeepWritable } if (typeof value === 'function') { - return value as T + return value as DeepWritable } if (value instanceof RegExp) { - return structuredClone(value) + return structuredClone(value) as DeepWritable } if (typeof value === 'object' && value != null) { throw new Error('Cannot deep clone non-plain object') } - return value + return value as DeepWritable } type MaybeFallback = undefined extends V ? Exclude | D : V @@ -1146,7 +1155,7 @@ function mergeWithDefaultsRecursively< export function mergeWithDefaults< D extends Record, V extends Record, ->(defaults: D, values: V): MergeWithDefaultsResult { +>(defaults: D, values: V): MergeWithDefaultsResult, V> { // NOTE: we need to clone the value here to avoid mutating the defaults const clonedDefaults = deepClone(defaults) return mergeWithDefaultsRecursively(clonedDefaults, values) diff --git a/playground/resolve/vite.config.js b/playground/resolve/vite.config.js index d12eb2283c3333..9216e5db62e818 100644 --- a/playground/resolve/vite.config.js +++ b/playground/resolve/vite.config.js @@ -1,5 +1,5 @@ import path from 'node:path' -import { defineConfig, normalizePath } from 'vite' +import { defaultClientConditions, defineConfig, normalizePath } from 'vite' import { a } from './config-dep.cjs' const virtualFile = '@virtual-file' @@ -32,7 +32,7 @@ export default defineConfig({ resolve: { extensions: ['.mjs', '.js', '.es', '.ts'], mainFields: ['browser', 'custom', 'module'], - conditions: ['module', 'browser', 'development|production', 'custom'], + conditions: [...defaultClientConditions, 'custom'], }, define: { VITE_CONFIG_DEP_TEST: a, diff --git a/playground/ssr-conditions/vite.config.js b/playground/ssr-conditions/vite.config.js index e59e1c2a612556..78eb0df4d35fe1 100644 --- a/playground/ssr-conditions/vite.config.js +++ b/playground/ssr-conditions/vite.config.js @@ -1,11 +1,11 @@ -import { defineConfig } from 'vite' +import { defaultServerConditions, defineConfig } from 'vite' export default defineConfig({ ssr: { external: ['@vitejs/test-ssr-conditions-external'], noExternal: ['@vitejs/test-ssr-conditions-no-external'], resolve: { - conditions: ['module', 'node', 'development|production', 'react-server'], + conditions: [...defaultServerConditions, 'react-server'], externalConditions: ['node', 'workerd', 'react-server'], }, }, diff --git a/playground/ssr-webworker/vite.config.js b/playground/ssr-webworker/vite.config.js index 223b79e5f8ba4c..a2542d7d422c07 100644 --- a/playground/ssr-webworker/vite.config.js +++ b/playground/ssr-webworker/vite.config.js @@ -1,4 +1,4 @@ -import { defineConfig } from 'vite' +import { defaultClientConditions, defineConfig } from 'vite' export default defineConfig({ build: { @@ -14,7 +14,7 @@ export default defineConfig({ // in the runtime, and so we can externalize it when bundling. external: ['node:assert'], resolve: { - conditions: ['module', 'browser', 'development|production', 'worker'], + conditions: [...defaultClientConditions, 'worker'], }, }, plugins: [