From ef1464fc7b101709bfbf7b040e5bad62998c2ff9 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 23 Jan 2025 22:38:54 +0900 Subject: [PATCH] fix: apply `development|production` condition on Vite 6 (#7301) Co-authored-by: Vladimir Sheremet --- docs/guide/migration.md | 4 ++ packages/vitest/src/node/plugins/index.ts | 15 +++---- packages/vitest/src/node/plugins/utils.ts | 21 ++++++++++ packages/vitest/src/node/plugins/workspace.ts | 14 ++----- packages/vitest/src/node/pool.ts | 20 +++++++--- pnpm-lock.yaml | 15 +++++++ .../test-dep-conditions-indirect/false.js | 1 + .../test-dep-conditions-indirect/package.json | 27 +++++++++++++ .../deps/test-dep-conditions-indirect/true.js | 1 + test/config/deps/test-dep-conditions/false.js | 1 + .../deps/test-dep-conditions/indirect.js | 13 +++++++ .../config/deps/test-dep-conditions/inline.js | 1 + .../deps/test-dep-conditions/package.json | 32 +++++++++++++++ test/config/deps/test-dep-conditions/true.js | 1 + test/config/fixtures/conditions/basic.test.js | 37 ++++++++++++++++++ .../config/fixtures/conditions/vite.config.ts | 15 +++++++ test/config/package.json | 1 + test/config/test/conditions-cli.test.ts | 39 +++++++++++++++++++ 18 files changed, 233 insertions(+), 25 deletions(-) create mode 100644 test/config/deps/test-dep-conditions-indirect/false.js create mode 100644 test/config/deps/test-dep-conditions-indirect/package.json create mode 100644 test/config/deps/test-dep-conditions-indirect/true.js create mode 100644 test/config/deps/test-dep-conditions/false.js create mode 100644 test/config/deps/test-dep-conditions/indirect.js create mode 100644 test/config/deps/test-dep-conditions/inline.js create mode 100644 test/config/deps/test-dep-conditions/package.json create mode 100644 test/config/deps/test-dep-conditions/true.js create mode 100644 test/config/fixtures/conditions/basic.test.js create mode 100644 test/config/fixtures/conditions/vite.config.ts diff --git a/docs/guide/migration.md b/docs/guide/migration.md index c0322a714e71..9e15c3043c9b 100644 --- a/docs/guide/migration.md +++ b/docs/guide/migration.md @@ -129,6 +129,10 @@ expect(() => { See PR for more details: [#5876](https://github.com/vitest-dev/vitest/pull/5876). +### `module` condition export is not resolved by default on Vite 6 + +Vite 6 allows more flexible [`resolve.conditions`](https://vite.dev/config/shared-options#resolve-conditions) options and Vitest configures it to exclude `module` conditional export by default. + ### `Custom` Type is Deprecated API {#custom-type-is-deprecated} The `Custom` type is now an alias for the `Test` type. Note that Vitest updated the public types in 2.1 and changed exported names to `RunnerCustomCase` and `RunnerTestCase`: diff --git a/packages/vitest/src/node/plugins/index.ts b/packages/vitest/src/node/plugins/index.ts index cd251177cdab..f846efcb557d 100644 --- a/packages/vitest/src/node/plugins/index.ts +++ b/packages/vitest/src/node/plugins/index.ts @@ -20,6 +20,7 @@ import { VitestOptimizer } from './optimizer' import { SsrReplacerPlugin } from './ssrReplacer' import { deleteDefineConfig, + getDefaultResolveOptions, hijackVitePluginInject, resolveFsAllow, } from './utils' @@ -73,6 +74,8 @@ export async function VitestPlugin( open = testConfig.uiBase ?? '/__vitest__/' } + const resolveOptions = getDefaultResolveOptions() + const config: ViteConfig = { root: viteConfig.test?.root || options.root, esbuild: @@ -86,11 +89,8 @@ export async function VitestPlugin( legalComments: 'inline', }, resolve: { - // by default Vite resolves `module` field, which not always a native ESM module - // setting this option can bypass that and fallback to cjs version - mainFields: [], + ...resolveOptions, alias: testConfig.alias, - conditions: ['node'], }, server: { ...testConfig.api, @@ -115,12 +115,7 @@ export async function VitestPlugin( // @ts-ignore Vite 6 compat environments: { ssr: { - resolve: { - // by default Vite resolves `module` field, which not always a native ESM module - // setting this option can bypass that and fallback to cjs version - mainFields: [], - conditions: ['node'], - }, + resolve: resolveOptions, }, }, test: { diff --git a/packages/vitest/src/node/plugins/utils.ts b/packages/vitest/src/node/plugins/utils.ts index 2424493447fa..9617eb1217fe 100644 --- a/packages/vitest/src/node/plugins/utils.ts +++ b/packages/vitest/src/node/plugins/utils.ts @@ -6,6 +6,7 @@ import type { import type { DepsOptimizationOptions, InlineConfig } from '../types/config' import { dirname } from 'pathe' import { searchForWorkspaceRoot, version as viteVersion } from 'vite' +import * as vite from 'vite' import { rootDir } from '../../paths' import { VitestCache } from '../cache' @@ -147,3 +148,23 @@ export function resolveFsAllow( rootDir, ] } + +export function getDefaultResolveOptions(): vite.ResolveOptions { + return { + // by default Vite resolves `module` field, which is not always a native ESM module + // setting this option can bypass that and fallback to cjs version + mainFields: [], + // same for `module` condition and Vite 5 doesn't even allow excluding it, + // but now it's possible since Vite 6. + conditions: getDefaultServerConditions(), + } +} + +function getDefaultServerConditions(): string[] { + const viteMajor = Number(viteVersion.split('.')[0]) + if (viteMajor >= 6) { + const conditions: string[] = (vite as any).defaultServerConditions + return conditions.filter(c => c !== 'module') + } + return ['node'] +} diff --git a/packages/vitest/src/node/plugins/workspace.ts b/packages/vitest/src/node/plugins/workspace.ts index 46d4f213ae7f..0f3594f4cda0 100644 --- a/packages/vitest/src/node/plugins/workspace.ts +++ b/packages/vitest/src/node/plugins/workspace.ts @@ -16,6 +16,7 @@ import { VitestOptimizer } from './optimizer' import { SsrReplacerPlugin } from './ssrReplacer' import { deleteDefineConfig, + getDefaultResolveOptions, hijackVitePluginInject, resolveFsAllow, } from './utils' @@ -92,14 +93,12 @@ export function WorkspaceVitestPlugin( } } + const resolveOptions = getDefaultResolveOptions() const config: ViteConfig = { root, resolve: { - // by default Vite resolves `module` field, which not always a native ESM module - // setting this option can bypass that and fallback to cjs version - mainFields: [], + ...resolveOptions, alias: testConfig.alias, - conditions: ['node'], }, esbuild: viteConfig.esbuild === false ? false @@ -130,12 +129,7 @@ export function WorkspaceVitestPlugin( // @ts-ignore Vite 6 compat environments: { ssr: { - resolve: { - // by default Vite resolves `module` field, which not always a native ESM module - // setting this option can bypass that and fallback to cjs version - mainFields: [], - conditions: ['node'], - }, + resolve: resolveOptions, }, }, test: { diff --git a/packages/vitest/src/node/pool.ts b/packages/vitest/src/node/pool.ts index 620596a56db6..d55497b0285d 100644 --- a/packages/vitest/src/node/pool.ts +++ b/packages/vitest/src/node/pool.ts @@ -5,6 +5,7 @@ import type { TestSpecification } from './spec' import type { BuiltinPool, Pool } from './types/pool-options' import { isatty } from 'node:tty' import mm from 'micromatch' +import { version as viteVersion } from 'vite' import { isWindows } from '../utils/env' import { createForksPool } from './pools/forks' import { createThreadsPool } from './pools/threads' @@ -91,11 +92,14 @@ export function createPool(ctx: Vitest): ProcessPool { // in addition to resolve.conditions Vite also adds production/development, // see: https://github.com/vitejs/vite/blob/af2aa09575229462635b7cbb6d248ca853057ba2/packages/vite/src/node/plugins/resolve.ts#L1056-L1080 - const potentialConditions = new Set([ - 'production', - 'development', - ...ctx.vite.config.resolve.conditions, - ]) + const viteMajor = Number(viteVersion.split('.')[0]) + const potentialConditions = new Set(viteMajor >= 6 + ? (ctx.vite.config.ssr.resolve?.conditions ?? []) + : [ + 'production', + 'development', + ...ctx.vite.config.resolve.conditions, + ]) const conditions = [...potentialConditions] .filter((condition) => { if (condition === 'production') { @@ -106,6 +110,12 @@ export function createPool(ctx: Vitest): ProcessPool { } return true }) + .map((condition) => { + if (viteMajor >= 6 && condition === 'development|production') { + return ctx.vite.config.isProduction ? 'production' : 'development' + } + return condition + }) .flatMap(c => ['--conditions', c]) // Instead of passing whole process.execArgv to the workers, pick allowed options. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 60a310da2e8b..7f08982d4b09 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1123,6 +1123,9 @@ importers: test/config: devDependencies: + '@vitest/test-dep-conditions': + specifier: file:./deps/test-dep-conditions + version: file:test/config/deps/test-dep-conditions tinyexec: specifier: ^0.3.2 version: 0.3.2 @@ -4126,6 +4129,12 @@ packages: '@vitest/test-dep-cjs@file:test/core/deps/dep-cjs': resolution: {directory: test/core/deps/dep-cjs, type: directory} + '@vitest/test-dep-conditions-indirect@file:test/config/deps/test-dep-conditions-indirect': + resolution: {directory: test/config/deps/test-dep-conditions-indirect, type: directory} + + '@vitest/test-dep-conditions@file:test/config/deps/test-dep-conditions': + resolution: {directory: test/config/deps/test-dep-conditions, type: directory} + '@vitest/test-dep1@file:test/core/deps/dep1': resolution: {directory: test/core/deps/dep1, type: directory} @@ -12719,6 +12728,12 @@ snapshots: '@vitest/test-dep-cjs@file:test/core/deps/dep-cjs': {} + '@vitest/test-dep-conditions-indirect@file:test/config/deps/test-dep-conditions-indirect': {} + + '@vitest/test-dep-conditions@file:test/config/deps/test-dep-conditions': + dependencies: + '@vitest/test-dep-conditions-indirect': file:test/config/deps/test-dep-conditions-indirect + '@vitest/test-dep1@file:test/core/deps/dep1': {} '@vitest/test-dep2@file:test/core/deps/dep2': diff --git a/test/config/deps/test-dep-conditions-indirect/false.js b/test/config/deps/test-dep-conditions-indirect/false.js new file mode 100644 index 000000000000..2693369b4433 --- /dev/null +++ b/test/config/deps/test-dep-conditions-indirect/false.js @@ -0,0 +1 @@ +export default false diff --git a/test/config/deps/test-dep-conditions-indirect/package.json b/test/config/deps/test-dep-conditions-indirect/package.json new file mode 100644 index 000000000000..d291b0c3cbd1 --- /dev/null +++ b/test/config/deps/test-dep-conditions-indirect/package.json @@ -0,0 +1,27 @@ +{ + "name": "@vitest/test-dep-conditions-indirect", + "type": "module", + "private": true, + "exports": { + "./custom": { + "custom": "./true.js", + "default": "./false.js" + }, + "./module": { + "module": "./true.js", + "default": "./false.js" + }, + "./node": { + "node": "./true.js", + "default": "./false.js" + }, + "./development": { + "development": "./true.js", + "default": "./false.js" + }, + "./production": { + "production": "./true.js", + "default": "./false.js" + } + } +} diff --git a/test/config/deps/test-dep-conditions-indirect/true.js b/test/config/deps/test-dep-conditions-indirect/true.js new file mode 100644 index 000000000000..186b120756be --- /dev/null +++ b/test/config/deps/test-dep-conditions-indirect/true.js @@ -0,0 +1 @@ +export default true diff --git a/test/config/deps/test-dep-conditions/false.js b/test/config/deps/test-dep-conditions/false.js new file mode 100644 index 000000000000..2693369b4433 --- /dev/null +++ b/test/config/deps/test-dep-conditions/false.js @@ -0,0 +1 @@ +export default false diff --git a/test/config/deps/test-dep-conditions/indirect.js b/test/config/deps/test-dep-conditions/indirect.js new file mode 100644 index 000000000000..ad1766a01e9d --- /dev/null +++ b/test/config/deps/test-dep-conditions/indirect.js @@ -0,0 +1,13 @@ +import conditionCustom from '@vitest/test-dep-conditions-indirect/custom' +import conditionDevelopment from '@vitest/test-dep-conditions-indirect/development' +import conditionModule from '@vitest/test-dep-conditions-indirect/module' +import conditionNode from '@vitest/test-dep-conditions-indirect/node' +import conditionProductioin from '@vitest/test-dep-conditions-indirect/production' + +export default { + conditionCustom, + conditionModule, + conditionNode, + conditionDevelopment, + conditionProductioin, +} diff --git a/test/config/deps/test-dep-conditions/inline.js b/test/config/deps/test-dep-conditions/inline.js new file mode 100644 index 000000000000..cf670f6b4f38 --- /dev/null +++ b/test/config/deps/test-dep-conditions/inline.js @@ -0,0 +1 @@ +export default !!import.meta.__IS_INLINE__ diff --git a/test/config/deps/test-dep-conditions/package.json b/test/config/deps/test-dep-conditions/package.json new file mode 100644 index 000000000000..43bc7c1ab86d --- /dev/null +++ b/test/config/deps/test-dep-conditions/package.json @@ -0,0 +1,32 @@ +{ + "name": "@vitest/test-dep-conditions", + "type": "module", + "private": true, + "exports": { + "./custom": { + "custom": "./true.js", + "default": "./false.js" + }, + "./module": { + "module": "./true.js", + "default": "./false.js" + }, + "./node": { + "node": "./true.js", + "default": "./false.js" + }, + "./development": { + "development": "./true.js", + "default": "./false.js" + }, + "./production": { + "production": "./true.js", + "default": "./false.js" + }, + "./inline": "./inline.js", + "./indirect": "./indirect.js" + }, + "dependencies": { + "@vitest/test-dep-conditions-indirect": "file:../test-dep-conditions-indirect" + } +} diff --git a/test/config/deps/test-dep-conditions/true.js b/test/config/deps/test-dep-conditions/true.js new file mode 100644 index 000000000000..186b120756be --- /dev/null +++ b/test/config/deps/test-dep-conditions/true.js @@ -0,0 +1 @@ +export default true diff --git a/test/config/fixtures/conditions/basic.test.js b/test/config/fixtures/conditions/basic.test.js new file mode 100644 index 000000000000..dc27e253a954 --- /dev/null +++ b/test/config/fixtures/conditions/basic.test.js @@ -0,0 +1,37 @@ +import { test, expect } from 'vitest'; +import conditionCustom from '@vitest/test-dep-conditions/custom'; +import conditionModule from '@vitest/test-dep-conditions/module'; +import conditionNode from '@vitest/test-dep-conditions/node'; +import conditionDevelopment from '@vitest/test-dep-conditions/development'; +import conditionProduction from '@vitest/test-dep-conditions/production'; +import inline from '@vitest/test-dep-conditions/inline'; +import indirect from '@vitest/test-dep-conditions/indirect'; + +import { viteVersion } from 'vitest/node' +const viteMajor = Number(viteVersion.split('.')[0]) + +test('conditions', () => { + expect({ + conditionCustom, + conditionModule, + conditionNode, + conditionDevelopment, + conditionProduction, + indirect + }).toEqual( + { + conditionCustom: true, + "conditionDevelopment": true, + "conditionModule": viteMajor <= 5, + "conditionNode": true, + "conditionProduction": false, + "indirect": { + conditionCustom: true, + "conditionDevelopment": true, + "conditionModule": viteMajor <= 5 && inline, + "conditionNode": true, + "conditionProductioin": false, + }, + } + ) +}) diff --git a/test/config/fixtures/conditions/vite.config.ts b/test/config/fixtures/conditions/vite.config.ts new file mode 100644 index 000000000000..5e766dee63d9 --- /dev/null +++ b/test/config/fixtures/conditions/vite.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from "vitest/config" + +export default defineConfig({ + define: { + 'import.meta.__IS_INLINE__': 'true', + }, + resolve: { + conditions: ['custom'], + }, + ssr: { + resolve: { + conditions: ['custom'], + }, + } +}) diff --git a/test/config/package.json b/test/config/package.json index c59161d0a6b0..dd7c8c7b2d1a 100644 --- a/test/config/package.json +++ b/test/config/package.json @@ -6,6 +6,7 @@ "test": "vitest --typecheck.enabled" }, "devDependencies": { + "@vitest/test-dep-conditions": "file:./deps/test-dep-conditions", "tinyexec": "^0.3.2", "vite": "latest", "vitest": "workspace:*" diff --git a/test/config/test/conditions-cli.test.ts b/test/config/test/conditions-cli.test.ts index 7f44397095fa..be959e59af57 100644 --- a/test/config/test/conditions-cli.test.ts +++ b/test/config/test/conditions-cli.test.ts @@ -53,6 +53,11 @@ test('correctly imports external dependencies with a custom condition', async () resolve: { conditions: ['custom'], }, + ssr: { + resolve: { + conditions: ['custom'], + }, + }, define: { TEST_CONDITION: '"custom"', }, @@ -60,3 +65,37 @@ test('correctly imports external dependencies with a custom condition', async () expect(stderr).toBe('') }) + +test('conditions (external)', async () => { + const { stderr } = await runVitest({ + root: 'fixtures/conditions', + }) + + expect(stderr).toBe('') +}) + +test('conditions (inline direct)', async () => { + const { stderr } = await runVitest({ + root: 'fixtures/conditions', + server: { + deps: { + inline: ['@vitest/test-dep-conditions'], + }, + }, + }) + + expect(stderr).toBe('') +}) + +test('conditions (inline indirect)', async () => { + const { stderr } = await runVitest({ + root: 'fixtures/conditions', + server: { + deps: { + inline: ['@vitest/test-dep-conditions', '@vitest/test-dep-conditions-indirect'], + }, + }, + }) + + expect(stderr).toBe('') +})