Skip to content

Commit 9350461

Browse files
authored
fix(vitest): deduplicate vitest when running globally or in a workspace (#4238)
1 parent 62d0080 commit 9350461

File tree

7 files changed

+38
-21
lines changed

7 files changed

+38
-21
lines changed

packages/vite-node/src/server.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { performance } from 'node:perf_hooks'
22
import { existsSync } from 'node:fs'
3+
import assert from 'node:assert'
34
import { join, normalize, relative, resolve } from 'pathe'
45
import type { TransformResult, ViteDevServer } from 'vite'
56
import createDebug from 'debug'
@@ -131,9 +132,14 @@ export class ViteNodeServer {
131132
return (ssrTransformResult?.map || null) as unknown as EncodedSourceMap | null
132133
}
133134

135+
private assertMode(mode: 'web' | 'ssr') {
136+
assert(mode === 'web' || mode === 'ssr', `"transformMode" can only be "web" or "ssr", received "${mode}".`)
137+
}
138+
134139
async fetchModule(id: string, transformMode?: 'web' | 'ssr'): Promise<FetchResult> {
135140
const moduleId = normalizeModuleId(id)
136141
const mode = transformMode || this.getTransformMode(id)
142+
this.assertMode(mode)
137143
const promiseMap = this.fetchPromiseMap[mode]
138144
// reuse transform for concurrent requests
139145
if (!promiseMap.has(moduleId)) {
@@ -152,6 +158,7 @@ export class ViteNodeServer {
152158

153159
async transformRequest(id: string, filepath = id, transformMode?: 'web' | 'ssr') {
154160
const mode = transformMode || this.getTransformMode(id)
161+
this.assertMode(mode)
155162
const promiseMap = this.transformPromiseMap[mode]
156163
// reuse transform for concurrent requests
157164
if (!promiseMap.has(id)) {

packages/vitest/src/node/core.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { existsSync, promises as fs } from 'node:fs'
22
import type { ViteDevServer } from 'vite'
33
import { mergeConfig } from 'vite'
4-
import { basename, dirname, join, normalize, relative } from 'pathe'
4+
import { basename, dirname, join, normalize, relative, resolve } from 'pathe'
55
import fg from 'fast-glob'
66
import mm from 'micromatch'
77
import c from 'picocolors'
@@ -15,6 +15,7 @@ import { hasFailed, noop, slash, toArray } from '../utils'
1515
import { getCoverageProvider } from '../integrations/coverage'
1616
import type { BrowserProvider } from '../types/browser'
1717
import { CONFIG_NAMES, configFiles, workspacesFiles as workspaceFiles } from '../constants'
18+
import { rootDir } from '../paths'
1819
import { createPool } from './pool'
1920
import type { ProcessPool, WorkspaceSpec } from './pool'
2021
import { createBenchmarkReporters, createReporters } from './reporters/utils'
@@ -58,6 +59,12 @@ export class Vitest {
5859
public projects: WorkspaceProject[] = []
5960
private projectsTestFiles = new Map<string, Set<WorkspaceProject>>()
6061

62+
projectFiles!: {
63+
workerPath: string
64+
forksPath: string
65+
vmPath: string
66+
}
67+
6168
constructor(
6269
public readonly mode: VitestRunMode,
6370
) {
@@ -90,6 +97,16 @@ export class Vitest {
9097
this.registerWatcher()
9198

9299
this.vitenode = new ViteNodeServer(server, this.config.server)
100+
101+
// if Vitest is running globally, then we should still import local vitest if possible
102+
const projectVitestPath = await this.vitenode.resolveId('vitest')
103+
const vitestDir = projectVitestPath ? resolve(projectVitestPath.id, '../..') : rootDir
104+
this.projectFiles = {
105+
workerPath: join(vitestDir, 'dist/worker.js'),
106+
forksPath: join(vitestDir, 'dist/child.js'),
107+
vmPath: join(vitestDir, 'dist/vm.js'),
108+
}
109+
93110
const node = this.vitenode
94111
this.runner = new ViteNodeRunner({
95112
root: server.config.root,

packages/vitest/src/node/plugins/utils.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { DepOptimizationOptions, ResolvedConfig, UserConfig as ViteConfig }
33
import { dirname } from 'pathe'
44
import type { DepsOptimizationOptions, InlineConfig } from '../../types'
55
import { VitestCache } from '../cache'
6+
import { rootDir } from '../../paths'
67

78
export function resolveOptimizerConfig(_testOptions: DepsOptimizationOptions | undefined, viteOptions: DepOptimizationOptions | undefined, testConfig: InlineConfig) {
89
const testOptions = _testOptions || {}
@@ -102,6 +103,6 @@ export function hijackVitePluginInject(viteConfig: ResolvedConfig) {
102103

103104
export function resolveFsAllow(projectRoot: string, rootConfigFile: string | false | undefined) {
104105
if (!rootConfigFile)
105-
return [searchForWorkspaceRoot(projectRoot)]
106-
return [dirname(rootConfigFile), searchForWorkspaceRoot(projectRoot)]
106+
return [searchForWorkspaceRoot(projectRoot), rootDir]
107+
return [dirname(rootConfigFile), searchForWorkspaceRoot(projectRoot), rootDir]
107108
}

packages/vitest/src/node/pool.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ export interface ProcessPool {
1919
}
2020

2121
export interface PoolProcessOptions {
22+
workerPath: string
23+
forksPath: string
24+
vmPath: string
2225
execArgv: string[]
2326
env: Record<string, string>
2427
}
@@ -61,6 +64,7 @@ export function createPool(ctx: Vitest): ProcessPool {
6164
)
6265

6366
const options: PoolProcessOptions = {
67+
...ctx.projectFiles,
6468
execArgv: ctx.config.deps.registerNodeLoader
6569
? [
6670
...execArgv,

packages/vitest/src/node/pools/child.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,17 @@
11
import v8 from 'node:v8'
2-
import { fileURLToPath, pathToFileURL } from 'node:url'
32
import * as nodeos from 'node:os'
43
import EventEmitter from 'node:events'
54
import { Tinypool } from 'tinypool'
65
import type { TinypoolChannel, Options as TinypoolOptions } from 'tinypool'
76
import { createBirpc } from 'birpc'
8-
import { resolve } from 'pathe'
97
import type { ContextTestEnvironment, ResolvedConfig, RunnerRPC, RuntimeRPC, Vitest } from '../../types'
108
import type { ChildContext } from '../../types/child'
119
import type { PoolProcessOptions, ProcessPool, RunWithFiles } from '../pool'
12-
import { distDir } from '../../paths'
1310
import type { WorkspaceProject } from '../workspace'
1411
import { envsOrder, groupFilesByEnv } from '../../utils/test-helpers'
1512
import { groupBy } from '../../utils'
1613
import { createMethodsRPC } from './rpc'
1714

18-
const childPath = fileURLToPath(pathToFileURL(resolve(distDir, './child.js')).href)
19-
2015
function createChildProcessChannel(project: WorkspaceProject) {
2116
const emitter = new EventEmitter()
2217
const cleanup = () => emitter.removeAllListeners()
@@ -53,7 +48,7 @@ function stringifyRegex(input: RegExp | string): string {
5348
return `$$vitest:${input.toString()}`
5449
}
5550

56-
export function createChildProcessPool(ctx: Vitest, { execArgv, env }: PoolProcessOptions): ProcessPool {
51+
export function createChildProcessPool(ctx: Vitest, { execArgv, env, forksPath }: PoolProcessOptions): ProcessPool {
5752
const numCpus
5853
= typeof nodeos.availableParallelism === 'function'
5954
? nodeos.availableParallelism()
@@ -68,7 +63,7 @@ export function createChildProcessPool(ctx: Vitest, { execArgv, env }: PoolProce
6863

6964
const options: TinypoolOptions = {
7065
runtime: 'child_process',
71-
filename: childPath,
66+
filename: forksPath,
7267

7368
maxThreads,
7469
minThreads,

packages/vitest/src/node/pools/threads.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,15 @@
11
import { MessageChannel } from 'node:worker_threads'
22
import * as nodeos from 'node:os'
3-
import { pathToFileURL } from 'node:url'
43
import { createBirpc } from 'birpc'
5-
import { resolve } from 'pathe'
64
import type { Options as TinypoolOptions } from 'tinypool'
75
import Tinypool from 'tinypool'
8-
import { distDir } from '../../paths'
96
import type { ContextTestEnvironment, ResolvedConfig, RunnerRPC, RuntimeRPC, Vitest, WorkerContext } from '../../types'
107
import type { PoolProcessOptions, ProcessPool, RunWithFiles } from '../pool'
118
import { envsOrder, groupFilesByEnv } from '../../utils/test-helpers'
129
import { AggregateError, groupBy } from '../../utils/base'
1310
import type { WorkspaceProject } from '../workspace'
1411
import { createMethodsRPC } from './rpc'
1512

16-
const workerPath = pathToFileURL(resolve(distDir, './worker.js')).href
17-
1813
function createWorkerChannel(project: WorkspaceProject) {
1914
const channel = new MessageChannel()
2015
const port = channel.port2
@@ -38,7 +33,7 @@ function createWorkerChannel(project: WorkspaceProject) {
3833
return { workerPort, port }
3934
}
4035

41-
export function createThreadsPool(ctx: Vitest, { execArgv, env }: PoolProcessOptions): ProcessPool {
36+
export function createThreadsPool(ctx: Vitest, { execArgv, env, workerPath }: PoolProcessOptions): ProcessPool {
4237
const numCpus
4338
= typeof nodeos.availableParallelism === 'function'
4439
? nodeos.availableParallelism()

packages/vitest/src/node/pools/vm-threads.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import { MessageChannel } from 'node:worker_threads'
22
import * as nodeos from 'node:os'
3-
import { pathToFileURL } from 'node:url'
43
import { createBirpc } from 'birpc'
54
import { resolve } from 'pathe'
65
import type { Options as TinypoolOptions } from 'tinypool'
76
import Tinypool from 'tinypool'
8-
import { distDir, rootDir } from '../../paths'
7+
import { rootDir } from '../../paths'
98
import type { ContextTestEnvironment, ResolvedConfig, RunnerRPC, RuntimeRPC, Vitest, WorkerContext } from '../../types'
109
import type { PoolProcessOptions, ProcessPool, RunWithFiles } from '../pool'
1110
import { groupFilesByEnv } from '../../utils/test-helpers'
@@ -14,7 +13,6 @@ import type { WorkspaceProject } from '../workspace'
1413
import { getWorkerMemoryLimit, stringToBytes } from '../../utils/memory-limit'
1514
import { createMethodsRPC } from './rpc'
1615

17-
const workerPath = pathToFileURL(resolve(distDir, './vm.js')).href
1816
const suppressWarningsPath = resolve(rootDir, './suppress-warnings.cjs')
1917

2018
function createWorkerChannel(project: WorkspaceProject) {
@@ -40,7 +38,7 @@ function createWorkerChannel(project: WorkspaceProject) {
4038
return { workerPort, port }
4139
}
4240

43-
export function createVmThreadsPool(ctx: Vitest, { execArgv, env }: PoolProcessOptions): ProcessPool {
41+
export function createVmThreadsPool(ctx: Vitest, { execArgv, env, vmPath }: PoolProcessOptions): ProcessPool {
4442
const numCpus
4543
= typeof nodeos.availableParallelism === 'function'
4644
? nodeos.availableParallelism()
@@ -54,7 +52,7 @@ export function createVmThreadsPool(ctx: Vitest, { execArgv, env }: PoolProcessO
5452
const minThreads = ctx.config.poolOptions?.vmThreads?.minThreads ?? threadsCount
5553

5654
const options: TinypoolOptions = {
57-
filename: workerPath,
55+
filename: vmPath,
5856
// TODO: investigate further
5957
// It seems atomics introduced V8 Fatal Error https://github.com/vitest-dev/vitest/issues/1191
6058
useAtomics: ctx.config.poolOptions?.vmThreads?.useAtomics ?? false,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy