Skip to content

Commit

Permalink
fix(vitest): remove excessive listeners when running without isolatio…
Browse files Browse the repository at this point in the history
…n, don't reset the state (#5132)
  • Loading branch information
sheremet-va committed Feb 7, 2024
1 parent 6f5b42b commit b607f1e
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 19 deletions.
37 changes: 26 additions & 11 deletions packages/vitest/src/runtime/execute.ts
Expand Up @@ -42,14 +42,11 @@ export interface ContextExecutorOptions {

const bareVitestRegexp = /^@?vitest(\/|$)/

export async function startVitestExecutor(options: ContextExecutorOptions) {
// @ts-expect-error injected untyped global
const state = (): WorkerGlobalState => globalThis.__vitest_worker__ || options.state
const rpc = () => state().rpc
const dispose: (() => void)[] = []

process.exit = (code = process.exitCode || 0): never => {
throw new Error(`process.exit unexpectedly called with "${code}"`)
}
function listenForErrors(state: () => WorkerGlobalState) {
dispose.forEach(fn => fn())
dispose.length = 0

function catchError(err: unknown, type: string) {
const worker = state()
Expand All @@ -60,13 +57,31 @@ export async function startVitestExecutor(options: ContextExecutorOptions) {
error.VITEST_TEST_PATH = relative(state().config.root, worker.filepath)
error.VITEST_AFTER_ENV_TEARDOWN = worker.environmentTeardownRun
}
rpc().onUnhandledError(error, type)
state().rpc.onUnhandledError(error, type)
}

process.setMaxListeners(25)
const uncaughtException = (e: Error) => catchError(e, 'Uncaught Exception')
const unhandledRejection = (e: Error) => catchError(e, 'Unhandled Rejection')

process.on('uncaughtException', uncaughtException)
process.on('unhandledRejection', unhandledRejection)

dispose.push(() => {
process.off('uncaughtException', uncaughtException)
process.off('unhandledRejection', unhandledRejection)
})
}

export async function startVitestExecutor(options: ContextExecutorOptions) {
// @ts-expect-error injected untyped global
const state = (): WorkerGlobalState => globalThis.__vitest_worker__ || options.state
const rpc = () => state().rpc

process.exit = (code = process.exitCode || 0): never => {
throw new Error(`process.exit unexpectedly called with "${code}"`)
}

process.on('uncaughtException', e => catchError(e, 'Uncaught Exception'))
process.on('unhandledRejection', e => catchError(e, 'Unhandled Rejection'))
listenForErrors(state)

const getTransformMode = () => {
return state().environment.transformMode ?? 'ssr'
Expand Down
4 changes: 4 additions & 0 deletions packages/vitest/src/runtime/rpc.ts
Expand Up @@ -73,6 +73,10 @@ export function createRuntimeRpc(options: Pick<BirpcOptions<RuntimeRPC>, 'on' |
if (functionName === 'fetch' || functionName === 'transform' || functionName === 'resolveId')
message += ` with "${JSON.stringify(args)}"`

// JSON.stringify cannot serialize Error instances
if (functionName === 'onUnhandledError')
message += ` with "${args[0]?.message || args[0]}"`

throw new Error(message)
},
...options,
Expand Down
9 changes: 1 addition & 8 deletions packages/vitest/src/runtime/worker.ts
Expand Up @@ -3,7 +3,6 @@ import { workerId as poolId } from 'tinypool'
import { ModuleCacheMap } from 'vite-node/client'
import type { ContextRPC } from '../types/rpc'
import { loadEnvironment } from '../integrations/env/loader'
import type { WorkerGlobalState } from '../types/worker'
import { isChildProcess, setProcessTitle } from '../utils/base'
import { setupInspect } from './inspector'
import { createRuntimeRpc, rpcDone } from './rpc'
Expand All @@ -21,8 +20,6 @@ export async function run(ctx: ContextRPC) {
process.env.VITEST_WORKER_ID = String(ctx.workerId)
process.env.VITEST_POOL_ID = String(poolId)

let state: WorkerGlobalState | null = null

try {
// worker is a filepath or URL to a file that exposes a default export with "getRpcOptions" and "runTests" methods
if (ctx.worker[0] === '.')
Expand All @@ -46,7 +43,7 @@ export async function run(ctx: ContextRPC) {
if (ctx.environment.transformMode)
environment.transformMode = ctx.environment.transformMode

state = {
const state = {
ctx,
// here we create a new one, workers can reassign this if they need to keep it non-isolated
moduleCache: new ModuleCacheMap(),
Expand All @@ -70,9 +67,5 @@ export async function run(ctx: ContextRPC) {
finally {
await rpcDone().catch(() => {})
inspectorCleanup()
if (state) {
state.environment = null as any
state = null
}
}
}

0 comments on commit b607f1e

Please sign in to comment.