Skip to content

Commit de11752

Browse files
committed
fs: apply function exclude to glob results
1 parent 4744070 commit de11752

2 files changed

Lines changed: 115 additions & 16 deletions

File tree

lib/internal/fs/glob.js

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,18 @@ class Glob {
491491
ArrayPrototypePush(this.#subpatterns.get(path), pattern);
492492
}
493493
}
494+
#addResult(path, dirent) {
495+
if (this.#exclude) {
496+
if (this.#withFileTypes) {
497+
if (dirent !== null && this.#exclude(dirent)) {
498+
return false;
499+
}
500+
} else if (this.#exclude(path)) {
501+
return false;
502+
}
503+
}
504+
return this.#results.add(path);
505+
}
494506
#addSubpatterns(path, pattern) {
495507
const seen = this.#cache.add(path, pattern);
496508
if (seen) {
@@ -532,7 +544,7 @@ class Glob {
532544
const p = pattern.at(-1);
533545
const stat = this.#cache.statSync(join(fullpath, p));
534546
if (stat && (p || isDirectory)) {
535-
this.#results.add(join(path, p));
547+
this.#addResult(join(path, p), stat);
536548
}
537549
if (pattern.indexes.size === 1 && pattern.indexes.has(last)) {
538550
return;
@@ -541,7 +553,7 @@ class Glob {
541553
(path !== '.' || pattern.at(0) === '.' || (last === 0 && stat))) {
542554
// If pattern ends with **, add to results
543555
// if path is ".", add it only if pattern starts with "." or pattern is exactly "**"
544-
this.#results.add(path);
556+
this.#addResult(path, stat);
545557
}
546558

547559
if (!isDirectory || this.#isCyclicSync(fullpath, isDirectory, pattern)) {
@@ -604,14 +616,14 @@ class Glob {
604616
subPatterns.add(index);
605617
} else if (!fromSymlink && index === last) {
606618
// If ** is last, add to results
607-
this.#results.add(entryPath);
619+
this.#addResult(entryPath, entry);
608620
}
609621

610622
// Any pattern after ** is also a potential pattern
611623
// so we can already test it here
612624
if (nextMatches && nextIndex === last && !isLast) {
613625
// If next pattern is the last one, add to results
614-
this.#results.add(entryPath);
626+
this.#addResult(entryPath, entry);
615627
} else if (nextMatches && entryIsDirectory) {
616628
// Pattern matched, meaning two patterns forward
617629
// are also potential patterns
@@ -644,11 +656,11 @@ class Glob {
644656
} else {
645657
if (!this.#cache.seen(path, pattern, nextIndex)) {
646658
this.#cache.add(path, pattern.child(new SafeSet().add(nextIndex)));
647-
this.#results.add(path);
659+
this.#addResult(path, this.#cache.statSync(fullpath));
648660
}
649661
if (!this.#cache.seen(path, pattern, nextIndex) || !this.#cache.seen(parent, pattern, nextIndex)) {
650662
this.#cache.add(parent, pattern.child(new SafeSet().add(nextIndex)));
651-
this.#results.add(parent);
663+
this.#addResult(parent, this.#cache.statSync(join(this.#root, parent)));
652664
}
653665
}
654666
}
@@ -661,7 +673,7 @@ class Glob {
661673
} else if (current === '.' && pattern.test(nextIndex, entry.name)) {
662674
// If current pattern is ".", proceed to test next pattern
663675
if (nextIndex === last) {
664-
this.#results.add(entryPath);
676+
this.#addResult(entryPath, entry);
665677
} else {
666678
subPatterns.add(nextIndex + 1);
667679
}
@@ -671,7 +683,7 @@ class Glob {
671683
// If current pattern is a regex that matches entry name (e.g *.js)
672684
// add next pattern to potential patterns, or to results if it's the last pattern
673685
if (index === last) {
674-
this.#results.add(entryPath);
686+
this.#addResult(entryPath, entry);
675687
} else if (entryIsDirectory) {
676688
subPatterns.add(nextIndex);
677689
}
@@ -740,7 +752,7 @@ class Glob {
740752
if (stat && (p || isDirectory)) {
741753
const result = join(path, p);
742754
if (!this.#results.has(result)) {
743-
if (this.#results.add(result)) {
755+
if (this.#addResult(result, stat)) {
744756
yield this.#withFileTypes ? stat : result;
745757
}
746758
}
@@ -753,7 +765,7 @@ class Glob {
753765
// If pattern ends with **, add to results
754766
// if path is ".", add it only if pattern starts with "." or pattern is exactly "**"
755767
if (!this.#results.has(path)) {
756-
if (this.#results.add(path)) {
768+
if (this.#addResult(path, stat)) {
757769
yield this.#withFileTypes ? stat : path;
758770
}
759771
}
@@ -821,7 +833,7 @@ class Glob {
821833
subPatterns.add(index);
822834
} else if (!fromSymlink && index === last) {
823835
// If ** is last, add to results
824-
if (!this.#results.has(entryPath) && this.#results.add(entryPath)) {
836+
if (!this.#results.has(entryPath) && this.#addResult(entryPath, entry)) {
825837
yield this.#withFileTypes ? entry : entryPath;
826838
}
827839
}
@@ -830,7 +842,7 @@ class Glob {
830842
// so we can already test it here
831843
if (nextMatches && nextIndex === last && !isLast) {
832844
// If next pattern is the last one, add to results
833-
if (!this.#results.has(entryPath) && this.#results.add(entryPath)) {
845+
if (!this.#results.has(entryPath) && this.#addResult(entryPath, entry)) {
834846
yield this.#withFileTypes ? entry : entryPath;
835847
}
836848
} else if (nextMatches && entryIsDirectory) {
@@ -866,15 +878,15 @@ class Glob {
866878
if (!this.#cache.seen(path, pattern, nextIndex)) {
867879
this.#cache.add(path, pattern.child(new SafeSet().add(nextIndex)));
868880
if (!this.#results.has(path)) {
869-
if (this.#results.add(path)) {
881+
if (this.#addResult(path, this.#cache.statSync(fullpath))) {
870882
yield this.#withFileTypes ? this.#cache.statSync(fullpath) : path;
871883
}
872884
}
873885
}
874886
if (!this.#cache.seen(path, pattern, nextIndex) || !this.#cache.seen(parent, pattern, nextIndex)) {
875887
this.#cache.add(parent, pattern.child(new SafeSet().add(nextIndex)));
876888
if (!this.#results.has(parent)) {
877-
if (this.#results.add(parent)) {
889+
if (this.#addResult(parent, this.#cache.statSync(join(this.#root, parent)))) {
878890
yield this.#withFileTypes ? this.#cache.statSync(join(this.#root, parent)) : parent;
879891
}
880892
}
@@ -891,7 +903,7 @@ class Glob {
891903
// If current pattern is ".", proceed to test next pattern
892904
if (nextIndex === last) {
893905
if (!this.#results.has(entryPath)) {
894-
if (this.#results.add(entryPath)) {
906+
if (this.#addResult(entryPath, entry)) {
895907
yield this.#withFileTypes ? entry : entryPath;
896908
}
897909
}
@@ -905,7 +917,7 @@ class Glob {
905917
// add next pattern to potential patterns, or to results if it's the last pattern
906918
if (index === last) {
907919
if (!this.#results.has(entryPath)) {
908-
if (this.#results.add(entryPath)) {
920+
if (this.#addResult(entryPath, entry)) {
909921
yield this.#withFileTypes ? entry : entryPath;
910922
}
911923
}

test/parallel/test-fs-glob.mjs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,9 @@ const patterns2 = [
486486
[ 'a/**', [ 'a/**' ], [] ],
487487
];
488488

489+
const excludedNestedFile = ['a', 'b', 'c', 'd'].join(sep);
490+
const excludesNestedFile = (path) => path === excludedNestedFile;
491+
489492
describe('globSync - exclude', function() {
490493
for (const [pattern, exclude] of Object.entries(patterns).map(([k, v]) => [k, v.filter(Boolean)])) {
491494
test(`${pattern} - exclude: ${exclude}`, () => {
@@ -500,6 +503,31 @@ describe('globSync - exclude', function() {
500503
assert.deepStrictEqual(actual, normalized);
501504
});
502505
}
506+
507+
test('applies function exclude to terminal globstar results', () => {
508+
const actual = globSync('**', {
509+
cwd: fixtureDir,
510+
exclude: excludesNestedFile,
511+
});
512+
assert.ok(!actual.includes(excludedNestedFile));
513+
});
514+
515+
test('applies function exclude to terminal pattern results', () => {
516+
const actual = globSync('**/d', {
517+
cwd: fixtureDir,
518+
exclude: excludesNestedFile,
519+
});
520+
assert.ok(!actual.includes(excludedNestedFile));
521+
});
522+
523+
test('applies function exclude to terminal results with file types', () => {
524+
const actual = globSync('**/d', {
525+
cwd: fixtureDir,
526+
exclude: (dirent) => normalizeDirent(dirent) === excludedNestedFile,
527+
withFileTypes: true,
528+
});
529+
assert.ok(!actual.map(normalizeDirent).includes(excludedNestedFile));
530+
});
503531
});
504532

505533
describe('glob - exclude', function() {
@@ -517,6 +545,31 @@ describe('glob - exclude', function() {
517545
assert.deepStrictEqual(actual, normalized);
518546
});
519547
}
548+
549+
test('applies function exclude to terminal globstar results', async () => {
550+
const actual = await promisified('**', {
551+
cwd: fixtureDir,
552+
exclude: excludesNestedFile,
553+
});
554+
assert.ok(!actual.includes(excludedNestedFile));
555+
});
556+
557+
test('applies function exclude to terminal pattern results', async () => {
558+
const actual = await promisified('**/d', {
559+
cwd: fixtureDir,
560+
exclude: excludesNestedFile,
561+
});
562+
assert.ok(!actual.includes(excludedNestedFile));
563+
});
564+
565+
test('applies function exclude to terminal results with file types', async () => {
566+
const actual = await promisified('**/d', {
567+
cwd: fixtureDir,
568+
exclude: (dirent) => normalizeDirent(dirent) === excludedNestedFile,
569+
withFileTypes: true,
570+
});
571+
assert.ok(!actual.map(normalizeDirent).includes(excludedNestedFile));
572+
});
520573
});
521574

522575
describe('fsPromises glob - exclude', function() {
@@ -536,6 +589,40 @@ describe('fsPromises glob - exclude', function() {
536589
assert.deepStrictEqual(actual.sort(), normalized);
537590
});
538591
}
592+
593+
test('applies function exclude to terminal globstar results', async () => {
594+
const actual = [];
595+
for await (const item of asyncGlob('**', {
596+
cwd: fixtureDir,
597+
exclude: excludesNestedFile,
598+
})) {
599+
actual.push(item);
600+
}
601+
assert.ok(!actual.includes(excludedNestedFile));
602+
});
603+
604+
test('applies function exclude to terminal pattern results', async () => {
605+
const actual = [];
606+
for await (const item of asyncGlob('**/d', {
607+
cwd: fixtureDir,
608+
exclude: excludesNestedFile,
609+
})) {
610+
actual.push(item);
611+
}
612+
assert.ok(!actual.includes(excludedNestedFile));
613+
});
614+
615+
test('applies function exclude to terminal results with file types', async () => {
616+
const actual = [];
617+
for await (const item of asyncGlob('**/d', {
618+
cwd: fixtureDir,
619+
exclude: (dirent) => normalizeDirent(dirent) === excludedNestedFile,
620+
withFileTypes: true,
621+
})) {
622+
actual.push(item);
623+
}
624+
assert.ok(!actual.map(normalizeDirent).includes(excludedNestedFile));
625+
});
539626
});
540627

541628
const followSymlinkPattern = 'follow/**';

0 commit comments

Comments
 (0)