Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement provide/inject API to transfer data from the main thread #4422

Merged
merged 10 commits into from
Nov 4, 2023
1 change: 1 addition & 0 deletions packages/browser/src/client/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ ws.addEventListener('open', async () => {
environment: 0,
prepare: 0,
},
providedContext: await client.rpc.getProvidedContext(),
}
// @ts-expect-error mocking vitest apis
globalThis.__vitest_mocker__ = new VitestBrowserClientMocker()
Expand Down
4 changes: 4 additions & 0 deletions packages/vitest/src/api/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ export function setup(vitestOrWorkspace: Vitest | WorkspaceProject, server?: Vit
getCountOfFailedTests() {
return ctx.state.getCountOfFailedTests()
},
// browser should have a separate RPC in the future, UI doesn't care for provided context
getProvidedContext() {
return 'ctx' in vitestOrWorkspace ? vitestOrWorkspace.getProvidedContext() : {}
},
},
{
post: msg => ws.send(msg),
Expand Down
3 changes: 2 additions & 1 deletion packages/vitest/src/api/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { TransformResult } from 'vite'
import type { CancelReason } from '@vitest/runner'
import type { AfterSuiteRunMeta, File, ModuleGraphData, Reporter, ResolvedConfig, SnapshotResult, TaskResultPack, UserConsoleLog } from '../types'
import type { AfterSuiteRunMeta, File, ModuleGraphData, ProvidedContext, Reporter, ResolvedConfig, SnapshotResult, TaskResultPack, UserConsoleLog } from '../types'

export interface TransformResultWithSource extends TransformResult {
source?: string
Expand Down Expand Up @@ -30,6 +30,7 @@ export interface WebSocketHandlers {
snapshotSaved(snapshot: SnapshotResult): void
rerun(files: string[]): Promise<void>
updateSnapshot(file?: File): Promise<void>
getProvidedContext(): ProvidedContext
}

export interface WebSocketEvents extends Pick<Reporter, 'onCollected' | 'onFinished' | 'onTaskUpdate' | 'onUserConsoleLog' | 'onPathsCollected'> {
Expand Down
12 changes: 11 additions & 1 deletion packages/vitest/src/integrations/vi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { FakeTimerInstallOpts } from '@sinonjs/fake-timers'
import { assertTypes, createSimpleStackTrace } from '@vitest/utils'
import { parseSingleStack } from '../utils/source-map'
import type { VitestMocker } from '../runtime/mocker'
import type { ResolvedConfig, RuntimeConfig } from '../types'
import type { ProvidedContext, ResolvedConfig, RuntimeConfig } from '../types'
import type { MockFactoryWithHelper } from '../types/mocker'
import { getWorkerState } from '../utils/global'
import { resetModules, waitForImportsToResolve } from '../utils/modules'
Expand Down Expand Up @@ -333,6 +333,12 @@ export interface VitestUtils {
* If config was changed with `vi.setConfig`, this will reset it to the original state.
*/
resetConfig(): void

/**
* Access to injected context provided from the main thread.
* This usually returns a value provided by `globalSetup` or an external library.
*/
inject<T extends keyof ProvidedContext>(key: T): ProvidedContext[T]
}

function createVitest(): VitestUtils {
Expand Down Expand Up @@ -609,6 +615,10 @@ function createVitest(): VitestUtils {
Object.assign(state.config, _config)
}
},

inject<T extends keyof ProvidedContext>(key: T): ProvidedContext[T] {
return workerState.providedContext[key]
},
}

return utils
Expand Down
9 changes: 8 additions & 1 deletion packages/vitest/src/node/globalSetup.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import { toArray } from '@vitest/utils'
import type { ViteNodeRunner } from 'vite-node/client'
import type { ProvidedContext } from '../types/general'
import type { ResolvedConfig } from '../types/config'

interface GlobalSetupUtilities {
config: ResolvedConfig
provide<T extends keyof ProvidedContext>(key: T, value: ProvidedContext[T]): void
}

export interface GlobalSetupFile {
file: string
setup?: () => Promise<Function | void> | void
setup?: (utilities: GlobalSetupUtilities) => Promise<Function | void> | void
teardown?: Function
}

Expand Down
1 change: 1 addition & 0 deletions packages/vitest/src/node/pools/child.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ export function createChildProcessPool(ctx: Vitest, { execArgv, env, forksPath }
environment,
workerId,
projectName: project.getName(),
providedContext: project.getProvidedContext(),
}
try {
await pool.run(data, { name, channel })
Expand Down
1 change: 1 addition & 0 deletions packages/vitest/src/node/pools/threads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export function createThreadsPool(ctx: Vitest, { execArgv, env, workerPath }: Po
environment,
workerId,
projectName: project.getName(),
providedContext: project.getProvidedContext(),
}
try {
await pool.run(data, { transferList: [workerPort], name })
Expand Down
1 change: 1 addition & 0 deletions packages/vitest/src/node/pools/vm-threads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export function createVmThreadsPool(ctx: Vitest, { execArgv, env, vmPath }: Pool
environment,
workerId,
projectName: project.getName(),
providedContext: project.getProvidedContext(),
}
try {
await pool.run(data, { transferList: [workerPort], name })
Expand Down
14 changes: 12 additions & 2 deletions packages/vitest/src/node/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ViteNodeServer } from 'vite-node/server'
import c from 'picocolors'
import type { RawSourceMap } from 'vite-node'
import { createBrowserServer } from '../integrations/browser/server'
import type { ArgumentsType, Reporter, ResolvedConfig, UserConfig, UserWorkspaceConfig, Vitest } from '../types'
import type { ArgumentsType, ProvidedContext, Reporter, ResolvedConfig, UserConfig, UserWorkspaceConfig, Vitest } from '../types'
import { deepMerge } from '../utils'
import type { Typechecker } from '../typecheck/typechecker'
import type { BrowserProvider } from '../types/browser'
Expand Down Expand Up @@ -71,6 +71,7 @@ export class WorkspaceProject {

private _globalSetupInit = false
private _globalSetups: GlobalSetupFile[] = []
private _provided: ProvidedContext = {}

constructor(
public path: string | number,
Expand All @@ -85,6 +86,14 @@ export class WorkspaceProject {
return this.ctx.getCoreWorkspaceProject() === this
}

provide = (key: string, value: unknown) => {
sheremet-va marked this conversation as resolved.
Show resolved Hide resolved
(this._provided as any)[key] = value
}

getProvidedContext() {
return this._provided
}

async initializeGlobalSetup() {
if (this._globalSetupInit)
return
Expand All @@ -95,7 +104,7 @@ export class WorkspaceProject {

try {
for (const globalSetupFile of this._globalSetups) {
const teardown = await globalSetupFile.setup?.()
const teardown = await globalSetupFile.setup?.({ provide: this.provide, config: this.config })
sheremet-va marked this conversation as resolved.
Show resolved Hide resolved
if (teardown == null || !!globalSetupFile.teardown)
continue
if (typeof teardown !== 'function')
Expand Down Expand Up @@ -339,6 +348,7 @@ export class WorkspaceProject {
this.typechecker?.stop(),
this.browser?.close(),
this.teardownGlobalSetup(),
() => this._provided = {},
].filter(Boolean))
}
return this.closingPromise
Expand Down
3 changes: 2 additions & 1 deletion packages/vitest/src/runtime/child.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { createSafeRpc, rpcDone } from './rpc'
import { setupInspect } from './inspector'

async function init(ctx: ChildContext) {
const { config, workerId } = ctx
const { config, workerId, providedContext } = ctx

process.env.VITEST_WORKER_ID = String(workerId)
process.env.VITEST_POOL_ID = String(poolId)
Expand Down Expand Up @@ -72,6 +72,7 @@ async function init(ctx: ChildContext) {
prepare: performance.now(),
},
rpc,
providedContext,
isChildProcess: true,
}

Expand Down
3 changes: 2 additions & 1 deletion packages/vitest/src/runtime/vm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const entryFile = pathToFileURL(resolve(distDir, 'entry-vm.js')).href
export async function run(ctx: WorkerContext) {
const moduleCache = new ModuleCacheMap()
const mockMap = new Map()
const { config, port } = ctx
const { config, port, providedContext } = ctx

let setCancel = (_reason: CancelReason) => {}
const onCancel = new Promise<CancelReason>((resolve) => {
Expand Down Expand Up @@ -64,6 +64,7 @@ export async function run(ctx: WorkerContext) {
prepare: performance.now(),
},
rpc,
providedContext,
}

installSourcemapsSupport({
Expand Down
3 changes: 2 additions & 1 deletion packages/vitest/src/runtime/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ async function init(ctx: WorkerContext) {
if (isInitialized && isIsolatedThreads)
throw new Error(`worker for ${ctx.files.join(',')} already initialized by ${getWorkerState().ctx.files.join(',')}. This is probably an internal bug of Vitest.`)

const { config, port, workerId } = ctx
const { config, port, workerId, providedContext } = ctx

process.env.VITEST_WORKER_ID = String(workerId)
process.env.VITEST_POOL_ID = String(poolId)
Expand Down Expand Up @@ -58,6 +58,7 @@ async function init(ctx: WorkerContext) {
prepare: performance.now(),
},
rpc,
providedContext,
}

Object.defineProperty(globalThis, '__vitest_worker__', {
Expand Down
2 changes: 2 additions & 0 deletions packages/vitest/src/types/general.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,5 @@ export interface ModuleGraphData {
}

export type OnServerRestartHandler = (reason?: string) => Promise<void> | void

export interface ProvidedContext {}
1 change: 1 addition & 0 deletions packages/vitest/src/types/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,5 @@ export interface ContextRPC {
files: string[]
invalidates?: string[]
environment: ContextTestEnvironment
providedContext: Record<string, any>
}
1 change: 1 addition & 0 deletions packages/vitest/src/types/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export interface WorkerGlobalState {
onCancel: Promise<CancelReason>
moduleCache: ModuleCacheMap
mockMap: MockMap
providedContext: Record<string, any>
durations: {
environment: number
prepare: number
Expand Down