Skip to content

Commit

Permalink
fix(vite-node): use fsPath for moduleCache, fix hmr (#961)
Browse files Browse the repository at this point in the history
  • Loading branch information
antfu committed Mar 16, 2022
1 parent 1722f98 commit f26cdf2
Show file tree
Hide file tree
Showing 10 changed files with 87 additions and 33 deletions.
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -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",
Expand Down
21 changes: 20 additions & 1 deletion packages/ui/client/auto-imports.d.ts
Expand Up @@ -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']
Expand All @@ -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']
Expand Down Expand Up @@ -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']
Expand Down Expand Up @@ -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']
Expand All @@ -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']
Expand Down Expand Up @@ -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']
Expand All @@ -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']
}
Expand Down
56 changes: 41 additions & 15 deletions packages/vite-node/src/client.ts
Expand Up @@ -19,14 +19,47 @@ export const DEFAULT_REQUEST_STUBS = {
},
}

export class ModuleCacheMap extends Map<string, ModuleCache> {
normalizePath(fsPath: string) {
return fsPath
.replace(/\\/g, '/')
.replace(/^\/@fs\//, '/')
.replace(/^file:\//, '/')
.replace(/^\/+/, '/')
}

set(fsPath: string, mod: Partial<ModuleCache>) {
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<string, ModuleCache>
/**
* 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) {
Expand All @@ -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
}
Expand Down Expand Up @@ -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
}

Expand All @@ -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 = {
Expand Down Expand Up @@ -135,13 +168,6 @@ export class ViteNodeRunner {
return context
}

setCache(id: string, mod: Partial<ModuleCache>) {
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
Expand Down
6 changes: 5 additions & 1 deletion packages/vite-node/src/types.ts
@@ -1,3 +1,5 @@
import type { ModuleCacheMap } from './client'

export interface DepsHandlingOptions {
external?: (string | RegExp)[]
inline?: (string | RegExp)[]
Expand Down Expand Up @@ -42,7 +44,7 @@ export interface ViteNodeRunnerOptions {
fetchModule: FetchFunction
resolveId?: ResolveIdFunction
base?: string
moduleCache?: Map<string, ModuleCache>
moduleCache?: ModuleCacheMap
interopDefault?: boolean
requestStubs?: Record<string, any>
}
Expand Down Expand Up @@ -73,3 +75,5 @@ export interface ViteNodeServerOptions {
web?: RegExp[]
}
}

export type { ModuleCacheMap }
2 changes: 1 addition & 1 deletion packages/vitest/src/runtime/execute.ts
Expand Up @@ -37,7 +37,7 @@ export class VitestRunner extends ViteNodeRunner {
const mocker = this.mocker.withRequest(request)

mocker.on('mocked', (dep: string, module: Partial<ModuleCache>) => {
this.setCache(dep, module)
this.moduleCache.set(dep, module)
})

const workerState = getWorkerState()
Expand Down
4 changes: 2 additions & 2 deletions 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'
Expand Down Expand Up @@ -43,7 +43,7 @@ export class VitestMocker {

constructor(
public options: ExecuteOptions,
private moduleCache: Map<string, ModuleCache>,
private moduleCache: ModuleCacheMap,
request?: (dep: string) => unknown,
) {
this.root = this.options.root
Expand Down
5 changes: 3 additions & 2 deletions 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'
Expand All @@ -11,7 +12,7 @@ let _viteNode: {
collect: (files: string[], config: ResolvedConfig) => Promise<void>
}

const moduleCache: Map<string, ModuleCache> = new Map()
const moduleCache = new ModuleCacheMap()
const mockMap = {}

async function startViteNode(ctx: WorkerContext) {
Expand Down
4 changes: 2 additions & 2 deletions 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'
Expand Down Expand Up @@ -37,6 +37,6 @@ export interface WorkerGlobalState {
rpc: BirpcReturn<WorkerRPC>
current?: Test
filepath?: string
moduleCache: Map<string, ModuleCache>
moduleCache: ModuleCacheMap
mockMap: SuiteMocks
}
1 change: 1 addition & 0 deletions packages/web-worker/rollup.config.js
Expand Up @@ -14,6 +14,7 @@ const external = [
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.peerDependencies || {}),
'vitest',
'vitest/node',
]

const plugins = [
Expand Down
19 changes: 11 additions & 8 deletions 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
Expand Down Expand Up @@ -44,7 +44,7 @@ interface InlineWorkerContext {
postMessage: (data: any) => void
self: InlineWorkerContext
global: InlineWorkerContext
invalidate: string[]
invalidates: string[]
importScripts?: any
}

Expand Down Expand Up @@ -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) => {
Expand All @@ -112,7 +112,7 @@ export function defineWebWorker() {
get global() {
return context
},
invalidate,
invalidates,
}

this.inside.on('message', (e) => {
Expand All @@ -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`)
Expand Down

0 comments on commit f26cdf2

Please sign in to comment.