@@ -25,6 +25,8 @@ import type {
2525 ThemeFSEventPayload ,
2626} from '@shopify/cli-kit/node/themes/types'
2727
28+ const FILE_EVENT_DEBOUNCE_TIME_IN_MS = 250
29+
2830const THEME_DIRECTORY_PATTERNS = [
2931 'assets/**/*.*' ,
3032 'config/**/*.json' ,
@@ -321,10 +323,30 @@ export function mountThemeFileSystem(root: string, options?: ThemeFileSystemOpti
321323 ignoreInitial : true ,
322324 } )
323325
326+ // Debounce file events per-file and per-event-type
327+ const pendingEvents = new Map < string , NodeJS . Timeout > ( )
328+
329+ const queueFsEvent = ( eventName : 'add' | 'change' | 'unlink' , filePath : string ) => {
330+ const fileKey = getKey ( filePath )
331+ const eventKey = `${ fileKey } :${ eventName } `
332+
333+ const pending = pendingEvents . get ( eventKey )
334+ if ( pending ) {
335+ clearTimeout ( pending )
336+ }
337+
338+ const timeout = setTimeout ( ( ) => {
339+ pendingEvents . delete ( eventKey )
340+ handleFsEvent ( eventName , themeId , adminSession , filePath )
341+ } , FILE_EVENT_DEBOUNCE_TIME_IN_MS )
342+
343+ pendingEvents . set ( eventKey , timeout )
344+ }
345+
324346 watcher
325- . on ( 'add' , handleFsEvent . bind ( null , 'add' , themeId , adminSession ) )
326- . on ( 'change' , handleFsEvent . bind ( null , 'change' , themeId , adminSession ) )
327- . on ( 'unlink' , handleFsEvent . bind ( null , 'unlink' , themeId , adminSession ) )
347+ . on ( 'add' , queueFsEvent . bind ( null , 'add' ) )
348+ . on ( 'change' , queueFsEvent . bind ( null , 'change' ) )
349+ . on ( 'unlink' , queueFsEvent . bind ( null , 'unlink' ) )
328350 } ,
329351 }
330352}
0 commit comments