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

Commit

Permalink
fix: Ensure directories can be unignored.
Browse files Browse the repository at this point in the history
Allows the use of negated patterns in the ignores array to unignore a
previously ignored directory.
  • Loading branch information
nzakas committed Oct 13, 2022
1 parent b85184e commit 206404c
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 55 deletions.
84 changes: 31 additions & 53 deletions src/config-array.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,50 +175,42 @@ function normalizeSync(items, context, extraConfigTypes) {
* @param {string} relativeFilePath The relative path of the file to check.
* @returns {boolean} True if the path should be ignored and false if not.
*/
function shouldIgnoreFilePath(ignores, filePath, relativeFilePath) {
function shouldIgnorePath(ignores, filePath, relativeFilePath) {

// all files outside of the basePath are ignored
if (relativeFilePath.startsWith('..')) {
return true;
}

let shouldIgnore = false;
return ignores.reduce((ignored, matcher) => {

for (const matcher of ignores) {
if (!ignored) {

if (typeof matcher === 'function') {
shouldIgnore = shouldIgnore || matcher(filePath);
continue;
}

/*
* If there's a negated pattern, that means anything matching
* must NOT be ignored. To do that, we need to use the `flipNegate`
* option for minimatch to check if the filepath matches the
* pattern specified after the !, and if that result is true,
* then we return false immediately because this file should
* never be ignored.
*/
if (matcher.startsWith('!')) {
if (typeof matcher === 'function') {
return matcher(filePath);
}

/*
* The file must already be ignored in order to apply a negated
* pattern, because negated patterns simply remove files that
* would already be ignored.
*/
if (shouldIgnore &&
doMatch(relativeFilePath, matcher, {
flipNegate: true
})) {
return false;
// don't check negated patterns because we're not ignored yet
if (!matcher.startsWith('!')) {
return doMatch(relativeFilePath, matcher);
}
} else {
shouldIgnore = shouldIgnore || doMatch(relativeFilePath, matcher);

// otherwise we're still not ignored
return false;

}

}
// only need to check negated patterns because we're ignored
if (typeof matcher === 'string' && matcher.startsWith('!')) {
return !doMatch(relativeFilePath, matcher, {
flipNegate: true
});
}

return ignored;

}, false);

return shouldIgnore;
}

/**
Expand Down Expand Up @@ -273,7 +265,7 @@ function pathMatches(filePath, basePath, config) {
* if there are any files to ignore.
*/
if (filePathMatchesPattern && config.ignores) {
filePathMatchesPattern = !shouldIgnoreFilePath(config.ignores, filePath, relativeFilePath);
filePathMatchesPattern = !shouldIgnorePath(config.ignores, filePath, relativeFilePath);
}

return filePathMatchesPattern;
Expand Down Expand Up @@ -597,7 +589,7 @@ export class ConfigArray extends Array {
// TODO: Maybe move elsewhere? Maybe combine with getConfig() logic?
const relativeFilePath = path.relative(this.basePath, filePath);

if (shouldIgnoreFilePath(this.ignores, filePath, relativeFilePath)) {
if (shouldIgnorePath(this.ignores, filePath, relativeFilePath)) {
debug(`Ignoring ${filePath}`);

// cache and return result
Expand Down Expand Up @@ -655,7 +647,7 @@ export class ConfigArray extends Array {
// TODO: Maybe move elsewhere?
const relativeFilePath = path.relative(this.basePath, filePath);

if (shouldIgnoreFilePath(this.ignores, filePath, relativeFilePath)) {
if (shouldIgnorePath(this.ignores, filePath, relativeFilePath)) {
debug(`Ignoring ${filePath} based on file pattern`);

// cache and return result - finalConfig is undefined at this point
Expand Down Expand Up @@ -765,25 +757,11 @@ export class ConfigArray extends Array {
return cache.get(relativeDirectoryPath);
}

// if we've made it here, it means there's nothing in the cache
const result = this.ignores.some(matcher => {

if (typeof matcher === 'function') {
return matcher(relativeDirectoryPath);
}

// skip negated patterns because you can't unignore directories
if (matcher.startsWith('!')) {
return false;
}

// patterns ending with ** never match directories
if (matcher.endsWith('/**')) {
return false;
}

return doMatch(relativeDirectoryPath, matcher);
});
const result = shouldIgnorePath(
this.ignores.filter(matcher => typeof matcher === 'function' || !matcher.endsWith('/**')),
directoryPath,
relativeDirectoryPath
);

cache.set(relativeDirectoryPath, result);

Expand Down
43 changes: 41 additions & 2 deletions tests/config-array.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,26 @@ describe('ConfigArray', () => {
expect(configs.isFileIgnored(path.join(basePath, 'ignoreme/foo.js'))).to.be.true;
});

it('should return false when file is inside of ignored directory', () => {
configs = new ConfigArray([
{
files: ['**/*.js']
},
{
ignores: [
'foo/*',
'!foo/bar'
]
}
], {
basePath
});

configs.normalizeSync();

expect(configs.isFileIgnored(path.join(basePath, 'foo/bar/a.js'))).to.be.false;
});

});

describe('isDirectoryIgnored()', () => {
Expand Down Expand Up @@ -1017,7 +1037,7 @@ describe('ConfigArray', () => {
expect(configs.isDirectoryIgnored(path.join(basePath, 'node_modules') + '/'), 'Trailing slash').to.be.false;
});

it('should return true and not check negated pattern when there is a negated pattern', () => {
it('should return true when directory matches and there is a negated pattern', () => {
configs = new ConfigArray([
{
ignores: ['**/foo/', '!**/node_modules']
Expand All @@ -1032,7 +1052,7 @@ describe('ConfigArray', () => {
expect(configs.isDirectoryIgnored(path.join(basePath, 'foo') + '/'), 'Trailing slash').to.be.true;
});

it('should return false and not check negated pattern when there is a negated pattern', () => {
it('should return false when directory doesn\'t match and there is a negated pattern', () => {
configs = new ConfigArray([
{
ignores: ['**/foo/', '!**/node_modules']
Expand All @@ -1047,6 +1067,25 @@ describe('ConfigArray', () => {
expect(configs.isDirectoryIgnored(path.join(basePath, 'bar') + '/'), 'Trailing slash').to.be.false;
});

it('should return false when ignored directory is unignored', () => {
configs = new ConfigArray([
{
ignores: [
'foo/*',
'!foo/bar'
]
}
], {
basePath
});

configs.normalizeSync();

expect(configs.isDirectoryIgnored(path.join(basePath, 'foo/bar'))).to.be.false;
expect(configs.isDirectoryIgnored(path.join(basePath, 'foo/bar/'))).to.be.false;
});


it('should return true when there is a directory relative to basePath in ignores', () => {
configs = new ConfigArray([
{
Expand Down

0 comments on commit 206404c

Please sign in to comment.