Skip to content

Commit

Permalink
fix: avoid caching transform result of invalidated module (#7254)
Browse files Browse the repository at this point in the history
  • Loading branch information
patak-dev committed Mar 10, 2022
1 parent 0fd6422 commit 2d7ba72
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 15 deletions.
16 changes: 13 additions & 3 deletions packages/vite/src/node/server/moduleGraph.ts
Expand Up @@ -32,6 +32,7 @@ export class ModuleNode {
ssrTransformResult: TransformResult | null = null
ssrModule: Record<string, any> | null = null
lastHMRTimestamp = 0
lastInvalidationTimestamp = 0

constructor(url: string) {
this.url = url
Expand Down Expand Up @@ -94,17 +95,26 @@ export class ModuleGraph {
}
}

invalidateModule(mod: ModuleNode, seen: Set<ModuleNode> = new Set()): void {
mod.info = undefined
invalidateModule(
mod: ModuleNode,
seen: Set<ModuleNode> = new Set(),
timestamp: number = Date.now()
): void {
// Save the timestamp for this invalidation, so we can avoid caching the result of possible already started
// processing being done for this module
mod.lastInvalidationTimestamp = timestamp
// Don't invalidate mod.info and mod.meta, as they are part of the processing pipeline
// Invalidating the transform result is enough to ensure this module is re-processed next time it is requested
mod.transformResult = null
mod.ssrTransformResult = null
invalidateSSRModule(mod, seen)
}

invalidateAll(): void {
const timestamp = Date.now()
const seen = new Set<ModuleNode>()
this.idToModuleMap.forEach((mod) => {
this.invalidateModule(mod, seen)
this.invalidateModule(mod, seen, timestamp)
})
}

Expand Down
46 changes: 34 additions & 12 deletions packages/vite/src/node/server/transformRequest.ts
Expand Up @@ -81,6 +81,25 @@ async function doTransform(
return cached
}

// This module may get invalidated while we are processing it. For example
// when a full page reload is needed after the re-processing of pre-bundled
// dependencies when a missing dep is discovered. We save the current time
// to compare it to the last invalidation performed to know if we should
// cache the result of the transformation or we should discard it as stale.
//
// A module can be invalidated due to:
// 1. A full reload because of pre-bundling newly discovered deps
// 2. A full reload after a config change
// 3. The file that generated the module changed
// 4. Invalidation for a virtual module
//
// For 1 and 2, a new request for this module will be issued after
// the invalidation as part of the browser reloading the page. For 3 and 4
// there may not be a new request right away because of HMR handling.
// In all cases, the next time this module is requested, it should be
// re-processed.
const timestamp = Date.now()

// resolve
const id =
(await pluginContainer.resolveId(url, undefined, { ssr }))?.id || url
Expand Down Expand Up @@ -179,17 +198,20 @@ async function doTransform(
}
}

if (ssr) {
return (mod.ssrTransformResult = await ssrTransform(
code,
map as SourceMap,
url
))
} else {
return (mod.transformResult = {
code,
map,
etag: getEtag(code, { weak: true })
} as TransformResult)
const result = ssr
? await ssrTransform(code, map as SourceMap, url)
: ({
code,
map,
etag: getEtag(code, { weak: true })
} as TransformResult)

// Only cache the result if the module wasn't invalidated while it was
// being processed, so it is re-processed next time if it is stale
if (timestamp > mod.lastInvalidationTimestamp) {
if (ssr) mod.ssrTransformResult = result
else mod.transformResult = result
}

return result
}

0 comments on commit 2d7ba72

Please sign in to comment.