@@ -173,13 +173,36 @@ function fingerprintPluginSourceRoots(
173173 env,
174174 } ) ;
175175 return {
176- global : fileFingerprint ( cacheInputs . roots . global ) ,
177- loadPaths : cacheInputs . loadPaths . map ( ( entry ) => fileFingerprint ( entry ) ) ,
178- stock : cacheInputs . roots . stock ? fileFingerprint ( cacheInputs . roots . stock ) : null ,
179- workspace : cacheInputs . roots . workspace ? fileFingerprint ( cacheInputs . roots . workspace ) : null ,
176+ global : sourceRootFingerprint ( cacheInputs . roots . global ) ,
177+ loadPaths : cacheInputs . loadPaths . map ( ( entry ) => sourceRootFingerprint ( entry ) ) ,
178+ stock : cacheInputs . roots . stock ? sourceRootFingerprint ( cacheInputs . roots . stock ) : null ,
179+ workspace : cacheInputs . roots . workspace
180+ ? sourceRootFingerprint ( cacheInputs . roots . workspace )
181+ : null ,
180182 } ;
181183}
182184
185+ function sourceRootFingerprint ( rootPath : string ) : unknown {
186+ return {
187+ root : fileFingerprint ( rootPath ) ,
188+ // Directory mtimes can be too coarse on some Linux filesystems. Include only
189+ // immediate child names/kinds so same-tick plugin installs invalidate the
190+ // process memo without rereading manifests on hot registry lookups.
191+ children : directoryChildFingerprint ( rootPath ) ,
192+ } ;
193+ }
194+
195+ function directoryChildFingerprint ( directoryPath : string ) : unknown {
196+ try {
197+ return fs
198+ . readdirSync ( directoryPath , { withFileTypes : true } )
199+ . map ( ( entry ) => [ entry . name , entry . isDirectory ( ) ? "dir" : entry . isFile ( ) ? "file" : "other" ] )
200+ . toSorted ( ( [ left ] , [ right ] ) => left . localeCompare ( right ) ) ;
201+ } catch {
202+ return "unreadable" ;
203+ }
204+ }
205+
183206function fileFingerprint ( filePath : string ) : unknown {
184207 try {
185208 const stat = fs . statSync ( filePath , { bigint : true } ) ;
0 commit comments