From 8e36c2915f24e6b3933e728caa93445da8e4adb7 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Tue, 26 Nov 2024 15:45:43 +0100 Subject: [PATCH] chore: make the rest into getters with a check --- packages/snapshot/src/manager.ts | 4 +- packages/vitest/src/node/core.ts | 120 ++++++++++-------- packages/vitest/src/node/watcher.ts | 4 +- .../src/node/workspace/resolveWorkspace.ts | 10 +- 4 files changed, 73 insertions(+), 65 deletions(-) diff --git a/packages/snapshot/src/manager.ts b/packages/snapshot/src/manager.ts index 11ed69847aab..3bf30e22e42e 100644 --- a/packages/snapshot/src/manager.ts +++ b/packages/snapshot/src/manager.ts @@ -6,8 +6,8 @@ import type { import { basename, dirname, isAbsolute, join, resolve } from 'pathe' export class SnapshotManager { - summary: SnapshotSummary = undefined! - extension = '.snap' + public summary!: SnapshotSummary + public extension = '.snap' constructor( public options: Omit, diff --git a/packages/vitest/src/node/core.ts b/packages/vitest/src/node/core.ts index 788af3f012f9..4dca52f8cbf1 100644 --- a/packages/vitest/src/node/core.ts +++ b/packages/vitest/src/node/core.ts @@ -57,45 +57,21 @@ export class Vitest { public readonly watcher: VitestWatcher public readonly distPath = distDir - /** - * @experimental The State API is experimental and not subject to semver. - */ - // TODO: getter - public state: StateManager = undefined! - // TODO: getter - public snapshot: SnapshotManager = undefined! - // TODO: getter - public cache: VitestCache = undefined! - public projects: TestProject[] = [] - /** @internal */ - configOverride: Partial = {} - /** @internal */ - reporters: Reporter[] = undefined! - - coverageProvider: CoverageProvider | null | undefined - pool: ProcessPool | undefined - - vitenode: ViteNodeServer = undefined! - runner: ViteNodeRunner = undefined! - - /** @internal */ - filenamePattern?: string - /** @internal */ - runningPromise?: Promise - /** @internal */ - closingPromise?: Promise - /** @internal */ - isCancelling = false - /** @internal */ - coreWorkspaceProject: TestProject | undefined - /** @internal */ - resolvedProjects: TestProject[] = [] - /** @internal */ - _browserLastPort = defaultBrowserPort - /** @internal */ - _options: UserConfig = {} + /** @internal */ coverageProvider: CoverageProvider | null | undefined + /** @internal */ filenamePattern?: string + /** @internal */ runningPromise?: Promise + /** @internal */ closingPromise?: Promise + /** @internal */ isCancelling = false + /** @internal */ coreWorkspaceProject: TestProject | undefined + /** @internal */ resolvedProjects: TestProject[] = [] + /** @internal */ _browserLastPort = defaultBrowserPort + /** @internal */ _options: UserConfig = {} + /** @internal */ configOverride: Partial = {} + /** @internal */ reporters: Reporter[] = undefined! + /** @internal */ vitenode: ViteNodeServer = undefined! + /** @internal */ runner: ViteNodeRunner = undefined! // TODO: remove in 3.0 private watchedTests: Set = new Set() @@ -103,11 +79,15 @@ export class Vitest { private isFirstRun = true private restartsCount = 0 + private specifications: VitestSpecifications + private pool: ProcessPool | undefined private _ready = false - private _config: ResolvedConfig | undefined - private _vite: ViteDevServer | undefined + private _config?: ResolvedConfig + private _vite?: ViteDevServer + private _state?: StateManager + private _cache?: VitestCache + private _snapshot?: SnapshotManager private _workspaceConfigPath?: string - private specifications: VitestSpecifications constructor( public readonly mode: VitestRunMode, @@ -127,12 +107,12 @@ export class Vitest { private _onCancelListeners: ((reason: CancelReason) => Awaitable)[] = [] private _onUserTestsRerun: OnTestsRerunHandler[] = [] - /** @deprecated will be removed in 3.0 */ + /** @deprecated will be removed in 3.0, use `vitest.watcher` */ public get invalidates() { return this.watcher.invalidates } - /** @deprecated will be removed in 3.0 */ + /** @deprecated will be removed in 3.0, use `vitest.watcher` */ public get changedTests() { return this.watcher.changedTests } @@ -141,9 +121,7 @@ export class Vitest { * The global config. */ get config(): ResolvedConfig { - if (!this._config) { - throw new Error('The config was not set. It means that `vitest.config` was called before the Vite server was established.') - } + assert(this._config, 'config') return this._config } @@ -156,12 +134,35 @@ export class Vitest { * Global Vite's dev server instance. */ get vite(): ViteDevServer { - if (!this._vite) { - throw new Error('The server was not set. It means that `vitest.vite` was called before the Vite server was established.') - } + assert(this._vite, 'vite', 'server') return this._vite } + /** + * The global test state manager. + * @experimental The State API is experimental and not subject to semver. + */ + get state(): StateManager { + assert(this._state, 'state') + return this._state + } + + /** + * The global snapshot manager. You can access the current state on `snapshot.summary`. + */ + get snapshot(): SnapshotManager { + assert(this._snapshot, 'snapshot', 'snapshot manager') + return this._snapshot + } + + /** + * Test results and test file stats cache. Primarily used by the sequencer to order tests. + */ + get cache(): VitestCache { + assert(this._cache, 'cache') + return this._cache + } + /** * Returns whether Vitest was fully initialised. This means that the Vite server was established and the workspace config was resolved. * It's not necessary to call this method unless the instance was created manually via the public API, and the promise was not awaited. @@ -198,9 +199,9 @@ export class Vitest { this._vite = server this._config = resolved - this.state = new StateManager() - this.cache = new VitestCache(this.version) - this.snapshot = new SnapshotManager({ ...resolved.snapshotOptions }) + this._state = new StateManager() + this._cache = new VitestCache(this.version) + this._snapshot = new SnapshotManager({ ...resolved.snapshotOptions }) if (this.config.watch) { this.watcher.registerWatcher() @@ -286,7 +287,10 @@ export class Vitest { } /** @internal */ - _createRootProject() { + _ensureRootProject() { + if (this.coreWorkspaceProject) { + return this.coreWorkspaceProject + } this.coreWorkspaceProject = TestProject._createBasicProject(this) return this.coreWorkspaceProject } @@ -307,9 +311,7 @@ export class Vitest { public getProjectByTaskId(taskId: string): TestProject { const task = this.state.idMap.get(taskId) const projectName = (task as File).projectName || task?.file?.projectName || '' - return this.projects.find(p => p.name === projectName) - || this.getRootTestProject() - || this.projects[0] + return this.getProjectByName(projectName) } public getProjectByName(name: string): TestProject { @@ -359,7 +361,7 @@ export class Vitest { this._workspaceConfigPath = workspaceConfigPath if (!workspaceConfigPath) { - return [this._createRootProject()] + return [this._ensureRootProject()] } const workspaceModule = await this.runner.executeFile(workspaceConfigPath) as { @@ -1135,3 +1137,9 @@ export class Vitest { this._onSetServer.push(fn) } } + +function assert(condition: unknown, property: string, name: string = property): asserts condition { + if (!condition) { + throw new Error(`The ${name} was not set. It means that \`vitest.${property}\` was called before the Vite server was established. Either await the Vitest promise or check that it is initialized with \`vitest.ready()\` before accessing \`vitest.${property}\`.`) + } +} diff --git a/packages/vitest/src/node/watcher.ts b/packages/vitest/src/node/watcher.ts index 3e2b817245c2..3a01e8c69752 100644 --- a/packages/vitest/src/node/watcher.ts +++ b/packages/vitest/src/node/watcher.ts @@ -5,8 +5,8 @@ import { noop, slash } from '@vitest/utils' import mm from 'micromatch' export class VitestWatcher { - public invalidates: Set = new Set() - public changedTests: Set = new Set() + public readonly invalidates: Set = new Set() + public readonly changedTests: Set = new Set() private _onRerun: ((file: string) => void)[] = [] private _onFilterTestFile: ((file: string) => boolean)[] = [] diff --git a/packages/vitest/src/node/workspace/resolveWorkspace.ts b/packages/vitest/src/node/workspace/resolveWorkspace.ts index c7216dae0610..765ce348eb4c 100644 --- a/packages/vitest/src/node/workspace/resolveWorkspace.ts +++ b/packages/vitest/src/node/workspace/resolveWorkspace.ts @@ -78,8 +78,8 @@ export async function resolveWorkspace( for (const path of fileProjects) { // if file leads to the root config, then we can just reuse it because we already initialized it - if (vitest.server.config.configFile === path) { - projectPromises.push(Promise.resolve(vitest._createRootProject())) + if (vitest.vite.config.configFile === path) { + projectPromises.push(Promise.resolve(vitest._ensureRootProject())) continue } @@ -97,7 +97,7 @@ export async function resolveWorkspace( // pretty rare case - the glob didn't match anything and there are no inline configs if (!projectPromises.length) { - return [vitest._createRootProject()] + return [vitest._ensureRootProject()] } const resolvedProjects = await Promise.all(projectPromises) @@ -193,8 +193,8 @@ async function resolveTestProjectConfigs( // if the config is inlined, we can resolve it immediately else if (typeof definition === 'function') { projectsOptions.push(await definition({ - command: vitest.server.config.command, - mode: vitest.server.config.mode, + command: vitest.vite.config.command, + mode: vitest.vite.config.mode, isPreview: false, isSsrBuild: false, }))