Skip to content

Commit

Permalink
perf(vite-node): cache module fetch
Browse files Browse the repository at this point in the history
  • Loading branch information
antfu committed Jan 22, 2022
1 parent 9cdc504 commit 626a8dd
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 24 deletions.
73 changes: 53 additions & 20 deletions packages/vite-node/src/server.ts
@@ -1,12 +1,18 @@
import type { TransformResult, ViteDevServer } from 'vite'
import type { FetchResult } from '..'
import { shouldExternalize } from './externalize'
import type { ViteNodeResolveId, ViteNodeServerOptions } from './types'
import { toFilePath, withInlineSourcemap } from './utils'

export * from './externalize'

export class ViteNodeServer {
promiseMap = new Map<string, Promise<TransformResult | null | undefined>>()
private fetchPromiseMap = new Map<string, Promise<FetchResult>>()
private transformPromiseMap = new Map<string, Promise<TransformResult | null | undefined>>()
private fetchCache = new Map<string, {
timestamp: number
result: FetchResult
}>()

constructor(
public server: ViteDevServer,
Expand All @@ -17,32 +23,37 @@ export class ViteNodeServer {
return shouldExternalize(id, this.options.deps)
}

async fetchModule(id: string) {
const externalize = await this.shouldExternalize(toFilePath(id, this.server.config.root))
if (externalize)
return { externalize }
const r = await this.transformRequest(id)
return { code: r?.code }
}

async resolveId(id: string, importer?: string): Promise<ViteNodeResolveId | null> {
return this.server.pluginContainer.resolveId(id, importer, { ssr: true })
}

async fetchModule(id: string): Promise<FetchResult> {
// reuse transform for concurrent requests
if (!this.fetchPromiseMap.has(id)) {
this.fetchPromiseMap.set(id,
this._fetchModule(id)
.finally(() => {
this.fetchPromiseMap.delete(id)
}),
)
}
return this.fetchPromiseMap.get(id)!
}

async transformRequest(id: string) {
// reuse transform for concurrent requests
if (!this.promiseMap.has(id)) {
this.promiseMap.set(id,
if (!this.transformPromiseMap.has(id)) {
this.transformPromiseMap.set(id,
this._transformRequest(id)
.finally(() => {
this.promiseMap.delete(id)
this.transformPromiseMap.delete(id)
}),
)
}
return this.promiseMap.get(id)
return this.transformPromiseMap.get(id)!
}

private getTransformMode(id: string) {
getTransformMode(id: string) {
const withoutQuery = id.split('?')[0]

if (this.options.transformMode?.web?.some(r => withoutQuery.match(r)))
Expand All @@ -55,11 +66,37 @@ export class ViteNodeServer {
return 'web'
}

private async _fetchModule(id: string): Promise<FetchResult> {
let result: FetchResult

const timestamp = this.server.moduleGraph.getModuleById(id)?.lastHMRTimestamp
const cache = this.fetchCache.get(id)
if (timestamp && cache && cache.timestamp >= timestamp)
return cache.result

const externalize = await this.shouldExternalize(toFilePath(id, this.server.config.root))
if (externalize) {
result = { externalize }
}
else {
const r = await this._transformRequest(id)
result = { code: r?.code }
}

if (timestamp) {
this.fetchCache.set(id, {
timestamp,
result,
})
}

return result
}

private async _transformRequest(id: string) {
let result: TransformResult | null = null

const mode = this.getTransformMode(id)
if (mode === 'web') {
if (this.getTransformMode(id) === 'web') {
// for components like Vue, we want to use the client side
// plugins but then covert the code to be consumed by the server
result = await this.server.transformRequest(id)
Expand All @@ -73,10 +110,6 @@ export class ViteNodeServer {
if (this.options.sourcemap !== false && result && !id.includes('node_modules'))
withInlineSourcemap(result)

// if (result?.map && process.env.NODE_V8_COVERAGE)
// visitedFilesMap.set(toFilePath(id, config.root), result.map as any)

return result
}
}

7 changes: 6 additions & 1 deletion packages/vite-node/src/types.ts
Expand Up @@ -8,7 +8,12 @@ export interface DepsHandlingOptions {
fallbackCJS?: boolean
}

export type FetchFunction = (id: string) => Promise<{ code?: string; externalize?: string }>
export interface FetchResult {
code?: string
externalize?: string
}

export type FetchFunction = (id: string) => Promise<FetchResult>
export type ResolveIdFunction = (id: string, importer?: string) => Promise<ViteNodeResolveId | null>

export interface ModuleCache {
Expand Down
1 change: 0 additions & 1 deletion packages/vite-node/src/utils.ts
Expand Up @@ -43,7 +43,6 @@ export function toFilePath(id: string, root: string): string {
: absolute
}


let SOURCEMAPPING_URL = 'sourceMa'
SOURCEMAPPING_URL += 'ppingURL'

Expand Down
3 changes: 1 addition & 2 deletions packages/vitest/src/types/worker.ts
@@ -1,5 +1,5 @@
import type { MessagePort } from 'worker_threads'
import type { ViteNodeResolveId } from 'vite-node'
import type { FetchFunction, ViteNodeResolveId } from 'vite-node'
import type { RawSourceMap } from '../types'
import type { ResolvedConfig } from './config'
import type { File, TaskResultPack } from './tasks'
Expand All @@ -13,7 +13,6 @@ export interface WorkerContext {
invalidates?: string[]
}

export type FetchFunction = (id: string) => Promise<{ code?: string; externalize?: string }>
export type ResolveIdFunction = (id: string, importer?: string) => Promise<ViteNodeResolveId | null>

export interface WorkerRPC {
Expand Down

0 comments on commit 626a8dd

Please sign in to comment.