@@ -16,8 +16,8 @@ const {
1616 StringPrototypeEndsWith,
1717} = primordials ;
1818
19- const { lstatSync, readdirSync } = require ( 'fs' ) ;
20- const { lstat, readdir } = require ( 'fs/promises' ) ;
19+ const { lstatSync, readdirSync, statSync : fsStatSync } = require ( 'fs' ) ;
20+ const { lstat, readdir, stat : fsStat } = require ( 'fs/promises' ) ;
2121const { join, resolve, basename, isAbsolute, dirname } = require ( 'path' ) ;
2222
2323const {
@@ -264,12 +264,14 @@ class Glob {
264264 #subpatterns = new SafeMap ( ) ;
265265 #patterns;
266266 #withFileTypes;
267+ #followSymlinks;
267268 #isExcluded = ( ) => false ;
268269 constructor ( pattern , options = kEmptyObject ) {
269270 validateObject ( options , 'options' ) ;
270- const { exclude, cwd, withFileTypes } = options ;
271+ const { exclude, cwd, withFileTypes, followSymlinks } = options ;
271272 this . #root = toPathIfFileURL ( cwd ) ?? '.' ;
272273 this . #withFileTypes = ! ! withFileTypes ;
274+ this . #followSymlinks = ! ! followSymlinks ;
273275 if ( exclude != null ) {
274276 validateStringArrayOrFunction ( exclude , 'options.exclude' ) ;
275277 if ( ArrayIsArray ( exclude ) ) {
@@ -429,6 +431,16 @@ class Glob {
429431 const entryPath = join ( path , entry . name ) ;
430432 this . #cache. addToStatCache ( join ( fullpath , entry . name ) , entry ) ;
431433
434+ let isDirectory = entry . isDirectory ( ) ;
435+ if ( entry . isSymbolicLink ( ) && this . #followSymlinks) {
436+ try {
437+ const stat = fsStatSync ( join ( fullpath , entry . name ) ) ;
438+ isDirectory = stat . isDirectory ( ) ;
439+ } catch {
440+ // ignore
441+ }
442+ }
443+
432444 const subPatterns = new SafeSet ( ) ;
433445 const nSymlinks = new SafeSet ( ) ;
434446 for ( const index of pattern . indexes ) {
@@ -456,7 +468,7 @@ class Glob {
456468 ( this . #exclude && this . #exclude( this . #withFileTypes ? entry : entry . name ) ) ) {
457469 continue ;
458470 }
459- if ( ! fromSymlink && entry . isDirectory ( ) ) {
471+ if ( ! fromSymlink && isDirectory ) {
460472 // If directory, add ** to its potential patterns
461473 subPatterns . add ( index ) ;
462474 } else if ( ! fromSymlink && index === last ) {
@@ -469,24 +481,24 @@ class Glob {
469481 if ( nextMatches && nextIndex === last && ! isLast ) {
470482 // If next pattern is the last one, add to results
471483 this . #results. add ( entryPath ) ;
472- } else if ( nextMatches && entry . isDirectory ( ) ) {
484+ } else if ( nextMatches && isDirectory ) {
473485 // Pattern matched, meaning two patterns forward
474486 // are also potential patterns
475487 // e.g **/b/c when entry is a/b - add c to potential patterns
476488 subPatterns . add ( index + 2 ) ;
477489 }
478490 if ( ( nextMatches || pattern . at ( 0 ) === '.' ) &&
479- ( entry . isDirectory ( ) || entry . isSymbolicLink ( ) ) && ! fromSymlink ) {
491+ ( isDirectory || entry . isSymbolicLink ( ) ) && ! fromSymlink ) {
480492 // If pattern after ** matches, or pattern starts with "."
481493 // and entry is a directory or symlink, add to potential patterns
482494 subPatterns . add ( nextIndex ) ;
483495 }
484496
485- if ( entry . isSymbolicLink ( ) ) {
497+ if ( entry . isSymbolicLink ( ) && ! this . #followSymlinks ) {
486498 nSymlinks . add ( index ) ;
487499 }
488500
489- if ( next === '..' && entry . isDirectory ( ) ) {
501+ if ( next === '..' && isDirectory ) {
490502 // In case pattern is "**/..",
491503 // both parent and current directory should be added to the queue
492504 // if this is the last pattern, add to results instead
@@ -529,7 +541,7 @@ class Glob {
529541 // add next pattern to potential patterns, or to results if it's the last pattern
530542 if ( index === last ) {
531543 this . #results. add ( entryPath ) ;
532- } else if ( entry . isDirectory ( ) ) {
544+ } else if ( isDirectory ) {
533545 subPatterns . add ( nextIndex ) ;
534546 }
535547 }
@@ -639,6 +651,16 @@ class Glob {
639651 const entryPath = join ( path , entry . name ) ;
640652 this . #cache. addToStatCache ( join ( fullpath , entry . name ) , entry ) ;
641653
654+ let isDirectory = entry . isDirectory ( ) ;
655+ if ( entry . isSymbolicLink ( ) && this . #followSymlinks) {
656+ try {
657+ const s = await fsStat ( join ( fullpath , entry . name ) ) ;
658+ isDirectory = s . isDirectory ( ) ;
659+ } catch {
660+ // ignore
661+ }
662+ }
663+
642664 const subPatterns = new SafeSet ( ) ;
643665 const nSymlinks = new SafeSet ( ) ;
644666 for ( const index of pattern . indexes ) {
@@ -666,7 +688,7 @@ class Glob {
666688 ( this . #exclude && this . #exclude( this . #withFileTypes ? entry : entry . name ) ) ) {
667689 continue ;
668690 }
669- if ( ! fromSymlink && entry . isDirectory ( ) ) {
691+ if ( ! fromSymlink && isDirectory ) {
670692 // If directory, add ** to its potential patterns
671693 subPatterns . add ( index ) ;
672694 } else if ( ! fromSymlink && index === last ) {
@@ -683,24 +705,24 @@ class Glob {
683705 if ( ! this . #results. has ( entryPath ) && this . #results. add ( entryPath ) ) {
684706 yield this . #withFileTypes ? entry : entryPath ;
685707 }
686- } else if ( nextMatches && entry . isDirectory ( ) ) {
708+ } else if ( nextMatches && isDirectory ) {
687709 // Pattern matched, meaning two patterns forward
688710 // are also potential patterns
689711 // e.g **/b/c when entry is a/b - add c to potential patterns
690712 subPatterns . add ( index + 2 ) ;
691713 }
692714 if ( ( nextMatches || pattern . at ( 0 ) === '.' ) &&
693- ( entry . isDirectory ( ) || entry . isSymbolicLink ( ) ) && ! fromSymlink ) {
715+ ( isDirectory || entry . isSymbolicLink ( ) ) && ! fromSymlink ) {
694716 // If pattern after ** matches, or pattern starts with "."
695717 // and entry is a directory or symlink, add to potential patterns
696718 subPatterns . add ( nextIndex ) ;
697719 }
698720
699- if ( entry . isSymbolicLink ( ) ) {
721+ if ( entry . isSymbolicLink ( ) && ! this . #followSymlinks ) {
700722 nSymlinks . add ( index ) ;
701723 }
702724
703- if ( next === '..' && entry . isDirectory ( ) ) {
725+ if ( next === '..' && isDirectory ) {
704726 // In case pattern is "**/..",
705727 // both parent and current directory should be added to the queue
706728 // if this is the last pattern, add to results instead
@@ -759,7 +781,7 @@ class Glob {
759781 yield this . #withFileTypes ? entry : entryPath ;
760782 }
761783 }
762- } else if ( entry . isDirectory ( ) ) {
784+ } else if ( isDirectory ) {
763785 subPatterns . add ( nextIndex ) ;
764786 }
765787 }
0 commit comments