fix(hmr): await module watcher readiness

This commit is contained in:
Tianyi Cui
2026-08-09 16:40:13 +08:00
parent f1c5c6e0b2
commit 455e6ec583
5 changed files with 36 additions and 15 deletions

View File

@@ -214,6 +214,17 @@ class Hmr extends Service {
const match = picomatch(ignored)
const watchBaseDir = await realpath(this.baseDir)
// Collect externals before opening the watcher so every post-ready change
// is observed by listeners that already have their classification state.
const mainUrl = pathToFileURL(resolve(process.argv[1])).href
const mainJob = this.internal.loadCache.get(mainUrl)
if (mainJob) {
this.externals = await loadDependencies(mainJob)
} else {
this.externals = new Set()
}
this.watcher = watch(root, {
...this.config,
cwd: watchBaseDir,
@@ -228,16 +239,6 @@ class Hmr extends Service {
ignoreInitial: true,
})
// Collect externals: framework modules reachable from the main entry.
// Changes to these files require a full process restart, not HMR.
const mainUrl = pathToFileURL(resolve(process.argv[1])).href
const mainJob = this.internal.loadCache.get(mainUrl)
if (mainJob) {
this.externals = await loadDependencies(mainJob)
} else {
this.externals = new Set()
}
const partialReload = this.ctx.debounce(() => this.partialReload(), this.config.debounce)
const onChange = (kind: 'add' | 'change' | 'unlink', path: string) => {
@@ -271,6 +272,26 @@ class Hmr extends Service {
this.watcher.on('add', path => onChange('add', path))
this.watcher.on('change', path => onChange('change', path))
this.watcher.on('unlink', path => onChange('unlink', path))
const ready = Promise.withResolvers<void>()
let readyState: 'pending' | 'resolved' | 'rejected' = root.length === 0 ? 'resolved' : 'pending'
if (root.length === 0) {
ready.resolve()
} else {
this.watcher.once('ready', () => {
readyState = 'resolved'
ready.resolve()
})
}
this.watcher.on('error', (error) => {
if (readyState === 'pending') {
readyState = 'rejected'
ready.reject(error)
} else {
this.ctx.logger.warn(error)
}
})
await ready.promise
}
private refreshConfig(key: object, filename: string, refresh: () => Promise<void> | void) {