Skip to content
This repository was archived by the owner on Jun 10, 2024. It is now read-only.

Commit 8e17f4a

Browse files
authored
fix: Ensure gitignore-style directory ignores (#74)
1 parent 8b22cd0 commit 8e17f4a

File tree

2 files changed

+111
-21
lines changed

2 files changed

+111
-21
lines changed

src/config-array.js

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -841,7 +841,9 @@ export class ConfigArray extends Array {
841841

842842
assertNormalized(this);
843843

844-
const relativeDirectoryPath = path.relative(this.basePath, directoryPath);
844+
const relativeDirectoryPath = path.relative(this.basePath, directoryPath)
845+
.replace(/\\/g, '/');
846+
845847
if (relativeDirectoryPath.startsWith('..')) {
846848
return true;
847849
}
@@ -852,14 +854,35 @@ export class ConfigArray extends Array {
852854
if (cache.has(relativeDirectoryPath)) {
853855
return cache.get(relativeDirectoryPath);
854856
}
855-
856-
// first check non-/** paths
857-
const result = shouldIgnorePath(
858-
this.ignores, //.filter(matcher => typeof matcher === "function" || !matcher.endsWith("/**")),
859-
directoryPath,
860-
relativeDirectoryPath
861-
);
862857

858+
const directoryParts = relativeDirectoryPath.split('/');
859+
let relativeDirectoryToCheck = '';
860+
let result = false;
861+
862+
/*
863+
* In order to get the correct gitignore-style ignores, where an
864+
* ignored parent directory cannot have any descendants unignored,
865+
* we need to check every directory starting at the parent all
866+
* the way down to the actual requested directory.
867+
*
868+
* We aggressively cache all of this info to make sure we don't
869+
* have to recalculate everything for every call.
870+
*/
871+
do {
872+
873+
relativeDirectoryToCheck += directoryParts.shift() + '/';
874+
875+
result = shouldIgnorePath(
876+
this.ignores,
877+
path.join(this.basePath, relativeDirectoryToCheck),
878+
relativeDirectoryToCheck
879+
);
880+
881+
cache.set(relativeDirectoryToCheck, result);
882+
883+
} while (!result && directoryParts.length);
884+
885+
// also cache the result for the requested path
863886
cache.set(relativeDirectoryPath, result);
864887

865888
return result;

tests/config-array.test.js

Lines changed: 80 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,7 +1007,7 @@ describe('ConfigArray', () => {
10071007
expect(configs.isFileIgnored(path.join(basePath, 'foo/bar/a.js'))).to.be.true;
10081008
});
10091009

1010-
it('should return false when an ignored directory is later unignored with **', () => {
1010+
it('should return true when an ignored directory is later negated with **', () => {
10111011
configs = new ConfigArray([
10121012
{
10131013
files: ['**/*.js']
@@ -1028,10 +1028,10 @@ describe('ConfigArray', () => {
10281028

10291029
configs.normalizeSync();
10301030

1031-
expect(configs.isFileIgnored(path.join(basePath, 'node_modules/package/a.js'))).to.be.false;
1031+
expect(configs.isFileIgnored(path.join(basePath, 'node_modules/package/a.js'))).to.be.true;
10321032
});
10331033

1034-
it('should return false when an ignored directory is later unignored with *', () => {
1034+
it('should return true when an ignored directory is later negated with *', () => {
10351035
configs = new ConfigArray([
10361036
{
10371037
files: ['**/*.js']
@@ -1052,7 +1052,7 @@ describe('ConfigArray', () => {
10521052

10531053
configs.normalizeSync();
10541054

1055-
expect(configs.isFileIgnored(path.join(basePath, 'node_modules/package/a.js'))).to.be.false;
1055+
expect(configs.isFileIgnored(path.join(basePath, 'node_modules/package/a.js'))).to.be.true;
10561056
});
10571057

10581058
it('should return true when there are only patterns ending with /*', () => {
@@ -1216,7 +1216,7 @@ describe('ConfigArray', () => {
12161216
expect(configs.isDirectoryIgnored(path.join(basePath, 'node_modules') + '/'), 'Trailing slash').to.be.true;
12171217
});
12181218

1219-
it('should return false when a directory followed by ** is in ignores', () => {
1219+
it('should return true when a directory followed by ** is in ignores', () => {
12201220
configs = new ConfigArray([
12211221
{
12221222
ignores: ['**/node_modules/**']
@@ -1227,8 +1227,8 @@ describe('ConfigArray', () => {
12271227

12281228
configs.normalizeSync();
12291229

1230-
expect(configs.isDirectoryIgnored(path.join(basePath, 'node_modules'))).to.be.false;
1231-
expect(configs.isDirectoryIgnored(path.join(basePath, 'node_modules') + '/')).to.be.false;
1230+
expect(configs.isDirectoryIgnored(path.join(basePath, 'node_modules'))).to.be.true;
1231+
expect(configs.isDirectoryIgnored(path.join(basePath, 'node_modules') + '/')).to.be.true;
12321232

12331233
});
12341234

@@ -1361,7 +1361,7 @@ describe('ConfigArray', () => {
13611361
});
13621362

13631363

1364-
it('should return false when an ignored directory is later unignored with **', () => {
1364+
it('should return true when an ignored directory is later negated with **', () => {
13651365
configs = new ConfigArray([
13661366
{
13671367
files: ['**/*.js']
@@ -1382,11 +1382,11 @@ describe('ConfigArray', () => {
13821382

13831383
configs.normalizeSync();
13841384

1385-
expect(configs.isDirectoryIgnored(path.join(basePath, 'node_modules/package'))).to.be.false;
1386-
expect(configs.isDirectoryIgnored(path.join(basePath, 'node_modules/package/'))).to.be.false;
1385+
expect(configs.isDirectoryIgnored(path.join(basePath, 'node_modules/package'))).to.be.true;
1386+
expect(configs.isDirectoryIgnored(path.join(basePath, 'node_modules/package/'))).to.be.true;
13871387
});
13881388

1389-
it('should return false when an ignored directory is later unignored with *', () => {
1389+
it('should return true when an ignored directory is later unignored with *', () => {
13901390
configs = new ConfigArray([
13911391
{
13921392
files: ['**/*.js']
@@ -1407,10 +1407,77 @@ describe('ConfigArray', () => {
14071407

14081408
configs.normalizeSync();
14091409

1410-
expect(configs.isDirectoryIgnored(path.join(basePath, 'node_modules/package'))).to.be.false;
1411-
expect(configs.isDirectoryIgnored(path.join(basePath, 'node_modules/package/'))).to.be.false;
1410+
expect(configs.isDirectoryIgnored(path.join(basePath, 'node_modules/package'))).to.be.true;
1411+
expect(configs.isDirectoryIgnored(path.join(basePath, 'node_modules/package/'))).to.be.true;
14121412
});
14131413

1414+
// https://github.com/eslint/eslint/pull/16579/files
1415+
describe('gitignore-style unignores', () => {
1416+
1417+
it('should return false when first-level subdirectories are ignored and then one is negated', () => {
1418+
configs = new ConfigArray([
1419+
{
1420+
ignores: [
1421+
'**/node_modules/*',
1422+
'!**/node_modules/foo/'
1423+
],
1424+
}
1425+
], { basePath });
1426+
1427+
configs.normalizeSync();
1428+
const directoryPath = path.resolve(basePath, 'node_modules/foo');
1429+
1430+
expect(configs.isDirectoryIgnored(directoryPath)).to.be.false;
1431+
});
1432+
1433+
it('should return false when first-level subdirectories are ignored with leading slash and then one is negated', () => {
1434+
configs = new ConfigArray([
1435+
{
1436+
ignores: [
1437+
'/**/node_modules/*',
1438+
'!**/node_modules/foo/'
1439+
],
1440+
}
1441+
], { basePath });
1442+
1443+
configs.normalizeSync();
1444+
const directoryPath = path.resolve(basePath, 'node_modules/foo');
1445+
1446+
expect(configs.isDirectoryIgnored(directoryPath)).to.be.false;
1447+
});
1448+
1449+
it('should return true when all descendant subdirectories are ignored and then one is negated', () => {
1450+
configs = new ConfigArray([
1451+
{
1452+
ignores: [
1453+
'**/node_modules/**',
1454+
'!**/node_modules/foo/'
1455+
],
1456+
}
1457+
], { basePath });
1458+
1459+
configs.normalizeSync();
1460+
const directoryPath = path.resolve(basePath, 'node_modules/foo');
1461+
1462+
expect(configs.isDirectoryIgnored(directoryPath)).to.be.true;
1463+
});
1464+
1465+
it('should return true when all descendant subdirectories are ignored and then other descendants are negated', () => {
1466+
configs = new ConfigArray([
1467+
{
1468+
ignores: [
1469+
'**/node_modules/**',
1470+
'!**/node_modules/foo/**'
1471+
],
1472+
}
1473+
], { basePath });
1474+
1475+
configs.normalizeSync();
1476+
const directoryPath = path.resolve(basePath, 'node_modules/foo');
1477+
1478+
expect(configs.isDirectoryIgnored(directoryPath)).to.be.true;
1479+
});
1480+
});
14141481

14151482
});
14161483

0 commit comments

Comments
 (0)