Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion lib/internal/fs/glob.js
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,10 @@ class Glob {
// consistent comparison before instantiating matchers.
const matchers = exclude
.map((pattern) => resolve(this.#root, pattern))
.map((pattern) => createMatcher(pattern));
// Exclude matching is a pure string comparison with no filesystem
// lookups, so unlike include patterns, literal patterns must also
// match case-insensitively on case-insensitive platforms.
.map((pattern) => createMatcher(pattern, { nocaseMagicOnly: false }));
this.#isExcluded = (value) =>
matchers.some((matcher) => matcher.match(value));
this.#results.setup(this.#root, this.#isExcluded);
Expand Down
28 changes: 28 additions & 0 deletions test/parallel/test-fs-glob.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -669,3 +669,31 @@ describe('globSync - ENOTDIR', function() {
}
});
});

// gh-58991: exclude patterns must apply the same case-sensitivity as include
// patterns. On case-insensitive filesystems include patterns match entries
// regardless of case, so literal exclude patterns must match that behavior.
const skipCaseTests = { skip: !common.isWindows && !common.isMacOS };
describe('glob - exclude case-insensitive consistency', skipCaseTests, function() {
test('literal exclude ignores case (sync)', () => {
assert.deepStrictEqual(
globSync('a/b', { cwd: fixtureDir, exclude: ['A/B'] }), []);
});
test('literal exclude matches differently-cased results (sync)', () => {
assert.deepStrictEqual(
globSync('A/b', { cwd: fixtureDir, exclude: ['a/b'] }), []);
});
test('literal exclude ignores case (async)', async () => {
const promisified = promisify(glob);
assert.deepStrictEqual(
await promisified('a/b', { cwd: fixtureDir, exclude: ['A/B'] }), []);
});
test('literal exclude ignores case (promise)', async () => {
const actual = [];
for await (const entry of asyncGlob(
'a/b', { cwd: fixtureDir, exclude: ['A/B'] })) {
actual.push(entry);
}
assert.deepStrictEqual(actual, []);
});
});
Loading