diff --git a/package.json b/package.json index 6a0bd897cbd5..c69f34c2d09b 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "scripts": { "build": "pnpm -r --filter {packages} run build", "ci": "ni && nr typecheck && nr lint && nr build && nr test:all", - "dev": "pnpm -r --parallel --filter vitest --filter ui run dev", + "dev": "pnpm -r --parallel --filter vitest --filter ui --filter web-worker run dev", "docs": "npm -C docs run dev", "docs:build": "npm -C docs run build", "docs:serve": "npm -C docs run serve", diff --git a/packages/ui/client/auto-imports.d.ts b/packages/ui/client/auto-imports.d.ts index 5af89ead92ee..674b46219e36 100644 --- a/packages/ui/client/auto-imports.d.ts +++ b/packages/ui/client/auto-imports.d.ts @@ -3,9 +3,11 @@ declare global { const asyncComputed: typeof import('@vueuse/core')['asyncComputed'] const autoResetRef: typeof import('@vueuse/core')['autoResetRef'] - const biSyncRef: typeof import('@vueuse/core')['biSyncRef'] const computed: typeof import('vue')['computed'] + const computedAsync: typeof import('@vueuse/core')['computedAsync'] + const computedEager: typeof import('@vueuse/core')['computedEager'] const computedInject: typeof import('@vueuse/core')['computedInject'] + const computedWithControl: typeof import('@vueuse/core')['computedWithControl'] const controlledComputed: typeof import('@vueuse/core')['controlledComputed'] const controlledRef: typeof import('@vueuse/core')['controlledRef'] const createApp: typeof import('vue')['createApp'] @@ -31,9 +33,13 @@ declare global { const isDefined: typeof import('@vueuse/core')['isDefined'] const isReadonly: typeof import('vue')['isReadonly'] const isRef: typeof import('vue')['isRef'] + const logicAnd: typeof import('@vueuse/core')['logicAnd'] + const logicNot: typeof import('@vueuse/core')['logicNot'] + const logicOr: typeof import('@vueuse/core')['logicOr'] const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable'] const markRaw: typeof import('vue')['markRaw'] const nextTick: typeof import('vue')['nextTick'] + const note: typeof import('@vueuse/core')['note'] const onActivated: typeof import('vue')['onActivated'] const onBeforeMount: typeof import('vue')['onBeforeMount'] const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] @@ -61,12 +67,17 @@ declare global { const reactivePick: typeof import('@vueuse/core')['reactivePick'] const readonly: typeof import('vue')['readonly'] const ref: typeof import('vue')['ref'] + const refAutoReset: typeof import('@vueuse/core')['refAutoReset'] + const refDebounced: typeof import('@vueuse/core')['refDebounced'] const refDefault: typeof import('@vueuse/core')['refDefault'] + const refThrottled: typeof import('@vueuse/core')['refThrottled'] + const refWithControl: typeof import('@vueuse/core')['refWithControl'] const resolveComponent: typeof import('vue')['resolveComponent'] const shallowReactive: typeof import('vue')['shallowReactive'] const shallowReadonly: typeof import('vue')['shallowReadonly'] const shallowRef: typeof import('vue')['shallowRef'] const syncRef: typeof import('@vueuse/core')['syncRef'] + const syncRefs: typeof import('@vueuse/core')['syncRefs'] const templateRef: typeof import('@vueuse/core')['templateRef'] const throttledRef: typeof import('@vueuse/core')['throttledRef'] const throttledWatch: typeof import('@vueuse/core')['throttledWatch'] @@ -102,6 +113,7 @@ declare global { const useCssVars: typeof import('vue')['useCssVars'] const useCycleList: typeof import('@vueuse/core')['useCycleList'] const useDark: typeof import('@vueuse/core')['useDark'] + const useDateFormat: typeof import('@vueuse/core')['useDateFormat'] const useDebounce: typeof import('@vueuse/core')['useDebounce'] const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory'] const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn'] @@ -123,10 +135,12 @@ declare global { const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper'] const useFavicon: typeof import('@vueuse/core')['useFavicon'] const useFetch: typeof import('@vueuse/core')['useFetch'] + const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess'] const useFocus: typeof import('@vueuse/core')['useFocus'] const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin'] const useFps: typeof import('@vueuse/core')['useFps'] const useFullscreen: typeof import('@vueuse/core')['useFullscreen'] + const useGamepad: typeof import('@vueuse/core')['useGamepad'] const useGeolocation: typeof import('@vueuse/core')['useGeolocation'] const useIdle: typeof import('@vueuse/core')['useIdle'] const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll'] @@ -186,6 +200,7 @@ declare global { const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo'] const useTimeout: typeof import('@vueuse/core')['useTimeout'] const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn'] + const useTimeoutPoll: typeof import('@vueuse/core')['useTimeoutPoll'] const useTimestamp: typeof import('@vueuse/core')['useTimestamp'] const useTitle: typeof import('@vueuse/core')['useTitle'] const useToggle: typeof import('@vueuse/core')['useToggle'] @@ -206,8 +221,12 @@ declare global { const useWindowSize: typeof import('@vueuse/core')['useWindowSize'] const watch: typeof import('vue')['watch'] const watchAtMost: typeof import('@vueuse/core')['watchAtMost'] + const watchDebounced: typeof import('@vueuse/core')['watchDebounced'] const watchEffect: typeof import('vue')['watchEffect'] + const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable'] const watchOnce: typeof import('@vueuse/core')['watchOnce'] + const watchPausable: typeof import('@vueuse/core')['watchPausable'] + const watchThrottled: typeof import('@vueuse/core')['watchThrottled'] const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter'] const whenever: typeof import('@vueuse/core')['whenever'] } diff --git a/packages/vite-node/src/client.ts b/packages/vite-node/src/client.ts index f0d845b53b33..d248e840cb0e 100644 --- a/packages/vite-node/src/client.ts +++ b/packages/vite-node/src/client.ts @@ -19,14 +19,47 @@ export const DEFAULT_REQUEST_STUBS = { }, } +export class ModuleCacheMap extends Map { + normalizePath(fsPath: string) { + return fsPath + .replace(/\\/g, '/') + .replace(/^\/@fs\//, '/') + .replace(/^file:\//, '/') + .replace(/^\/+/, '/') + } + + set(fsPath: string, mod: Partial) { + fsPath = this.normalizePath(fsPath) + if (!super.has(fsPath)) + super.set(fsPath, mod) + else + Object.assign(super.get(fsPath), mod) + return this + } + + get(fsPath: string) { + fsPath = this.normalizePath(fsPath) + return super.get(fsPath) + } + + delete(fsPath: string) { + fsPath = this.normalizePath(fsPath) + return super.delete(fsPath) + } +} + export class ViteNodeRunner { root: string - moduleCache: Map + /** + * Holds the cache of modules + * Keys of the map are filepaths, or plain package names + */ + moduleCache: ModuleCacheMap constructor(public options: ViteNodeRunnerOptions) { this.root = options.root || process.cwd() - this.moduleCache = options.moduleCache || new Map() + this.moduleCache = options.moduleCache || new ModuleCacheMap() } async executeFile(file: string) { @@ -39,13 +72,13 @@ export class ViteNodeRunner { async cachedRequest(rawId: string, callstack: string[]) { const id = normalizeId(rawId, this.options.base) + const fsPath = toFilePath(id, this.root) - if (this.moduleCache.get(id)?.promise) - return this.moduleCache.get(id)?.promise + if (this.moduleCache.get(fsPath)?.promise) + return this.moduleCache.get(fsPath)?.promise - const fsPath = toFilePath(id, this.root) const promise = this.directRequest(id, fsPath, callstack) - this.setCache(id, { promise }) + this.moduleCache.set(fsPath, { promise }) return await promise } @@ -75,7 +108,7 @@ export class ViteNodeRunner { const { code: transformed, externalize } = await this.options.fetchModule(id) if (externalize) { const mod = await this.interopedImport(externalize) - this.setCache(id, { exports: mod }) + this.moduleCache.set(fsPath, { exports: mod }) return mod } @@ -87,7 +120,7 @@ export class ViteNodeRunner { const exports: any = Object.create(null) exports[Symbol.toStringTag] = 'Module' - this.setCache(id, { code: transformed, exports }) + this.moduleCache.set(id, { code: transformed, exports }) const __filename = fileURLToPath(url) const moduleProxy = { @@ -135,13 +168,6 @@ export class ViteNodeRunner { return context } - setCache(id: string, mod: Partial) { - if (!this.moduleCache.has(id)) - this.moduleCache.set(id, mod) - else - Object.assign(this.moduleCache.get(id), mod) - } - shouldResolveId(dep: string) { if (isNodeBuiltin(dep) || dep in (this.options.requestStubs || DEFAULT_REQUEST_STUBS)) return false diff --git a/packages/vite-node/src/types.ts b/packages/vite-node/src/types.ts index 9bfc1e47c83e..92a15e6c2d8c 100644 --- a/packages/vite-node/src/types.ts +++ b/packages/vite-node/src/types.ts @@ -1,3 +1,5 @@ +import type { ModuleCacheMap } from './client' + export interface DepsHandlingOptions { external?: (string | RegExp)[] inline?: (string | RegExp)[] @@ -42,7 +44,7 @@ export interface ViteNodeRunnerOptions { fetchModule: FetchFunction resolveId?: ResolveIdFunction base?: string - moduleCache?: Map + moduleCache?: ModuleCacheMap interopDefault?: boolean requestStubs?: Record } @@ -73,3 +75,5 @@ export interface ViteNodeServerOptions { web?: RegExp[] } } + +export type { ModuleCacheMap } diff --git a/packages/vitest/src/runtime/execute.ts b/packages/vitest/src/runtime/execute.ts index bf3ecf99b220..ddf812ac4e03 100644 --- a/packages/vitest/src/runtime/execute.ts +++ b/packages/vitest/src/runtime/execute.ts @@ -37,7 +37,7 @@ export class VitestRunner extends ViteNodeRunner { const mocker = this.mocker.withRequest(request) mocker.on('mocked', (dep: string, module: Partial) => { - this.setCache(dep, module) + this.moduleCache.set(dep, module) }) const workerState = getWorkerState() diff --git a/packages/vitest/src/runtime/mocker.ts b/packages/vitest/src/runtime/mocker.ts index 39686cacb18f..4f2c4d6c476f 100644 --- a/packages/vitest/src/runtime/mocker.ts +++ b/packages/vitest/src/runtime/mocker.ts @@ -1,8 +1,8 @@ import { existsSync, readdirSync } from 'fs' import { isNodeBuiltin } from 'mlly' import { basename, dirname, resolve } from 'pathe' -import type { ModuleCache } from 'vite-node' import { toFilePath } from 'vite-node/utils' +import type { ModuleCacheMap } from 'vite-node/client' import { getWorkerState, isWindows, mergeSlashes, normalizeId } from '../utils' import { distDir } from '../constants' import type { PendingSuiteMock } from '../types/mocker' @@ -43,7 +43,7 @@ export class VitestMocker { constructor( public options: ExecuteOptions, - private moduleCache: Map, + private moduleCache: ModuleCacheMap, request?: (dep: string) => unknown, ) { this.root = this.options.root diff --git a/packages/vitest/src/runtime/worker.ts b/packages/vitest/src/runtime/worker.ts index 4ad38dec5b4c..acbae1023e42 100644 --- a/packages/vitest/src/runtime/worker.ts +++ b/packages/vitest/src/runtime/worker.ts @@ -1,6 +1,7 @@ import { resolve } from 'pathe' import { createBirpc } from 'birpc' -import type { ModuleCache, ResolvedConfig, WorkerContext, WorkerRPC } from '../types' +import { ModuleCacheMap } from 'vite-node/client' +import type { ResolvedConfig, WorkerContext, WorkerRPC } from '../types' import { distDir } from '../constants' import { getWorkerState } from '../utils' import { executeInViteNode } from './execute' @@ -11,7 +12,7 @@ let _viteNode: { collect: (files: string[], config: ResolvedConfig) => Promise } -const moduleCache: Map = new Map() +const moduleCache = new ModuleCacheMap() const mockMap = {} async function startViteNode(ctx: WorkerContext) { diff --git a/packages/vitest/src/types/worker.ts b/packages/vitest/src/types/worker.ts index 303ecdcc8fd6..578427741768 100644 --- a/packages/vitest/src/types/worker.ts +++ b/packages/vitest/src/types/worker.ts @@ -1,5 +1,5 @@ import type { MessagePort } from 'worker_threads' -import type { FetchFunction, ModuleCache, RawSourceMap, ViteNodeResolveId } from 'vite-node' +import type { FetchFunction, ModuleCacheMap, RawSourceMap, ViteNodeResolveId } from 'vite-node' import type { BirpcReturn } from 'birpc' import type { SuiteMocks } from './mocker' import type { ResolvedConfig } from './config' @@ -37,6 +37,6 @@ export interface WorkerGlobalState { rpc: BirpcReturn current?: Test filepath?: string - moduleCache: Map + moduleCache: ModuleCacheMap mockMap: SuiteMocks } diff --git a/packages/web-worker/rollup.config.js b/packages/web-worker/rollup.config.js index fe3b462beefc..2ada0d4fa378 100644 --- a/packages/web-worker/rollup.config.js +++ b/packages/web-worker/rollup.config.js @@ -14,6 +14,7 @@ const external = [ ...Object.keys(pkg.dependencies || {}), ...Object.keys(pkg.peerDependencies || {}), 'vitest', + 'vitest/node', ] const plugins = [ diff --git a/packages/web-worker/src/pure.ts b/packages/web-worker/src/pure.ts index 2e18d3bc0342..56f87f913b7e 100644 --- a/packages/web-worker/src/pure.ts +++ b/packages/web-worker/src/pure.ts @@ -1,7 +1,7 @@ /* eslint-disable no-restricted-imports */ - import { VitestRunner } from 'vitest/node' import type { WorkerGlobalState } from 'vitest' +import { toFilePath } from '../../vite-node/src/utils' function getWorkerState(): WorkerGlobalState { // @ts-expect-error untyped global @@ -44,7 +44,7 @@ interface InlineWorkerContext { postMessage: (data: any) => void self: InlineWorkerContext global: InlineWorkerContext - invalidate: string[] + invalidates: string[] importScripts?: any } @@ -94,7 +94,7 @@ export function defineWebWorker() { public onerror: null | Procedure = null constructor(url: URL | string) { - const invalidate: string[] = [] + const invalidates: string[] = [] const context: InlineWorkerContext = { onmessage: null, dispatchEvent: (event: Event) => { @@ -112,7 +112,7 @@ export function defineWebWorker() { get global() { return context }, - invalidate, + invalidates, } this.inside.on('message', (e) => { @@ -127,13 +127,16 @@ export function defineWebWorker() { let id = url instanceof URL ? url.toString() : url - id = id.replace('?worker_file', '') + id = id + .replace('?worker_file', '') + .replace(/^file:\/+/, '/') - invalidate.push(id) + const fsPath = toFilePath(id, config.root) + invalidates.push(fsPath) - runner.executeId(id) + runner.executeFile(fsPath) .then(() => { - invalidate.forEach((path) => { + invalidates.forEach((path) => { // worker should be new every time moduleCache.delete(path) moduleCache.delete(`${path}__mock`)