[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
12

Configure Feed

Select the types of activity you want to include in your feed.

perf: reduce the amount of dynamic imports (#8465)

authored by

Vladimir and committed by
GitHub
(Aug 25, 2025, 2:37 PM +0200) db6cd73b cc98c611

+224 -133
+1 -1
packages/web-worker/src/runner.ts
··· 6 6 VitestModuleEvaluator, 7 7 } from 'vitest/internal/module-runner' 8 8 9 - export function startWebWorkerModuleRunner(context: Record<string, unknown>): Promise<VitestModuleRunner> { 9 + export function startWebWorkerModuleRunner(context: Record<string, unknown>): VitestModuleRunner { 10 10 const state = getWorkerState() 11 11 const mocker = (globalThis as any).__vitest_mocker__ 12 12
+25 -28
packages/web-worker/src/shared-worker.ts
··· 117 117 118 118 this._vw_name = fileId 119 119 120 - startWebWorkerModuleRunner(context) 121 - .then((runner) => { 122 - return runner.mocker.resolveId(fileId).then(({ url, id: resolvedId }) => { 123 - this._vw_name = name ?? url 124 - debug('initialize shared worker %s', this._vw_name) 120 + const runner = startWebWorkerModuleRunner(context) 121 + runner.mocker.resolveId(fileId).then(({ url, id: resolvedId }) => { 122 + this._vw_name = name ?? url 123 + debug('initialize shared worker %s', this._vw_name) 125 124 126 - return runner.import(url).then(() => { 127 - runner._invalidateSubTreeById([ 128 - resolvedId, 129 - runner.mocker.getMockPath(resolvedId), 130 - ]) 131 - this._vw_workerTarget.dispatchEvent( 132 - new MessageEvent('connect', { 133 - ports: [this._vw_workerPort], 134 - }), 135 - ) 136 - debug('shared worker %s successfully initialized', this._vw_name) 137 - }) 138 - }) 125 + return runner.import(url).then(() => { 126 + runner._invalidateSubTreeById([ 127 + resolvedId, 128 + runner.mocker.getMockPath(resolvedId), 129 + ]) 130 + this._vw_workerTarget.dispatchEvent( 131 + new MessageEvent('connect', { 132 + ports: [this._vw_workerPort], 133 + }), 134 + ) 135 + debug('shared worker %s successfully initialized', this._vw_name) 139 136 }) 140 - .catch((e) => { 141 - debug('shared worker %s failed to initialize: %o', this._vw_name, e) 142 - const EventConstructor = globalThis.ErrorEvent || globalThis.Event 143 - const error = new EventConstructor('error', { 144 - error: e, 145 - message: e.message, 146 - }) 147 - this.dispatchEvent(error) 148 - this.onerror?.(error) 149 - console.error(e) 137 + }).catch((e) => { 138 + debug('shared worker %s failed to initialize: %o', this._vw_name, e) 139 + const EventConstructor = globalThis.ErrorEvent || globalThis.Event 140 + const error = new EventConstructor('error', { 141 + error: e, 142 + message: e.message, 150 143 }) 144 + this.dispatchEvent(error) 145 + this.onerror?.(error) 146 + console.error(e) 147 + }) 151 148 } 152 149 } 153 150 }
+28 -31
packages/web-worker/src/worker.ts
··· 116 116 117 117 this._vw_name = fileId 118 118 119 - startWebWorkerModuleRunner(context) 120 - .then((runner) => { 121 - return runner.mocker.resolveId(fileId).then(({ url, id: resolvedId }) => { 122 - this._vw_name = options?.name ?? url 123 - debug('initialize worker %s', this._vw_name) 119 + const runner = startWebWorkerModuleRunner(context) 120 + runner.mocker.resolveId(fileId).then(({ url, id: resolvedId }) => { 121 + this._vw_name = options?.name ?? url 122 + debug('initialize worker %s', this._vw_name) 124 123 125 - return runner.import(url).then(() => { 126 - runner._invalidateSubTreeById([ 127 - resolvedId, 128 - runner.mocker.getMockPath(resolvedId), 129 - ]) 130 - const q = this._vw_messageQueue 131 - this._vw_messageQueue = null 132 - if (q) { 133 - q.forEach( 134 - ([data, transfer]) => this.postMessage(data, transfer), 135 - this, 136 - ) 137 - } 138 - debug('worker %s successfully initialized', this._vw_name) 139 - }) 140 - }) 124 + return runner.import(url).then(() => { 125 + runner._invalidateSubTreeById([ 126 + resolvedId, 127 + runner.mocker.getMockPath(resolvedId), 128 + ]) 129 + const q = this._vw_messageQueue 130 + this._vw_messageQueue = null 131 + if (q) { 132 + q.forEach( 133 + ([data, transfer]) => this.postMessage(data, transfer), 134 + this, 135 + ) 136 + } 137 + debug('worker %s successfully initialized', this._vw_name) 141 138 }) 142 - .catch((e) => { 143 - debug('worker %s failed to initialize: %o', this._vw_name, e) 144 - const EventConstructor = globalThis.ErrorEvent || globalThis.Event 145 - const error = new EventConstructor('error', { 146 - error: e, 147 - message: e.message, 148 - }) 149 - this.dispatchEvent(error) 150 - this.onerror?.(error) 151 - console.error(e) 139 + }).catch((e) => { 140 + debug('worker %s failed to initialize: %o', this._vw_name, e) 141 + const EventConstructor = globalThis.ErrorEvent || globalThis.Event 142 + const error = new EventConstructor('error', { 143 + error: e, 144 + message: e.message, 152 145 }) 146 + this.dispatchEvent(error) 147 + this.onerror?.(error) 148 + console.error(e) 149 + }) 153 150 } 154 151 155 152 addEventListener(
+12 -16
packages/vitest/src/integrations/vi.ts
··· 422 422 setConfig: (config: RuntimeOptions) => void 423 423 424 424 /** 425 - * If config was changed with `vi.setConfig`, this will reset it to the original state. 425 + * If config was changed with `vi.setConfig`, this will reset it to the original state(). 426 426 */ 427 427 resetConfig: () => void 428 428 } 429 429 430 430 function createVitest(): VitestUtils { 431 - let _mockedDate: Date | null = null 432 431 let _config: null | SerializedConfig = null 433 432 434 - const workerState = getWorkerState() 433 + const state = () => getWorkerState() 435 434 436 435 let _timers: FakeTimers 437 436 438 437 const timers = () => 439 438 (_timers ||= new FakeTimers({ 440 439 global: globalThis, 441 - config: workerState.config.fakeTimers, 440 + config: state().config.fakeTimers, 442 441 })) 443 442 444 443 const _stubsGlobal = new Map< ··· 454 453 if (isChildProcess()) { 455 454 if ( 456 455 config?.toFake?.includes('nextTick') 457 - || workerState.config?.fakeTimers?.toFake?.includes('nextTick') 456 + || state().config?.fakeTimers?.toFake?.includes('nextTick') 458 457 ) { 459 458 throw new Error( 460 459 'vi.useFakeTimers({ toFake: ["nextTick"] }) is not supported in node:child_process. Use --pool=threads if mocking nextTick is required.', ··· 463 462 } 464 463 465 464 if (config) { 466 - timers().configure({ ...workerState.config.fakeTimers, ...config }) 465 + timers().configure({ ...state().config.fakeTimers, ...config }) 467 466 } 468 467 else { 469 - timers().configure(workerState.config.fakeTimers) 468 + timers().configure(state().config.fakeTimers) 470 469 } 471 470 472 471 timers().useFakeTimers() ··· 479 478 480 479 useRealTimers() { 481 480 timers().useRealTimers() 482 - _mockedDate = null 483 481 return utils 484 482 }, 485 483 ··· 687 685 }, 688 686 689 687 stubEnv(name: string, value: string | boolean | undefined) { 690 - const state = getWorkerState() 691 - const env = state.metaEnv 688 + const env = state().metaEnv 692 689 if (!_stubsEnv.has(name)) { 693 690 _stubsEnv.set(name, env[name]) 694 691 } ··· 718 715 }, 719 716 720 717 unstubAllEnvs() { 721 - const state = getWorkerState() 722 - const env = state.metaEnv 718 + const env = state().metaEnv 723 719 _stubsEnv.forEach((original, name) => { 724 720 if (original === undefined) { 725 721 delete env[name] ··· 733 729 }, 734 730 735 731 resetModules() { 736 - resetModules(workerState.evaluatedModules) 732 + resetModules(state().evaluatedModules) 737 733 return utils 738 734 }, 739 735 ··· 743 739 744 740 setConfig(config: RuntimeOptions) { 745 741 if (!_config) { 746 - _config = { ...workerState.config } 742 + _config = { ...state().config } 747 743 } 748 - Object.assign(workerState.config, config) 744 + Object.assign(state().config, config) 749 745 }, 750 746 751 747 resetConfig() { 752 748 if (_config) { 753 - Object.assign(workerState.config, _config) 749 + Object.assign(state().config, _config) 754 750 } 755 751 }, 756 752 }
+6 -5
packages/vitest/src/node/project.ts
··· 22 22 import pm from 'picomatch' 23 23 import { glob } from 'tinyglobby' 24 24 import { setup } from '../api/setup' 25 + import { createDefinesScript } from '../utils/config-helpers' 25 26 import { isBrowserEnabled, resolveConfig } from './config/resolveConfig' 26 27 import { serializeConfig } from './config/serializeConfig' 27 28 import { ServerModuleRunner } from './environments/serverRunner' ··· 62 63 /** @internal */ _vite?: ViteDevServer 63 64 /** @internal */ _hash?: string 64 65 /** @internal */ _resolver!: VitestResolver 66 + /** @internal */ _serializedDefines?: string 65 67 /** @inetrnal */ testFilesList: string[] | null = null 66 68 67 69 private runner!: ModuleRunner ··· 555 557 556 558 this._resolver = new VitestResolver(server.config.cacheDir, this._config) 557 559 this._vite = server 560 + this._serializedDefines = createDefinesScript(server.config.define) 558 561 559 562 const environment = server.environments.__vitest__ 560 563 this.runner = new ServerModuleRunner( ··· 566 569 567 570 private _serializeOverriddenConfig(): SerializedConfig { 568 571 // TODO: serialize the config _once_ or when needed 569 - const config = serializeConfig( 570 - this.config, 571 - this.vitest.config, 572 - this.vite.config, 573 - ) 572 + const config = serializeConfig(this) 574 573 if (!this.vitest.configOverride) { 575 574 return config 576 575 } ··· 619 618 project._vite = vitest.vite 620 619 project._config = vitest.config 621 620 project._resolver = vitest._resolver 621 + project._serializedDefines = createDefinesScript(vitest.vite.config.define) 622 622 project._setHash() 623 623 project._provideObject(vitest.config.provide) 624 624 return project ··· 633 633 clone._config = config 634 634 clone._setHash() 635 635 clone._parent = parent 636 + clone._serializedDefines = parent._serializedDefines 636 637 clone._provideObject(config.provide) 637 638 return clone 638 639 }
+1
packages/vitest/src/runtime/config.ts
··· 130 130 benchmark: { 131 131 includeSamples: boolean 132 132 } | undefined 133 + serializedDefines: string 133 134 } 134 135 135 136 export interface SerializedCoverageConfig {
+3
packages/vitest/src/runtime/runVmTests.ts
··· 36 36 }) 37 37 38 38 const viteEnvironment = workerState.environment.viteEnvironment || workerState.environment.name 39 + VitestIndex.expect.setState({ 40 + environment: workerState.environment.name, 41 + }) 39 42 if (viteEnvironment === 'client') { 40 43 const _require = createRequire(import.meta.url) 41 44 // always mock "required" `css` files, because we cannot process them
+4 -4
packages/vitest/src/runtime/setup-common.ts
··· 9 9 10 10 let globalSetup = false 11 11 export async function setupCommonEnv(config: SerializedConfig): Promise<void> { 12 - setupDefines(config.defines) 12 + setupDefines(config) 13 13 setupEnv(config.env) 14 14 15 15 if (globalSetup) { ··· 24 24 } 25 25 } 26 26 27 - function setupDefines(defines: Record<string, any>) { 28 - for (const key in defines) { 29 - (globalThis as any)[key] = defines[key] 27 + function setupDefines(config: SerializedConfig) { 28 + for (const key in config.defines) { 29 + (globalThis as any)[key] = config.defines[key] 30 30 } 31 31 } 32 32
+65
packages/vitest/src/utils/config-helpers.ts
··· 49 49 defines, 50 50 } as SerializedConfig 51 51 } 52 + 53 + export function createDefinesScript(define: Record<string, any> | undefined): string { 54 + if (!define) { 55 + return '' 56 + } 57 + const serializedDefine = serializeDefine(define) 58 + if (serializedDefine === '{}') { 59 + return '' 60 + } 61 + return ` 62 + const defines = ${serializeDefine(define)} 63 + Object.keys(defines).forEach((key) => { 64 + const segments = key.split('.') 65 + let target = globalThis 66 + for (let i = 0; i < segments.length; i++) { 67 + const segment = segments[i] 68 + if (i === segments.length - 1) { 69 + target[segment] = defines[key] 70 + } else { 71 + target = target[segment] || (target[segment] = {}) 72 + } 73 + } 74 + }) 75 + ` 76 + } 77 + 78 + /** 79 + * Like `JSON.stringify` but keeps raw string values as a literal 80 + * in the generated code. For example: `"window"` would refer to 81 + * the global `window` object directly. 82 + */ 83 + function serializeDefine(define: Record<string, any>): string { 84 + const userDefine: Record<string, any> = {} 85 + for (const key in define) { 86 + // vitest sets this to avoid vite:client-inject plugin 87 + if (key === 'process.env.NODE_ENV' && define[key] === 'process.env.NODE_ENV') { 88 + continue 89 + } 90 + // import.meta.env.* is handled in `importAnalysis` plugin 91 + if (!key.startsWith('import.meta.env.')) { 92 + userDefine[key] = define[key] 93 + } 94 + } 95 + let res = `{` 96 + const keys = Object.keys(userDefine).sort() 97 + for (let i = 0; i < keys.length; i++) { 98 + const key = keys[i] 99 + const val = userDefine[key] 100 + res += `${JSON.stringify(key)}: ${handleDefineValue(val)}` 101 + if (i !== keys.length - 1) { 102 + res += `, ` 103 + } 104 + } 105 + return `${res}}` 106 + } 107 + 108 + function handleDefineValue(value: any): string { 109 + if (typeof value === 'undefined') { 110 + return 'undefined' 111 + } 112 + if (typeof value === 'string') { 113 + return value 114 + } 115 + return JSON.stringify(value) 116 + }
+1 -2
packages/vitest/src/integrations/chai/index.ts
··· 11 11 } from '@vitest/expect' 12 12 import { getCurrentTest } from '@vitest/runner' 13 13 import { getTestName } from '@vitest/runner/utils' 14 - import { getCurrentEnvironment, getWorkerState } from '../../runtime/utils' 14 + import { getWorkerState } from '../../runtime/utils' 15 15 import { createExpectPoll } from './poll' 16 16 import './setup' 17 17 ··· 47 47 isExpectingAssertionsError: null, 48 48 expectedAssertionsNumber: null, 49 49 expectedAssertionsNumberErrorGen: null, 50 - environment: getCurrentEnvironment(), 51 50 get testPath() { 52 51 return getWorkerState().filepath 53 52 },
+28 -26
packages/vitest/src/node/config/serializeConfig.ts
··· 1 - import type { ResolvedConfig as ViteConfig } from 'vite' 2 - import type { ResolvedConfig, SerializedConfig } from '../types/config' 1 + import type { TestProject } from '../project' 2 + import type { SerializedConfig } from '../types/config' 3 3 4 - export function serializeConfig( 5 - config: ResolvedConfig, 6 - coreConfig: ResolvedConfig, 7 - viteConfig: ViteConfig | undefined, 8 - ): SerializedConfig { 4 + export function serializeConfig(project: TestProject): SerializedConfig { 5 + const { config, globalConfig } = project 6 + const viteConfig = project._vite?.config 9 7 const optimizer = config.deps?.optimizer || {} 10 8 const poolOptions = config.poolOptions 11 9 ··· 69 67 forks: { 70 68 singleFork: 71 69 poolOptions?.forks?.singleFork 72 - ?? coreConfig.poolOptions?.forks?.singleFork 70 + ?? globalConfig.poolOptions?.forks?.singleFork 73 71 ?? false, 74 72 isolate: 75 73 poolOptions?.forks?.isolate 76 74 ?? isolate 77 - ?? coreConfig.poolOptions?.forks?.isolate 75 + ?? globalConfig.poolOptions?.forks?.isolate 78 76 ?? true, 79 77 }, 80 78 threads: { 81 79 singleThread: 82 80 poolOptions?.threads?.singleThread 83 - ?? coreConfig.poolOptions?.threads?.singleThread 81 + ?? globalConfig.poolOptions?.threads?.singleThread 84 82 ?? false, 85 83 isolate: 86 84 poolOptions?.threads?.isolate 87 85 ?? isolate 88 - ?? coreConfig.poolOptions?.threads?.isolate 86 + ?? globalConfig.poolOptions?.threads?.isolate 89 87 ?? true, 90 88 }, 91 89 vmThreads: { 92 90 singleThread: 93 91 poolOptions?.vmThreads?.singleThread 94 - ?? coreConfig.poolOptions?.vmThreads?.singleThread 92 + ?? globalConfig.poolOptions?.vmThreads?.singleThread 95 93 ?? false, 96 94 }, 97 95 vmForks: { 98 96 singleFork: 99 97 poolOptions?.vmForks?.singleFork 100 - ?? coreConfig.poolOptions?.vmForks?.singleFork 98 + ?? globalConfig.poolOptions?.vmForks?.singleFork 101 99 ?? false, 102 100 }, 103 101 }, ··· 113 111 snapshotOptions: { 114 112 // TODO: store it differently, not on the config 115 113 snapshotEnvironment: undefined!, 116 - updateSnapshot: coreConfig.snapshotOptions.updateSnapshot, 114 + updateSnapshot: globalConfig.snapshotOptions.updateSnapshot, 117 115 snapshotFormat: { 118 - ...coreConfig.snapshotOptions.snapshotFormat, 116 + ...globalConfig.snapshotOptions.snapshotFormat, 119 117 }, 120 118 expand: 121 119 config.snapshotOptions.expand 122 - ?? coreConfig.snapshotOptions.expand, 120 + ?? globalConfig.snapshotOptions.expand, 123 121 }, 124 122 sequence: { 125 - shuffle: coreConfig.sequence.shuffle, 126 - concurrent: coreConfig.sequence.concurrent, 127 - seed: coreConfig.sequence.seed, 128 - hooks: coreConfig.sequence.hooks, 129 - setupFiles: coreConfig.sequence.setupFiles, 123 + shuffle: globalConfig.sequence.shuffle, 124 + concurrent: globalConfig.sequence.concurrent, 125 + seed: globalConfig.sequence.seed, 126 + hooks: globalConfig.sequence.hooks, 127 + setupFiles: globalConfig.sequence.setupFiles, 130 128 }, 131 - inspect: coreConfig.inspect, 132 - inspectBrk: coreConfig.inspectBrk, 133 - inspector: coreConfig.inspector, 129 + inspect: globalConfig.inspect, 130 + inspectBrk: globalConfig.inspectBrk, 131 + inspector: globalConfig.inspector, 134 132 watch: config.watch, 135 133 includeTaskLocation: 136 134 config.includeTaskLocation 137 - ?? coreConfig.includeTaskLocation, 135 + ?? globalConfig.includeTaskLocation, 138 136 env: { 139 137 ...viteConfig?.env, 140 138 ...config.env, ··· 161 159 })(config.browser), 162 160 standalone: config.standalone, 163 161 printConsoleTrace: 164 - config.printConsoleTrace ?? coreConfig.printConsoleTrace, 162 + config.printConsoleTrace ?? globalConfig.printConsoleTrace, 165 163 benchmark: config.benchmark && { 166 164 includeSamples: config.benchmark.includeSamples, 167 165 }, 166 + // the browser initialized them via `@vite/env` import 167 + serializedDefines: config.browser.enabled 168 + ? '' 169 + : project._serializedDefines || '', 168 170 } 169 171 }
+5 -1
packages/vitest/src/runtime/moduleRunner/moduleMocker.ts
··· 20 20 21 21 export interface VitestMockerOptions { 22 22 context?: vm.Context 23 - 23 + spyModule?: typeof import('@vitest/spy') 24 24 root: string 25 25 moduleDirectories: string[] 26 26 resolveId: (id: string, importer?: string) => Promise<{ ··· 70 70 Array, 71 71 Map, 72 72 } 73 + } 74 + 75 + if (options.spyModule) { 76 + this.spyModule = options.spyModule 73 77 } 74 78 75 79 const Symbol = this.primitives.Symbol
+2
packages/vitest/src/runtime/moduleRunner/moduleRunner.ts
··· 29 29 ) 30 30 this.moduleExecutionInfo = options.getWorkerState().moduleExecutionInfo 31 31 this.mocker = options.mocker || new VitestMocker(this, { 32 + spyModule: options.spyModule, 32 33 context: options.vm?.context, 33 34 resolveId: options.transport.resolveId, 34 35 get root() { ··· 143 144 getWorkerState: () => WorkerGlobalState 144 145 mocker?: VitestMocker 145 146 vm?: VitestVmOptions 147 + spyModule?: typeof import('@vitest/spy') 146 148 } 147 149 148 150 export interface VitestVmOptions {
+5 -3
packages/vitest/src/runtime/moduleRunner/startModuleRunner.ts
··· 25 25 context?: vm.Context 26 26 externalModulesExecutor?: ExternalModulesExecutor 27 27 state: WorkerGlobalState 28 + spyModule?: typeof import('@vitest/spy') 28 29 } 29 30 30 31 const cwd = process.cwd() 31 32 const isWindows = process.platform === 'win32' 32 33 33 - export async function startVitestModuleRunner(options: ContextModuleRunnerOptions): Promise<VitestModuleRunner> { 34 + export function startVitestModuleRunner(options: ContextModuleRunnerOptions): VitestModuleRunner { 34 35 const state = (): WorkerGlobalState => 35 36 // @ts-expect-error injected untyped global 36 37 globalThis.__vitest_worker__ || options.state ··· 68 69 ) 69 70 70 71 const moduleRunner: VitestModuleRunner = new VitestModuleRunner({ 72 + spyModule: options.spyModule, 71 73 evaluatedModules: options.evaluatedModules, 72 74 evaluator, 73 75 mocker: options.mocker, ··· 161 163 vm, 162 164 }) 163 165 164 - await moduleRunner.import('/@vite/env') 165 - await moduleRunner.mocker.initializeSpyModule() 166 + // await moduleRunner.import('/@vite/env') 167 + // await moduleRunner.mocker.initializeSpyModule() 166 168 167 169 return moduleRunner 168 170 }
+3 -8
packages/vitest/src/runtime/runners/index.ts
··· 1 1 import type { VitestRunner, VitestRunnerConstructor } from '@vitest/runner' 2 2 import type { SerializedConfig } from '../config' 3 3 import type { VitestModuleRunner } from '../moduleRunner/moduleRunner' 4 - import { join, resolve } from 'node:path' 5 4 import { takeCoverageInsideWorker } from '../../integrations/coverage' 6 - import { distDir } from '../../paths' 7 5 import { rpc } from '../rpc' 8 6 import { loadDiffConfig, loadSnapshotSerializers } from '../setup-common' 9 7 import { getWorkerState } from '../utils' 10 - 11 - const runnersFile = resolve(distDir, 'runners.js') 8 + import { NodeBenchmarkRunner } from './benchmark' 9 + import { VitestTestRunner } from './test' 12 10 13 11 async function getTestRunnerConstructor( 14 12 config: SerializedConfig, 15 13 moduleRunner: VitestModuleRunner, 16 14 ): Promise<VitestRunnerConstructor> { 17 15 if (!config.runner) { 18 - const { VitestTestRunner, NodeBenchmarkRunner } = await moduleRunner.import( 19 - join('/@fs/', runnersFile), 20 - ) 21 16 return ( 22 17 config.mode === 'test' ? VitestTestRunner : NodeBenchmarkRunner 23 - ) as VitestRunnerConstructor 18 + ) as any as VitestRunnerConstructor 24 19 } 25 20 const mod = await moduleRunner.import(config.runner) 26 21 if (!mod.default && typeof mod.default !== 'function') {
+21 -6
packages/vitest/src/runtime/workers/base.ts
··· 1 1 import type { WorkerGlobalState } from '../../types/worker' 2 2 import type { VitestModuleRunner } from '../moduleRunner/moduleRunner' 3 3 import type { ContextModuleRunnerOptions } from '../moduleRunner/startModuleRunner' 4 + import { runInThisContext } from 'node:vm' 5 + import * as spyModule from '@vitest/spy' 4 6 import { EvaluatedModules } from 'vite/module-runner' 5 7 import { startVitestModuleRunner } from '../moduleRunner/startModuleRunner' 8 + import { run } from '../runBaseTests' 6 9 import { provideWorkerState } from '../utils' 7 10 8 11 let _moduleRunner: VitestModuleRunner ··· 10 13 const evaluatedModules = new EvaluatedModules() 11 14 const moduleExecutionInfo = new Map() 12 15 13 - async function startModuleRunner(options: ContextModuleRunnerOptions) { 16 + function startModuleRunner(options: ContextModuleRunnerOptions) { 14 17 if (_moduleRunner) { 15 18 return _moduleRunner 16 19 } 17 20 18 - _moduleRunner = await startVitestModuleRunner(options) 21 + _moduleRunner = startVitestModuleRunner(options) 19 22 return _moduleRunner 20 23 } 21 24 ··· 45 48 }) 46 49 }) 47 50 48 - const [executor, { run }] = await Promise.all([ 49 - startModuleRunner({ state, evaluatedModules: state.evaluatedModules }), 50 - import('../runBaseTests'), 51 - ]) 51 + const executor = startModuleRunner({ 52 + state, 53 + evaluatedModules: state.evaluatedModules, 54 + spyModule, 55 + }) 52 56 const fileSpecs = ctx.files.map(f => 53 57 typeof f === 'string' 54 58 ? { filepath: f, testLocations: undefined } 55 59 : f, 56 60 ) 61 + if (ctx.config.serializedDefines) { 62 + try { 63 + runInThisContext(`(() =>{\n${ctx.config.serializedDefines}})()`, { 64 + lineOffset: 1, 65 + filename: 'virtual:load-defines.js', 66 + }) 67 + } 68 + catch (error: any) { 69 + throw new Error(`Failed to load custom "defines": ${error.message}`) 70 + } 71 + } 57 72 58 73 await run( 59 74 method,
+14 -2
packages/vitest/src/runtime/workers/vm.ts
··· 1 1 import type { Context } from 'node:vm' 2 2 import type { WorkerGlobalState } from '../../types/worker' 3 3 import { pathToFileURL } from 'node:url' 4 - import { isContext } from 'node:vm' 4 + import { isContext, runInContext } from 'node:vm' 5 5 import { resolve } from 'pathe' 6 6 import { distDir } from '../../paths' 7 7 import { createCustomConsole } from '../console' ··· 75 75 viteClientModule: stubs['/@vite/client'], 76 76 }) 77 77 78 - const moduleRunner = await startVitestModuleRunner({ 78 + const moduleRunner = startVitestModuleRunner({ 79 79 context, 80 80 evaluatedModules: state.evaluatedModules, 81 81 state, ··· 92 92 writable: false, 93 93 }) 94 94 context.__vitest_mocker__ = moduleRunner.mocker 95 + 96 + if (ctx.config.serializedDefines) { 97 + try { 98 + runInContext(ctx.config.serializedDefines, context, { 99 + filename: 'virtual:load-defines.js', 100 + }) 101 + } 102 + catch (error: any) { 103 + throw new Error(`Failed to load custom "defines": ${error.message}`) 104 + } 105 + } 106 + await moduleRunner.mocker.initializeSpyModule() 95 107 96 108 const { run } = (await moduleRunner.import( 97 109 entryFile,