2
2
3
3
const {
4
4
ArrayFrom,
5
+ ArrayIsArray,
5
6
ArrayPrototypeAt,
6
7
ArrayPrototypeFlatMap,
7
8
ArrayPrototypeMap,
@@ -24,12 +25,18 @@ const {
24
25
isMacOS,
25
26
} = require ( 'internal/util' ) ;
26
27
const {
27
- validateFunction,
28
28
validateObject,
29
29
validateString,
30
30
validateStringArray,
31
31
} = require ( 'internal/validators' ) ;
32
32
const { DirentFromStats } = require ( 'internal/fs/utils' ) ;
33
+ const {
34
+ codes : {
35
+ ERR_INVALID_ARG_TYPE ,
36
+ } ,
37
+ hideStackFrames,
38
+ } = require ( 'internal/errors' ) ;
39
+ const assert = require ( 'internal/assert' ) ;
33
40
34
41
let minimatch ;
35
42
function lazyMinimatch ( ) {
@@ -63,6 +70,45 @@ function getDirentSync(path) {
63
70
return new DirentFromStats ( basename ( path ) , stat , dirname ( path ) ) ;
64
71
}
65
72
73
+ /**
74
+ * @callback validateStringArrayOrFunction
75
+ * @param {* } value
76
+ * @param {string } name
77
+ */
78
+ const validateStringArrayOrFunction = hideStackFrames ( ( value , name ) => {
79
+ if ( ArrayIsArray ( value ) ) {
80
+ for ( let i = 0 ; i < value . length ; ++ i ) {
81
+ if ( typeof value [ i ] !== 'string' ) {
82
+ throw new ERR_INVALID_ARG_TYPE ( `${ name } [${ i } ]` , 'string' , value [ i ] ) ;
83
+ }
84
+ }
85
+ return ;
86
+ }
87
+ if ( typeof value !== 'function' ) {
88
+ throw new ERR_INVALID_ARG_TYPE ( name , [ 'string[]' , 'function' ] , value ) ;
89
+ }
90
+ } ) ;
91
+
92
+ /**
93
+ * @param {string } pattern
94
+ * @param {options } options
95
+ * @returns {Minimatch }
96
+ */
97
+ function createMatcher ( pattern , options = kEmptyObject ) {
98
+ const opts = {
99
+ __proto__ : null ,
100
+ nocase : isWindows || isMacOS ,
101
+ windowsPathsNoEscape : true ,
102
+ nonegate : true ,
103
+ nocomment : true ,
104
+ optimizationLevel : 2 ,
105
+ platform : process . platform ,
106
+ nocaseMagicOnly : true ,
107
+ ...options ,
108
+ } ;
109
+ return new ( lazyMinimatch ( ) . Minimatch ) ( pattern , opts ) ;
110
+ }
111
+
66
112
class Cache {
67
113
#cache = new SafeMap ( ) ;
68
114
#statsCache = new SafeMap ( ) ;
@@ -188,24 +234,56 @@ class Pattern {
188
234
}
189
235
}
190
236
237
+ class ResultSet extends SafeSet {
238
+ #root = '.' ;
239
+ #isExcluded = ( ) => false ;
240
+ constructor ( i ) { super ( i ) ; } // eslint-disable-line no-useless-constructor
241
+
242
+ setup ( root , isExcludedFn ) {
243
+ this . #root = root ;
244
+ this . #isExcluded = isExcludedFn ;
245
+ }
246
+
247
+ add ( value ) {
248
+ if ( this . #isExcluded( resolve ( this . #root, value ) ) ) {
249
+ return false ;
250
+ }
251
+ super . add ( value ) ;
252
+ return true ;
253
+ }
254
+ }
255
+
191
256
class Glob {
192
257
#root;
193
258
#exclude;
194
259
#cache = new Cache ( ) ;
195
- #results = new SafeSet ( ) ;
260
+ #results = new ResultSet ( ) ;
196
261
#queue = [ ] ;
197
262
#subpatterns = new SafeMap ( ) ;
198
263
#patterns;
199
264
#withFileTypes;
265
+ #isExcluded = ( ) => false ;
200
266
constructor ( pattern , options = kEmptyObject ) {
201
267
validateObject ( options , 'options' ) ;
202
268
const { exclude, cwd, withFileTypes } = options ;
203
- if ( exclude != null ) {
204
- validateFunction ( exclude , 'options.exclude' ) ;
205
- }
206
269
this . #root = cwd ?? '.' ;
207
- this . #exclude = exclude ;
208
270
this . #withFileTypes = ! ! withFileTypes ;
271
+ if ( exclude != null ) {
272
+ validateStringArrayOrFunction ( exclude , 'options.exclude' ) ;
273
+ if ( ArrayIsArray ( exclude ) ) {
274
+ assert ( typeof this . #root === 'string' ) ;
275
+ // Convert the path part of exclude patterns to absolute paths for
276
+ // consistent comparison before instantiating matchers.
277
+ const matchers = exclude
278
+ . map ( ( pattern ) => resolve ( this . #root, pattern ) )
279
+ . map ( ( pattern ) => createMatcher ( pattern ) ) ;
280
+ this . #isExcluded = ( value ) =>
281
+ matchers . some ( ( matcher ) => matcher . match ( value ) ) ;
282
+ this . #results. setup ( this . #root, this . #isExcluded) ;
283
+ } else {
284
+ this . #exclude = exclude ;
285
+ }
286
+ }
209
287
let patterns ;
210
288
if ( typeof pattern === 'object' ) {
211
289
validateStringArray ( pattern , 'patterns' ) ;
@@ -214,17 +292,7 @@ class Glob {
214
292
validateString ( pattern , 'patterns' ) ;
215
293
patterns = [ pattern ] ;
216
294
}
217
- this . matchers = ArrayPrototypeMap ( patterns , ( pattern ) => new ( lazyMinimatch ( ) . Minimatch ) ( pattern , {
218
- __proto__ : null ,
219
- nocase : isWindows || isMacOS ,
220
- windowsPathsNoEscape : true ,
221
- nonegate : true ,
222
- nocomment : true ,
223
- optimizationLevel : 2 ,
224
- platform : process . platform ,
225
- nocaseMagicOnly : true ,
226
- } ) ) ;
227
-
295
+ this . matchers = ArrayPrototypeMap ( patterns , ( pattern ) => createMatcher ( pattern ) ) ;
228
296
this . #patterns = ArrayPrototypeFlatMap ( this . matchers , ( matcher ) => ArrayPrototypeMap ( matcher . set ,
229
297
( pattern , i ) => new Pattern (
230
298
pattern ,
@@ -255,6 +323,9 @@ class Glob {
255
323
) ;
256
324
}
257
325
#addSubpattern( path , pattern ) {
326
+ if ( this . #isExcluded( path ) ) {
327
+ return ;
328
+ }
258
329
if ( ! this . #subpatterns. has ( path ) ) {
259
330
this . #subpatterns. set ( path , [ pattern ] ) ;
260
331
} else {
@@ -273,6 +344,9 @@ class Glob {
273
344
const isLast = pattern . isLast ( isDirectory ) ;
274
345
const isFirst = pattern . isFirst ( ) ;
275
346
347
+ if ( this . #isExcluded( fullpath ) ) {
348
+ return ;
349
+ }
276
350
if ( isFirst && isWindows && typeof pattern . at ( 0 ) === 'string' && StringPrototypeEndsWith ( pattern . at ( 0 ) , ':' ) ) {
277
351
// Absolute path, go to root
278
352
this . #addSubpattern( `${ pattern . at ( 0 ) } \\` , pattern . child ( new SafeSet ( ) . add ( 1 ) ) ) ;
@@ -461,6 +535,9 @@ class Glob {
461
535
const isLast = pattern . isLast ( isDirectory ) ;
462
536
const isFirst = pattern . isFirst ( ) ;
463
537
538
+ if ( this . #isExcluded( fullpath ) ) {
539
+ return ;
540
+ }
464
541
if ( isFirst && isWindows && typeof pattern . at ( 0 ) === 'string' && StringPrototypeEndsWith ( pattern . at ( 0 ) , ':' ) ) {
465
542
// Absolute path, go to root
466
543
this . #addSubpattern( `${ pattern . at ( 0 ) } \\` , pattern . child ( new SafeSet ( ) . add ( 1 ) ) ) ;
@@ -489,8 +566,9 @@ class Glob {
489
566
if ( stat && ( p || isDirectory ) ) {
490
567
const result = join ( path , p ) ;
491
568
if ( ! this . #results. has ( result ) ) {
492
- this . #results. add ( result ) ;
493
- yield this . #withFileTypes ? stat : result ;
569
+ if ( this . #results. add ( result ) ) {
570
+ yield this . #withFileTypes ? stat : result ;
571
+ }
494
572
}
495
573
}
496
574
if ( pattern . indexes . size === 1 && pattern . indexes . has ( last ) ) {
@@ -501,8 +579,9 @@ class Glob {
501
579
// If pattern ends with **, add to results
502
580
// if path is ".", add it only if pattern starts with "." or pattern is exactly "**"
503
581
if ( ! this . #results. has ( path ) ) {
504
- this . #results. add ( path ) ;
505
- yield this . #withFileTypes ? stat : path ;
582
+ if ( this . #results. add ( path ) ) {
583
+ yield this . #withFileTypes ? stat : path ;
584
+ }
506
585
}
507
586
}
508
587
@@ -551,8 +630,9 @@ class Glob {
551
630
} else if ( ! fromSymlink && index === last ) {
552
631
// If ** is last, add to results
553
632
if ( ! this . #results. has ( entryPath ) ) {
554
- this . #results. add ( entryPath ) ;
555
- yield this . #withFileTypes ? entry : entryPath ;
633
+ if ( this . #results. add ( entryPath ) ) {
634
+ yield this . #withFileTypes ? entry : entryPath ;
635
+ }
556
636
}
557
637
}
558
638
@@ -562,8 +642,9 @@ class Glob {
562
642
if ( nextMatches && nextIndex === last && ! isLast ) {
563
643
// If next pattern is the last one, add to results
564
644
if ( ! this . #results. has ( entryPath ) ) {
565
- this . #results. add ( entryPath ) ;
566
- yield this . #withFileTypes ? entry : entryPath ;
645
+ if ( this . #results. add ( entryPath ) ) {
646
+ yield this . #withFileTypes ? entry : entryPath ;
647
+ }
567
648
}
568
649
} else if ( nextMatches && entry . isDirectory ( ) ) {
569
650
// Pattern matched, meaning two patterns forward
@@ -598,15 +679,17 @@ class Glob {
598
679
if ( ! this . #cache. seen ( path , pattern , nextIndex ) ) {
599
680
this . #cache. add ( path , pattern . child ( new SafeSet ( ) . add ( nextIndex ) ) ) ;
600
681
if ( ! this . #results. has ( path ) ) {
601
- this . #results. add ( path ) ;
602
- yield this . #withFileTypes ? this . #cache. statSync ( fullpath ) : path ;
682
+ if ( this . #results. add ( path ) ) {
683
+ yield this . #withFileTypes ? this . #cache. statSync ( fullpath ) : path ;
684
+ }
603
685
}
604
686
}
605
687
if ( ! this . #cache. seen ( path , pattern , nextIndex ) || ! this . #cache. seen ( parent , pattern , nextIndex ) ) {
606
688
this . #cache. add ( parent , pattern . child ( new SafeSet ( ) . add ( nextIndex ) ) ) ;
607
689
if ( ! this . #results. has ( parent ) ) {
608
- this . #results. add ( parent ) ;
609
- yield this . #withFileTypes ? this . #cache. statSync ( join ( this . #root, parent ) ) : parent ;
690
+ if ( this . #results. add ( parent ) ) {
691
+ yield this . #withFileTypes ? this . #cache. statSync ( join ( this . #root, parent ) ) : parent ;
692
+ }
610
693
}
611
694
}
612
695
}
@@ -621,8 +704,9 @@ class Glob {
621
704
// If current pattern is ".", proceed to test next pattern
622
705
if ( nextIndex === last ) {
623
706
if ( ! this . #results. has ( entryPath ) ) {
624
- this . #results. add ( entryPath ) ;
625
- yield this . #withFileTypes ? entry : entryPath ;
707
+ if ( this . #results. add ( entryPath ) ) {
708
+ yield this . #withFileTypes ? entry : entryPath ;
709
+ }
626
710
}
627
711
} else {
628
712
subPatterns . add ( nextIndex + 1 ) ;
@@ -634,8 +718,9 @@ class Glob {
634
718
// add next pattern to potential patterns, or to results if it's the last pattern
635
719
if ( index === last ) {
636
720
if ( ! this . #results. has ( entryPath ) ) {
637
- this . #results. add ( entryPath ) ;
638
- yield this . #withFileTypes ? entry : entryPath ;
721
+ if ( this . #results. add ( entryPath ) ) {
722
+ yield this . #withFileTypes ? entry : entryPath ;
723
+ }
639
724
}
640
725
} else if ( entry . isDirectory ( ) ) {
641
726
subPatterns . add ( nextIndex ) ;
0 commit comments