fs: detect dot files when using globstar

Using globstar in glob pattern should not prevent dot (hidden) files
from being matched.

PR-URL: https://github.com/nodejs/node/pull/61012
Fixes: https://github.com/nodejs/node/issues/56321
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com>
This commit is contained in:
Robin van Wijngaarden
2025-12-11 22:12:03 +01:00
committed by GitHub
parent a00d95c73d
commit 4db307a4b0
2 changed files with 33 additions and 12 deletions

View File

@@ -442,7 +442,18 @@ class Glob {
const fromSymlink = pattern.symlinks.has(index);
if (current === lazyMinimatch().GLOBSTAR) {
if (entry.name[0] === '.' || (this.#exclude && this.#exclude(this.#withFileTypes ? entry : entry.name))) {
const isDot = entry.name[0] === '.';
const nextMatches = pattern.test(nextIndex, entry.name);
let nextNonGlobIndex = nextIndex;
while (pattern.at(nextNonGlobIndex) === lazyMinimatch().GLOBSTAR) {
nextNonGlobIndex++;
}
const matchesDot = isDot && pattern.test(nextNonGlobIndex, entry.name);
if ((isDot && !matchesDot) ||
(this.#exclude && this.#exclude(this.#withFileTypes ? entry : entry.name))) {
continue;
}
if (!fromSymlink && entry.isDirectory()) {
@@ -455,7 +466,6 @@ class Glob {
// Any pattern after ** is also a potential pattern
// so we can already test it here
const nextMatches = pattern.test(nextIndex, entry.name);
if (nextMatches && nextIndex === last && !isLast) {
// If next pattern is the last one, add to results
this.#results.add(entryPath);
@@ -642,7 +652,18 @@ class Glob {
const fromSymlink = pattern.symlinks.has(index);
if (current === lazyMinimatch().GLOBSTAR) {
if (entry.name[0] === '.' || (this.#exclude && this.#exclude(this.#withFileTypes ? entry : entry.name))) {
const isDot = entry.name[0] === '.';
const nextMatches = pattern.test(nextIndex, entry.name);
let nextNonGlobIndex = nextIndex;
while (pattern.at(nextNonGlobIndex) === lazyMinimatch().GLOBSTAR) {
nextNonGlobIndex++;
}
const matchesDot = isDot && pattern.test(nextNonGlobIndex, entry.name);
if ((isDot && !matchesDot) ||
(this.#exclude && this.#exclude(this.#withFileTypes ? entry : entry.name))) {
continue;
}
if (!fromSymlink && entry.isDirectory()) {
@@ -650,22 +671,17 @@ class Glob {
subPatterns.add(index);
} else if (!fromSymlink && index === last) {
// If ** is last, add to results
if (!this.#results.has(entryPath)) {
if (this.#results.add(entryPath)) {
yield this.#withFileTypes ? entry : entryPath;
}
if (!this.#results.has(entryPath) && this.#results.add(entryPath)) {
yield this.#withFileTypes ? entry : entryPath;
}
}
// Any pattern after ** is also a potential pattern
// so we can already test it here
const nextMatches = pattern.test(nextIndex, entry.name);
if (nextMatches && nextIndex === last && !isLast) {
// If next pattern is the last one, add to results
if (!this.#results.has(entryPath)) {
if (this.#results.add(entryPath)) {
yield this.#withFileTypes ? entry : entryPath;
}
if (!this.#results.has(entryPath) && this.#results.add(entryPath)) {
yield this.#withFileTypes ? entry : entryPath;
}
} else if (nextMatches && entry.isDirectory()) {
// Pattern matched, meaning two patterns forward

View File

@@ -30,6 +30,8 @@ async function setup() {
'a/cb/e/f',
'a/x/.y/b',
'a/z/.y/b',
'a/.b',
'a/b/.b',
].map((f) => resolve(fixtureDir, f));
const symlinkTo = resolve(fixtureDir, 'a/symlink/a/b/c');
@@ -188,6 +190,9 @@ const patterns = {
],
'*/*/*/f': ['a/bc/e/f', 'a/cb/e/f'],
'./**/f': ['a/bc/e/f', 'a/cb/e/f'],
'**/.b': ['a/.b', 'a/b/.b'],
'./**/.b': ['a/.b', 'a/b/.b'],
'a/**/.b': ['a/.b', 'a/b/.b'],
'a/symlink/a/b/c/a/b/c/a/b/c//a/b/c////a/b/c/**/b/c/**': common.isWindows ? [] : [
'a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c',
'a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a',