diff --git a/packages/vite/src/node/server/index.ts b/packages/vite/src/node/server/index.ts index 5cd066ffc736d4..2a6d457c8d76a4 100644 --- a/packages/vite/src/node/server/index.ts +++ b/packages/vite/src/node/server/index.ts @@ -768,22 +768,33 @@ export async function _createServer( await onHMRUpdate(isUnlink ? 'delete' : 'create', file) } - watcher.on('change', async (file) => { + async function onFileChange(file: string) { file = normalizePath(file) await container.watchChange(file, { event: 'update' }) // invalidate module graph cache on file change moduleGraph.onFileChange(file) await onHMRUpdate('update', file) - }) + } - getFsUtils(config).initWatcher?.(watcher) + const fsUtils = getFsUtils(config) - watcher.on('add', (file) => { - onFileAddUnlink(file, false) - }) - watcher.on('unlink', (file) => { - onFileAddUnlink(file, true) - }) + if (fsUtils.initWatcher) { + fsUtils.initWatcher(watcher) + setupDebouncedWatchEventsListeners( + watcher, + onFileAddUnlink, + onFileChange, + 5, + ) + } else { + watcher.on('add', (file) => { + onFileAddUnlink(file, false) + }) + watcher.on('unlink', (file) => { + onFileAddUnlink(file, true) + }) + watcher.on('change', onFileChange) + } hot.on('vite:invalidate', async ({ path, message }) => { const mod = moduleGraph.urlToModuleMap.get(path) @@ -1260,3 +1271,49 @@ function setupOnCrawlEnd(onCrawlEnd: () => void): CrawlEndFinder { cancel, } } + +interface WatchEvent { + type: 'add' | 'unlink' | 'change' + file: string +} +function setupDebouncedWatchEventsListeners( + watcher: FSWatcher, + onFileAddUnlink: (file: string, isUnlink: boolean) => void, + onFileChange: (file: string) => void, + debounceMs: number, +) { + let watchEvents: WatchEvent[] = [] + let processWatchEventsHandle: NodeJS.Timeout | null = null + function processWatchEvent(event: WatchEvent) { + switch (event.type) { + case 'add': + return onFileAddUnlink(event.file, true) + case 'unlink': + return onFileAddUnlink(event.file, false) + case 'change': + return onFileChange(event.file) + } + } + function debouncedProcessWatchEvents() { + if (processWatchEventsHandle) { + clearTimeout(processWatchEventsHandle) + processWatchEventsHandle = null + } + processWatchEventsHandle = setTimeout(() => { + watchEvents.forEach(processWatchEvent) + watchEvents = [] + }, debounceMs) + } + watcher.on('add', (file) => { + watchEvents.push({ type: 'add', file }) + debouncedProcessWatchEvents() + }) + watcher.on('unlink', (file) => { + watchEvents.push({ type: 'unlink', file }) + debouncedProcessWatchEvents() + }) + watcher.on('change', (file) => { + watchEvents.push({ type: 'change', file }) + debouncedProcessWatchEvents() + }) +}