@@ -10,6 +10,7 @@ const {
1010
1111const { normalizePath } = require ( 'internal/vfs/router' ) ;
1212const { pathToFileURL, fileURLToPath } = require ( 'internal/url' ) ;
13+ const { createENOENT } = require ( 'internal/vfs/errors' ) ;
1314
1415// Registry of active VFS instances
1516const activeVFSList = [ ] ;
@@ -26,6 +27,10 @@ let originalLstatSync = null;
2627let originalStatSync = null ;
2728// Original fs.readdirSync function
2829let originalReaddirSync = null ;
30+ // Original fs/promises.readdir function
31+ let originalPromisesReaddir = null ;
32+ // Original fs/promises.lstat function
33+ let originalPromisesLstat = null ;
2934// Track if hooks are installed
3035let hooksInstalled = false ;
3136// ESM hooks instance (for potential cleanup)
@@ -111,7 +116,6 @@ function findVFSForRead(filename, options) {
111116 } else if ( vfs . isMounted ) {
112117 // In mounted mode, if path is under mount point but doesn't exist,
113118 // don't fall through to real fs - throw ENOENT
114- const { createENOENT } = require ( 'internal/vfs/errors' ) ;
115119 throw createENOENT ( 'open' , filename ) ;
116120 }
117121 }
@@ -139,7 +143,6 @@ function findVFSForRealpath(filename) {
139143 }
140144 }
141145 } else if ( vfs . isMounted ) {
142- const { createENOENT } = require ( 'internal/vfs/errors' ) ;
143146 throw createENOENT ( 'realpath' , filename ) ;
144147 }
145148 }
@@ -167,7 +170,6 @@ function findVFSForFsStat(filename) {
167170 }
168171 }
169172 } else if ( vfs . isMounted ) {
170- const { createENOENT } = require ( 'internal/vfs/errors' ) ;
171173 throw createENOENT ( 'stat' , filename ) ;
172174 }
173175 }
@@ -196,14 +198,68 @@ function findVFSForReaddir(dirname, options) {
196198 }
197199 }
198200 } else if ( vfs . isMounted ) {
199- const { createENOENT } = require ( 'internal/vfs/errors' ) ;
200201 throw createENOENT ( 'scandir' , dirname ) ;
201202 }
202203 }
203204 }
204205 return null ;
205206}
206207
208+ /**
209+ * Async version: Checks all active VFS instances for readdir.
210+ * @param {string } dirname The directory path
211+ * @param {object } options The readdir options
212+ * @returns {Promise<{ vfs: VirtualFileSystem, entries: string[]|Dirent[] }|null> }
213+ */
214+ async function findVFSForReaddirAsync ( dirname , options ) {
215+ const normalized = normalizePath ( dirname ) ;
216+ for ( let i = 0 ; i < activeVFSList . length ; i ++ ) {
217+ const vfs = activeVFSList [ i ] ;
218+ if ( vfs . shouldHandle ( normalized ) ) {
219+ if ( vfs . existsSync ( normalized ) ) {
220+ try {
221+ const entries = await vfs . promises . readdir ( normalized , options ) ;
222+ return { vfs, entries } ;
223+ } catch ( e ) {
224+ if ( vfs . isMounted ) {
225+ throw e ;
226+ }
227+ }
228+ } else if ( vfs . isMounted ) {
229+ throw createENOENT ( 'scandir' , dirname ) ;
230+ }
231+ }
232+ }
233+ return null ;
234+ }
235+
236+ /**
237+ * Async version: Checks all active VFS instances for lstat.
238+ * @param {string } filename The path to stat
239+ * @returns {Promise<{ vfs: VirtualFileSystem, stats: Stats }|null> }
240+ */
241+ async function findVFSForLstatAsync ( filename ) {
242+ const normalized = normalizePath ( filename ) ;
243+ for ( let i = 0 ; i < activeVFSList . length ; i ++ ) {
244+ const vfs = activeVFSList [ i ] ;
245+ if ( vfs . shouldHandle ( normalized ) ) {
246+ if ( vfs . existsSync ( normalized ) ) {
247+ try {
248+ const stats = await vfs . promises . lstat ( normalized ) ;
249+ return { vfs, stats } ;
250+ } catch ( e ) {
251+ if ( vfs . isMounted ) {
252+ throw e ;
253+ }
254+ }
255+ } else if ( vfs . isMounted ) {
256+ throw createENOENT ( 'lstat' , filename ) ;
257+ }
258+ }
259+ }
260+ return null ;
261+ }
262+
207263/**
208264 * Determine module format from file extension.
209265 * @param {string } url The file URL
@@ -444,6 +500,35 @@ function installHooks() {
444500 return originalReaddirSync . call ( fs , path , options ) ;
445501 } ;
446502
503+ // Hook fs/promises for async glob support
504+ const fsPromises = require ( 'fs/promises' ) ;
505+
506+ // Override fs/promises.readdir (needed for async glob support)
507+ originalPromisesReaddir = fsPromises . readdir ;
508+ fsPromises . readdir = async function vfsPromisesReaddir ( path , options ) {
509+ if ( typeof path === 'string' || path instanceof URL ) {
510+ const pathStr = typeof path === 'string' ? path : path . pathname ;
511+ const vfsResult = await findVFSForReaddirAsync ( pathStr , options ) ;
512+ if ( vfsResult !== null ) {
513+ return vfsResult . entries ;
514+ }
515+ }
516+ return originalPromisesReaddir . call ( fsPromises , path , options ) ;
517+ } ;
518+
519+ // Override fs/promises.lstat (needed for async glob support)
520+ originalPromisesLstat = fsPromises . lstat ;
521+ fsPromises . lstat = async function vfsPromisesLstat ( path , options ) {
522+ if ( typeof path === 'string' || path instanceof URL ) {
523+ const pathStr = typeof path === 'string' ? path : path . pathname ;
524+ const vfsResult = await findVFSForLstatAsync ( pathStr ) ;
525+ if ( vfsResult !== null ) {
526+ return vfsResult . stats ;
527+ }
528+ }
529+ return originalPromisesLstat . call ( fsPromises , path , options ) ;
530+ } ;
531+
447532 // Register ESM hooks using Module.registerHooks
448533 esmHooksInstance = Module . registerHooks ( {
449534 resolve : vfsResolveHook ,
0 commit comments