refactor(data): memoize loader promise instead of manual deferred

Store the promise of the extracted loadData() directly in the
per-id map rather than resolving a hand-made pending promise at the
end of the load hook. This also propagates loader errors to the
other build's pending consumer, which previously awaited forever.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pull/5342/head
Divyansh Singh 1 month ago
parent e40ae7888a
commit 8af4a0f5ea

@ -58,15 +58,46 @@ export const staticDataPlugin: Plugin = {
load: { load: {
filter: { id: loaderMatch }, filter: { id: loaderMatch },
async handler(id) { handler(id) {
let _resolve: ((res: any) => void) | undefined if (isBuild) return (idToPendingPromiseMap[id] ??= loadData(id))
if (isBuild) { return loadData(id)
if (idToPendingPromiseMap[id]) return idToPendingPromiseMap[id] }
idToPendingPromiseMap[id] = new Promise((r) => { },
_resolve = r
}) hotUpdate({ file, modules: existingMods }) {
if (this.environment.name !== 'client') return
const modules: EnvironmentModuleNode[] = []
const normalizedFile = normalizePath(file)
// Trigger update if a dependency (including transitive ones) changed.
if (normalizedFile in depToLoaderModuleIdsMap) {
for (const id of Array.from(
depToLoaderModuleIdsMap[normalizedFile] || []
)) {
delete idToLoaderModulesMap[id]
const mod = this.environment.moduleGraph.getModuleById(id)
if (mod) modules.push(mod)
} }
}
// Also check if the file matches any custom watch patterns.
for (const id in idToLoaderModulesMap) {
const loader = idToLoaderModulesMap[id]
if (
loader?.watch?.length &&
pm(loader.watch, loader.options.globOptions)(normalizedFile)
) {
const mod = this.environment.moduleGraph.getModuleById(id)
if (mod) modules.push(mod)
}
}
return modules.length ? [...existingMods, ...modules] : undefined
}
}
async function loadData(id: string): Promise<string> {
const base = path.dirname(id) const base = path.dirname(id)
let watch: LoaderModule['watch'] let watch: LoaderModule['watch']
let load: LoaderModule['load'] let load: LoaderModule['load']
@ -107,42 +138,5 @@ export const staticDataPlugin: Plugin = {
// record loader module for HMR // record loader module for HMR
if (server) idToLoaderModulesMap[id] = { watch, load, options } if (server) idToLoaderModulesMap[id] = { watch, load, options }
const result = `export const data = JSON.parse(${JSON.stringify(JSON.stringify(data))})` return `export const data = JSON.parse(${JSON.stringify(JSON.stringify(data))})`
if (_resolve) _resolve(result)
return result
}
},
hotUpdate({ file, modules: existingMods }) {
if (this.environment.name !== 'client') return
const modules: EnvironmentModuleNode[] = []
const normalizedFile = normalizePath(file)
// Trigger update if a dependency (including transitive ones) changed.
if (normalizedFile in depToLoaderModuleIdsMap) {
for (const id of Array.from(
depToLoaderModuleIdsMap[normalizedFile] || []
)) {
delete idToLoaderModulesMap[id]
const mod = this.environment.moduleGraph.getModuleById(id)
if (mod) modules.push(mod)
}
}
// Also check if the file matches any custom watch patterns.
for (const id in idToLoaderModulesMap) {
const loader = idToLoaderModulesMap[id]
if (
loader?.watch?.length &&
pm(loader.watch, loader.options.globOptions)(normalizedFile)
) {
const mod = this.environment.moduleGraph.getModuleById(id)
if (mod) modules.push(mod)
}
}
return modules.length ? [...existingMods, ...modules] : undefined
}
} }

Loading…
Cancel
Save