diff --git a/packages/vitest/src/index.ts b/packages/vitest/src/index.ts index 1e0a0575ba88..b951f9c3964b 100644 --- a/packages/vitest/src/index.ts +++ b/packages/vitest/src/index.ts @@ -3,8 +3,9 @@ import type { MatcherState, MatchersObject } from './integrations/chai/types' import type { Constructable, InlineConfig } from './types' export { suite, test, describe, it } from './runtime/suite' - export * from './runtime/hooks' + +export { runOnce } from './integrations/run-once' export * from './integrations/chai' export * from './integrations/jest-mock' export * from './integrations/vi' diff --git a/packages/vitest/src/integrations/run-once.ts b/packages/vitest/src/integrations/run-once.ts new file mode 100644 index 000000000000..379e76e6df41 --- /dev/null +++ b/packages/vitest/src/integrations/run-once.ts @@ -0,0 +1,34 @@ +import type { Awaitable } from '../types' +import { getWorkerState } from '../utils' + +const filesCount = new Map() +const cache = new Map() + +/** + * This utils allows computational intensive tasks to only be ran once + * across test reruns to improve the watch mode performance. + * + * Currently only works with `isolate: false` + * + * @experimental + */ +export async function runOnce(fn: (() => Awaitable), key?: string): Promise { + if (!key) { + const filepath = getWorkerState().filepath || '__unknown_files__' + filesCount.set(filepath, (filesCount.get(filepath) || 0) + 1) + const count = filesCount.get(filepath)! + key = `${filepath}:${count}` + } + + if (!cache.has(key)) + cache.set(key, fn()) + + return await cache.get(key) +} + +/** + * @internal + */ +export function resetRunOnceCounter() { + filesCount.clear() +} diff --git a/packages/vitest/src/runtime/setup.ts b/packages/vitest/src/runtime/setup.ts index 0c592597a4d6..03c4a18e35fb 100644 --- a/packages/vitest/src/runtime/setup.ts +++ b/packages/vitest/src/runtime/setup.ts @@ -4,10 +4,13 @@ import { environments } from '../integrations/env' import type { ResolvedConfig } from '../types' import { getWorkerState, toArray } from '../utils' import * as VitestIndex from '../index' +import { resetRunOnceCounter } from '../integrations/run-once' import { rpc } from './rpc' let globalSetup = false export async function setupGlobalEnv(config: ResolvedConfig) { + resetRunOnceCounter() + Object.defineProperty(globalThis, '__vitest_index__', { value: VitestIndex, enumerable: false, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e12cddb61c94..72d454c2a608 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -437,7 +437,7 @@ importers: solid-js: 1.3.12 devDependencies: jsdom: 19.0.0 - solid-start: 0.1.0-alpha.66_solid-js@1.3.12+vite@2.8.6 + solid-start: 0.1.0-alpha.68_solid-js@1.3.12+vite@2.8.6 solid-testing-library: 0.3.0_solid-js@1.3.12 vitest: link:../../packages/vitest @@ -773,6 +773,12 @@ importers: execa: 6.1.0 vitest: link:../../packages/vitest + test/run-once: + specifiers: + vitest: workspace:* + devDependencies: + vitest: link:../../packages/vitest + test/single-thread: specifiers: vitest: workspace:* @@ -17139,8 +17145,8 @@ packages: solid-js: 1.3.12 dev: true - /solid-start-node/0.1.0-alpha.66_0ea384134918338f1c5b6cb6cab097e3: - resolution: {integrity: sha512-K70hiZzTzKmA6RSBYOODFpYKGkzNBwattiFSHBFdhucvQiEHGyP3IsaCs5TKXuav/oX4pWRzE3ssUcPF5B6N7A==} + /solid-start-node/0.1.0-alpha.68_eff5a7601ff8a6e482f1c876291835be: + resolution: {integrity: sha512-/wsB7KXj7JKPbXLI+kNWeWS8i43iRFZbLhzzHIQuT5uLcM1UbVrmoRlsOLHsm0Jo2sD105FltYW/TULAW3Tagw==} requiresBuild: true peerDependencies: solid-start: next @@ -17154,14 +17160,14 @@ packages: polka: 1.0.0-next.22 rollup: 2.70.1 sirv: 1.0.19 - solid-start: 0.1.0-alpha.66_solid-js@1.3.12+vite@2.8.6 + solid-start: 0.1.0-alpha.68_solid-js@1.3.12+vite@2.8.6 undici: 4.13.0 vite: 2.8.6 dev: true optional: true - /solid-start/0.1.0-alpha.66_solid-js@1.3.12+vite@2.8.6: - resolution: {integrity: sha512-HGJUQy2FIxfwQZTFpfx2ibjBoLT8fTCKpGvGwl215RSl7zDve9DFMuSTqRZiay0l4ERbu6uziS+gFg6tk3V0CA==} + /solid-start/0.1.0-alpha.68_solid-js@1.3.12+vite@2.8.6: + resolution: {integrity: sha512-cfpOWolqSlwfQzDEXFLPox9NtlPsg7/1VWPZN4FfwB+eAmnvc4wg78fcrl92mOpITdM1AusqryHibzrErz9ksA==} hasBin: true peerDependencies: solid-app-router: ^0.3.0 @@ -17191,7 +17197,7 @@ packages: vite-plugin-inspect: 0.3.14_vite@2.8.6 vite-plugin-solid: 2.2.6 optionalDependencies: - solid-start-node: 0.1.0-alpha.66_0ea384134918338f1c5b6cb6cab097e3 + solid-start-node: 0.1.0-alpha.68_eff5a7601ff8a6e482f1c876291835be transitivePeerDependencies: - less - sass diff --git a/test/core/vitest.config.ts b/test/core/vitest.config.ts index eef048ef29fd..f444eb55c357 100644 --- a/test/core/vitest.config.ts +++ b/test/core/vitest.config.ts @@ -38,7 +38,6 @@ export default defineConfig({ }, test: { testTimeout: 2000, - // threads: false, setupFiles: [ './test/setup.ts', ], diff --git a/test/run-once/package.json b/test/run-once/package.json new file mode 100644 index 000000000000..d1e3592a76aa --- /dev/null +++ b/test/run-once/package.json @@ -0,0 +1,11 @@ +{ + "name": "@vitest/test-run-once", + "private": true, + "scripts": { + "test": "vitest", + "coverage": "vitest run --coverage" + }, + "devDependencies": { + "vitest": "workspace:*" + } +} diff --git a/test/run-once/test/run-once.test.ts b/test/run-once/test/run-once.test.ts new file mode 100644 index 000000000000..5f80bc7cab20 --- /dev/null +++ b/test/run-once/test/run-once.test.ts @@ -0,0 +1,21 @@ +import { expect, it, runOnce } from 'vitest' + +let dummy = 0 + +const one = await runOnce(() => { + dummy += 1 + return 1 +}) + +const two = await runOnce(async() => { + dummy += 1 + return 2 +}) + +it('run once', async() => { + expect(one).toBe(1) + expect(two).toBe(2) + + // edit the file to trigger the hmr and dummy should be 0 + expect(dummy).toBe(2) +}) diff --git a/test/run-once/vitest.config.ts b/test/run-once/vitest.config.ts new file mode 100644 index 000000000000..9ca84b2b94b0 --- /dev/null +++ b/test/run-once/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' + +export default defineConfig({ + test: { + isolate: false, + }, +})