@@ -1141,42 +1141,33 @@ function createChokidarIgnored(
11411141
11421142interface ChokidarBaselineEntry {
11431143 posixPath : string ;
1144- mtimeMs ? : number ;
1145- size ? : number ;
1144+ mtimeMs : number ;
1145+ size : number ;
11461146}
11471147
11481148// Chokidar initializes file watchers asynchronously (`await stat(path)` followed by
1149- // `fs.watch` or `fs.watchFile` ). If a file is modified or recreated immediately after
1150- // `watcher.add()` is called, Chokidar may capture the post-modification state as its initial
1151- // baseline (or suppress the initial `'add'` event due to ` ignoreInitial: true`) and miss the
1152- // change. Re-statting after a short delay catches any modifications or recreations that occurred
1153- // during asynchronous watcher registration, while `fileStats` deduplicates if Chokidar also fired.
1149+ // `fs.watch` or polling ). If a file is modified immediately after `watcher.add()` is called,
1150+ // Chokidar may capture the post-modification state as its initial baseline (with
1151+ // ` ignoreInitial: true`) and miss the change. Re-statting after a short delay catches any
1152+ // modifications that occurred during asynchronous watcher registration, while `fileStats`
1153+ // deduplicates if Chokidar also fired.
11541154async function checkChokidarBaselines (
11551155 baselineStats : ReadonlyMap < string , ChokidarBaselineEntry > ,
11561156 watchedFiles : ReadonlySet < string > ,
1157- fileStats : ReadonlyMap < string , { mtimeMs : number ; size : number } > ,
11581157 watcher : Chokidar . FSWatcher ,
11591158 handleEvent : ( type : 'added' | 'modified' | 'removed' , rawPath : string ) => void ,
11601159) : Promise < void > {
11611160 await Promise . all (
11621161 Array . from ( baselineStats , async ( [ lookupKey , baseline ] ) => {
1163- if ( ! watchedFiles . has ( lookupKey ) ) {
1162+ if ( watcher . closed || ! watchedFiles . has ( lookupKey ) ) {
11641163 return ;
11651164 }
11661165 const currentStat = await fs . promises . stat ( baseline . posixPath ) . catch ( ( ) => null ) ;
1167- if ( ! watchedFiles . has ( lookupKey ) || ! currentStat ?. isFile ( ) ) {
1166+ if ( watcher . closed || ! watchedFiles . has ( lookupKey ) || ! currentStat ?. isFile ( ) ) {
11681167 return ;
11691168 }
11701169
1171- if ( baseline . mtimeMs === undefined || baseline . size === undefined ) {
1172- const recorded = fileStats . get ( lookupKey ) ;
1173- if ( ! recorded ) {
1174- watcher . add ( baseline . posixPath ) ;
1175- handleEvent ( 'added' , path . normalize ( baseline . posixPath ) ) ;
1176- } else if ( currentStat . mtimeMs !== recorded . mtimeMs || currentStat . size !== recorded . size ) {
1177- handleEvent ( 'modified' , path . normalize ( baseline . posixPath ) ) ;
1178- }
1179- } else if ( currentStat . mtimeMs !== baseline . mtimeMs || currentStat . size !== baseline . size ) {
1170+ if ( currentStat . mtimeMs !== baseline . mtimeMs || currentStat . size !== baseline . size ) {
11801171 handleEvent ( 'modified' , path . normalize ( baseline . posixPath ) ) ;
11811172 }
11821173 } ) ,
@@ -1206,8 +1197,8 @@ async function createChokidarWatcher(
12061197 ignoreInitial : true ,
12071198 ignored,
12081199 followSymlinks : options ?. followSymlinks ,
1209- usePolling : ! ! options ?. polling ,
1210- interval : options ?. interval ,
1200+ backend : options ?. polling ? 'polling' : 'auto' ,
1201+ pollingInterval : options ?. interval ,
12111202 } ) ;
12121203
12131204 // Prevent unhandled watcher errors (such as EMFILE or EACCES) from crashing the process.
@@ -1252,52 +1243,21 @@ async function createChokidarWatcher(
12521243 } ;
12531244
12541245 const scheduleBaselineCheck = ( baselineStats : ReadonlyMap < string , ChokidarBaselineEntry > ) => {
1255- if ( baselineStats . size === 0 ) {
1246+ if ( baselineStats . size === 0 || watcher . closed ) {
12561247 return ;
12571248 }
12581249
12591250 const timer = setTimeout ( ( ) => {
12601251 pendingTimers . delete ( timer ) ;
1261- void checkChokidarBaselines ( baselineStats , watchedFiles , fileStats , watcher , handleEvent ) ;
1252+ void checkChokidarBaselines ( baselineStats , watchedFiles , watcher , handleEvent ) ;
12621253 } , 50 ) ;
12631254 timer . unref ( ) ;
12641255 pendingTimers . add ( timer ) ;
12651256 } ;
12661257
12671258 watcher . on ( 'add' , ( path ) => handleEvent ( 'added' , path ) ) ;
12681259 watcher . on ( 'change' , ( path ) => handleEvent ( 'modified' , path ) ) ;
1269- watcher . on ( 'unlink' , ( rawPath ) => {
1270- const posixPath = toPosixPathNormalized ( rawPath ) ;
1271- const lookupKey = toLookupKey ( posixPath , isCaseSensitive ) ;
1272- if ( ! isPathWatched ( lookupKey , watchedFiles ) ) {
1273- return ;
1274- }
1275-
1276- // When Chokidar processes a file removal, it closes the underlying file watcher unless only a
1277- // single directory is tracked (`_watched.size === 1`). Re-adding a directly watched path
1278- // ensures Chokidar continues watching either the recreated file (if replaced within the atomic
1279- // window) or its parent directory for future recreation.
1280- if ( watchedFiles . has ( lookupKey ) ) {
1281- fileStats . delete ( lookupKey ) ;
1282- watcher . add ( posixPath ) ;
1283- const stat = fs . statSync ( rawPath , { throwIfNoEntry : false } ) ;
1284- if ( stat ?. isFile ( ) ) {
1285- handleEvent ( 'modified' , rawPath ) ;
1286- scheduleBaselineCheck (
1287- new Map ( [ [ lookupKey , { posixPath, mtimeMs : stat . mtimeMs , size : stat . size } ] ] ) ,
1288- ) ;
1289-
1290- return ;
1291- }
1292-
1293- handleEvent ( 'removed' , rawPath ) ;
1294- scheduleBaselineCheck ( new Map ( [ [ lookupKey , { posixPath } ] ] ) ) ;
1295-
1296- return ;
1297- }
1298-
1299- handleEvent ( 'removed' , rawPath ) ;
1300- } ) ;
1260+ watcher . on ( 'unlink' , ( path ) => handleEvent ( 'removed' , path ) ) ;
13011261
13021262 return {
13031263 [ Symbol . asyncIterator ] ( ) {
@@ -1324,6 +1284,10 @@ async function createChokidarWatcher(
13241284 } ,
13251285
13261286 add ( paths ) {
1287+ if ( watcher . closed ) {
1288+ return ;
1289+ }
1290+
13271291 const targets = typeof paths === 'string' ? [ paths ] : paths ;
13281292 const newPaths : string [ ] = [ ] ;
13291293 const baselineStats = new Map < string , ChokidarBaselineEntry > ( ) ;
@@ -1347,6 +1311,10 @@ async function createChokidarWatcher(
13471311 } ,
13481312
13491313 remove ( paths ) {
1314+ if ( watcher . closed ) {
1315+ return ;
1316+ }
1317+
13501318 const targets = typeof paths === 'string' ? [ paths ] : paths ;
13511319 const removePaths : string [ ] = [ ] ;
13521320 for ( const p of targets ) {
@@ -1368,6 +1336,8 @@ async function createChokidarWatcher(
13681336 clearTimeout ( timer ) ;
13691337 }
13701338 pendingTimers . clear ( ) ;
1339+ watchedFiles . clear ( ) ;
1340+ fileStats . clear ( ) ;
13711341 try {
13721342 await watcher . close ( ) ;
13731343 } finally {
0 commit comments