Skip to content

Commit dbac2c8

Browse files
committed
chore: cache specs
1 parent b477aa3 commit dbac2c8

11 files changed

Lines changed: 106 additions & 64 deletions

File tree

packages/browser/src/node/plugin.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ export default (browserServer: BrowserServer, base = '/'): Plugin[] => {
157157
name: 'vitest:browser:tests',
158158
enforce: 'pre',
159159
async config() {
160-
const allTestFiles = await project.globTestFiles()
160+
const { testFiles: allTestFiles } = await project.globTestFiles()
161161
const browserTestFiles = allTestFiles.filter(
162162
file => getFilePoolName(project, file) === 'browser',
163163
)

packages/browser/src/node/pool.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as nodeos from 'node:os'
22
import crypto from 'node:crypto'
33
import { relative } from 'pathe'
4-
import type { BrowserProvider, ProcessPool, Vitest, WorkspaceProject } from 'vitest/node'
4+
import type { BrowserProvider, ProcessPool, Vitest, WorkspaceProject, WorkspaceSpec } from 'vitest/node'
55
import { createDebugger } from 'vitest/node'
66

77
const debug = createDebugger('vitest:browser:pool')
@@ -92,7 +92,7 @@ export function createBrowserPool(ctx: Vitest): ProcessPool {
9292
await Promise.all(promises)
9393
}
9494

95-
const runWorkspaceTests = async (method: 'run' | 'collect', specs: [WorkspaceProject, string][]) => {
95+
const runWorkspaceTests = async (method: 'run' | 'collect', specs: WorkspaceSpec[]) => {
9696
const groupedFiles = new Map<WorkspaceProject, string[]>()
9797
for (const [project, file] of specs) {
9898
const files = groupedFiles.get(project) || []

packages/browser/src/node/serverTester.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export async function resolveTester(
2222
const { contextId, testFile } = server.resolveTesterUrl(url.pathname)
2323
const project = server.project
2424
const state = server.state
25-
const testFiles = await project.globTestFiles()
25+
const { testFiles } = await project.globTestFiles()
2626
// if decoded test file is "__vitest_all__" or not in the list of known files, run all tests
2727
const tests
2828
= testFile === '__vitest_all__'

packages/vitest/src/api/setup.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,13 @@ export function setup(ctx: Vitest, _server?: ViteDevServer) {
103103
},
104104
async getTestFiles() {
105105
const spec = await ctx.globTestFiles()
106-
return spec.map(([project, file]) => [
106+
return spec.map(([project, file, options]) => [
107107
{
108108
name: project.config.name,
109109
root: project.config.root,
110110
},
111111
file,
112+
options,
112113
])
113114
},
114115
},

packages/vitest/src/node/core.ts

Lines changed: 65 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { WebSocketReporter } from '../api/setup'
1717
import type { SerializedCoverageConfig } from '../runtime/config'
1818
import type { SerializedSpec } from '../runtime/types/utils'
1919
import type { ArgumentsType, OnServerRestartHandler, ProvidedContext, UserConsoleLog } from '../types/general'
20-
import { createPool } from './pool'
20+
import { createPool, getFilePoolName } from './pool'
2121
import type { ProcessPool, WorkspaceSpec } from './pool'
2222
import { createBenchmarkReporters, createReporters } from './reporters/utils'
2323
import { StateManager } from './state'
@@ -77,10 +77,14 @@ export class Vitest {
7777

7878
private resolvedProjects: WorkspaceProject[] = []
7979
public projects: WorkspaceProject[] = []
80-
private projectsTestFiles = new Map<string, Set<WorkspaceProject>>()
8180

8281
public distPath!: string
8382

83+
private _cachedSpecs = new Map<string, WorkspaceSpec[]>()
84+
85+
/** @deprecated use `_cachedSpecs` */
86+
projectTestFiles = this._cachedSpecs
87+
8488
constructor(
8589
public readonly mode: VitestRunMode,
8690
options: VitestOptions = {},
@@ -103,7 +107,7 @@ export class Vitest {
103107
this.coverageProvider = undefined
104108
this.runningPromise = undefined
105109
this.distPath = undefined!
106-
this.projectsTestFiles.clear()
110+
this._cachedSpecs.clear()
107111

108112
const resolved = resolveConfig(this.mode, options, server.config, this.logger)
109113

@@ -202,6 +206,9 @@ export class Vitest {
202206
return this.coreWorkspaceProject
203207
}
204208

209+
/**
210+
* @deprecated use Reported Task API instead
211+
*/
205212
public getProjectByTaskId(taskId: string): WorkspaceProject {
206213
const task = this.state.idMap.get(taskId)
207214
const projectName = (task as File).projectName || task?.file?.projectName || ''
@@ -216,7 +223,7 @@ export class Vitest {
216223
|| this.projects[0]
217224
}
218225

219-
private async getWorkspaceConfigPath() {
226+
private async getWorkspaceConfigPath(): Promise<string | null> {
220227
if (this.config.workspace) {
221228
return this.config.workspace
222229
}
@@ -423,8 +430,8 @@ export class Vitest {
423430
}
424431
}
425432

426-
private async getTestDependencies(filepath: WorkspaceSpec, deps = new Set<string>()) {
427-
const addImports = async ([project, filepath]: WorkspaceSpec) => {
433+
private async getTestDependencies([project, filepath]: WorkspaceSpec, deps = new Set<string>()) {
434+
const addImports = async (project: WorkspaceProject, filepath: string) => {
428435
if (deps.has(filepath)) {
429436
return
430437
}
@@ -440,13 +447,13 @@ export class Vitest {
440447
const path = await project.server.pluginContainer.resolveId(dep, filepath, { ssr: true })
441448
const fsPath = path && !path.external && path.id.split('?')[0]
442449
if (fsPath && !fsPath.includes('node_modules') && !deps.has(fsPath) && existsSync(fsPath)) {
443-
await addImports([project, fsPath])
450+
await addImports(project, fsPath)
444451
}
445452
}))
446453
}
447454

448-
await addImports(filepath)
449-
deps.delete(filepath[1])
455+
await addImports(project, filepath)
456+
deps.delete(filepath)
450457

451458
return deps
452459
}
@@ -500,12 +507,31 @@ export class Vitest {
500507
return runningTests
501508
}
502509

510+
/**
511+
* @deprecated remove when vscode extension supports "getFileWorkspaceSpecs"
512+
*/
503513
getProjectsByTestFile(file: string) {
504-
const projects = this.projectsTestFiles.get(file)
505-
if (!projects) {
506-
return []
514+
return this.getFileWorkspaceSpecs(file)
515+
}
516+
517+
getFileWorkspaceSpecs(file: string) {
518+
const _cached = this._cachedSpecs.get(file)
519+
if (_cached) {
520+
return _cached
507521
}
508-
return Array.from(projects).map(project => [project, file] as WorkspaceSpec)
522+
523+
const specs: WorkspaceSpec[] = []
524+
for (const project of this.projects) {
525+
if (project.isTestFile(file)) {
526+
const pool = getFilePoolName(project, file)
527+
specs.push([project, file, { pool }])
528+
}
529+
if (project.isTypecheckFile(file)) {
530+
specs.push([project, file, { pool: 'typescript' }])
531+
}
532+
}
533+
specs.forEach(spec => this.ensureSpecCached(spec))
534+
return specs
509535
}
510536

511537
async initializeGlobalSetup(paths: WorkspaceSpec[]) {
@@ -538,8 +564,11 @@ export class Vitest {
538564

539565
await this.report('onPathsCollected', filepaths)
540566
await this.report('onSpecsCollected', specs.map(
541-
([project, file]) =>
542-
[{ name: project.config.name, root: project.config.root }, file] as SerializedSpec,
567+
([project, file, options]) =>
568+
[{
569+
name: project.config.name,
570+
root: project.config.root,
571+
}, file, options] satisfies SerializedSpec,
543572
))
544573

545574
// previous run
@@ -856,7 +885,6 @@ export class Vitest {
856885
}))
857886

858887
if (matchingProjects.length > 0) {
859-
this.projectsTestFiles.set(id, new Set(matchingProjects))
860888
this.changedTests.add(id)
861889
this.scheduleRerun([id])
862890
}
@@ -1054,17 +1082,32 @@ export class Vitest {
10541082
public async globTestFiles(filters: string[] = []) {
10551083
const files: WorkspaceSpec[] = []
10561084
await Promise.all(this.projects.map(async (project) => {
1057-
const specs = await project.globTestFiles(filters)
1058-
specs.forEach((file) => {
1059-
files.push([project, file])
1060-
const projects = this.projectsTestFiles.get(file) || new Set()
1061-
projects.add(project)
1062-
this.projectsTestFiles.set(file, projects)
1085+
const { testFiles, typecheckTestFiles } = await project.globTestFiles(filters)
1086+
testFiles.forEach((file) => {
1087+
const pool = getFilePoolName(project, file)
1088+
const spec: WorkspaceSpec = [project, file, { pool }]
1089+
this.ensureSpecCached(spec)
1090+
files.push(spec)
1091+
})
1092+
typecheckTestFiles.forEach((file) => {
1093+
const spec: WorkspaceSpec = [project, file, { pool: 'typecheck' }]
1094+
this.ensureSpecCached(spec)
1095+
files.push(spec)
10631096
})
10641097
}))
10651098
return files
10661099
}
10671100

1101+
private ensureSpecCached(spec: WorkspaceSpec) {
1102+
const file = spec[1]
1103+
const specs = this._cachedSpecs.get(file) || []
1104+
const included = specs.some(_s => _s[0] === spec[0] && _s[2].pool === spec[2].pool)
1105+
if (!included) {
1106+
specs.push(spec)
1107+
this._cachedSpecs.set(file, specs)
1108+
}
1109+
}
1110+
10681111
// The server needs to be running for communication
10691112
shouldKeepServer() {
10701113
return !!this.config?.watch

packages/vitest/src/node/pool.ts

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import type { WorkspaceProject } from './workspace'
1010
import { createTypecheckPool } from './pools/typecheck'
1111
import { createVmForksPool } from './pools/vmForks'
1212

13-
export type WorkspaceSpec = [project: WorkspaceProject, testFile: string]
13+
export type WorkspaceSpec = [project: WorkspaceProject, testFile: string, options: { pool: Pool }]
1414
export type RunWithFiles = (
1515
files: WorkspaceSpec[],
1616
invalidates?: string[]
@@ -39,16 +39,11 @@ export const builtinPools: BuiltinPool[] = [
3939
'typescript',
4040
]
4141

42-
function getDefaultPoolName(project: WorkspaceProject, file: string): Pool | null {
43-
for (const glob of project.config.include) {
44-
if (mm.isMatch(file, glob, { cwd: project.config.root, ignore: project.config.exclude })) {
45-
if (project.config.browser.enabled) {
46-
return 'browser'
47-
}
48-
return project.config.pool
49-
}
42+
function getDefaultPoolName(project: WorkspaceProject): Pool {
43+
if (project.config.browser.enabled) {
44+
return 'browser'
5045
}
51-
return null
46+
return project.config.pool
5247
}
5348

5449
export function getFilePoolName(project: WorkspaceProject, file: string) {
@@ -62,7 +57,7 @@ export function getFilePoolName(project: WorkspaceProject, file: string) {
6257
return pool as Pool
6358
}
6459
}
65-
return getDefaultPoolName(project, file)
60+
return getDefaultPoolName(project)
6661
}
6762

6863
export function createPool(ctx: Vitest): ProcessPool {
@@ -170,21 +165,9 @@ export function createPool(ctx: Vitest): ProcessPool {
170165
}
171166

172167
for (const spec of files) {
173-
const [project, file] = spec
174-
const pool = getFilePoolName(project, file)
175-
if (pool != null) {
176-
filesByPool[pool] ??= []
177-
filesByPool[pool].push(spec)
178-
}
179-
180-
if (project.config.typecheck.enabled) {
181-
for (const glob of project.config.typecheck.include) {
182-
if (mm.isMatch(file, glob, { cwd: project.config.root, ignore: project.config.typecheck.exclude })) {
183-
filesByPool.typescript ??= []
184-
filesByPool.typescript.push(spec)
185-
}
186-
}
187-
}
168+
const { pool } = spec[2]
169+
filesByPool[pool] ??= []
170+
filesByPool[pool].push(spec)
188171
}
189172

190173
const Sequencer = ctx.config.sequence.sequencer

packages/vitest/src/node/reporters/renderers/utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,6 @@ export function formatProjectName(name: string | undefined, suffix = ' ') {
254254
const index = name
255255
.split('')
256256
.reduce((acc, v, idx) => acc + v.charCodeAt(0) + idx, 0)
257-
const colors = [c.blue, c.yellow, c.cyan, c.green, c.magenta]
258-
return colors[index % colors.length](`|${name}|`) + suffix
257+
const colors = [c.bgBlue, c.bgYellow, c.bgCyan, c.bgGreen, c.bgMagenta]
258+
return colors[index % colors.length](` ${c.white(name)} `) + suffix
259259
}

packages/vitest/src/node/workspace.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ export class WorkspaceProject {
9797
closingPromise: Promise<unknown> | undefined
9898

9999
testFilesList: string[] | null = null
100+
typecheckFilesList: string[] | null = null
100101

101102
public testProject!: TestProject
102103

@@ -225,15 +226,24 @@ export class WorkspaceProject {
225226
? []
226227
: this.globAllTestFiles(include, exclude, includeSource, dir),
227228
typecheck.enabled
228-
? this.globFiles(typecheck.include, typecheck.exclude, dir)
229+
? (this.typecheckFilesList || this.globFiles(typecheck.include, typecheck.exclude, dir))
229230
: [],
230231
])
231232

232-
return this.filterFiles(
233-
[...testFiles, ...typecheckTestFiles],
234-
filters,
235-
dir,
236-
)
233+
this.typecheckFilesList = typecheckTestFiles
234+
235+
return {
236+
testFiles: this.filterFiles(
237+
testFiles,
238+
filters,
239+
dir,
240+
),
241+
typecheckTestFiles: this.filterFiles(
242+
typecheckTestFiles,
243+
filters,
244+
dir,
245+
),
246+
}
237247
}
238248

239249
async globAllTestFiles(
@@ -275,6 +285,10 @@ export class WorkspaceProject {
275285
return this.testFilesList && this.testFilesList.includes(id)
276286
}
277287

288+
isTypecheckFile(id: string) {
289+
return this.typecheckFilesList && this.typecheckFilesList.includes(id)
290+
}
291+
278292
async globFiles(include: string[], exclude: string[], cwd: string) {
279293
const globOptions: fg.Options = {
280294
dot: true,
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export type SerializedSpec = [
22
project: { name: string | undefined; root: string },
33
file: string,
4+
options: { pool: string },
45
]

packages/vitest/src/utils/test-helpers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { promises as fs } from 'node:fs'
22
import mm from 'micromatch'
3-
import type { WorkspaceProject } from '../node/workspace'
43
import type { EnvironmentOptions, TransformModePatterns, VitestEnvironment } from '../node/types/config'
54
import type { ContextTestEnvironment } from '../types/worker'
5+
import type { WorkspaceSpec } from '../node/pool'
66
import { groupBy } from './base'
77

88
export const envsOrder = ['node', 'jsdom', 'happy-dom', 'edge-runtime']
@@ -27,7 +27,7 @@ function getTransformMode(
2727
}
2828

2929
export async function groupFilesByEnv(
30-
files: (readonly [WorkspaceProject, string])[],
30+
files: Array<WorkspaceSpec>,
3131
) {
3232
const filesWithEnv = await Promise.all(
3333
files.map(async ([project, file]) => {

0 commit comments

Comments
 (0)