Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(vite-node): rework moduleCache, fix hmr #961

Merged
merged 5 commits into from
Mar 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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