fix: apply development|production condition on Vite 6 (#7301)

Co-authored-by: Vladimir Sheremet <sleuths.slews0s@icloud.com>
This commit is contained in:
Hiroshi Ogawa 2025-01-23 22:38:54 +09:00 committed by GitHub
parent 0b404e567b
commit ef1464fc7b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 233 additions and 25 deletions

View File

@ -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 <Badge type="danger">API</Badge> {#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`:

View File

@ -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: {

View File

@ -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']
}

View File

@ -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: {

View File

@ -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.

15
pnpm-lock.yaml generated
View File

@ -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':

View File

@ -0,0 +1 @@
export default false

View File

@ -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"
}
}
}

View File

@ -0,0 +1 @@
export default true

View File

@ -0,0 +1 @@
export default false

View File

@ -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,
}

View File

@ -0,0 +1 @@
export default !!import.meta.__IS_INLINE__

View File

@ -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"
}
}

View File

@ -0,0 +1 @@
export default true

View File

@ -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,
},
}
)
})

View File

@ -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'],
},
}
})

View File

@ -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:*"

View File

@ -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('')
})