Allow negation of ignore and only patterns. (#5625)

This commit is contained in:
Logan Smyth
2017-04-12 11:10:33 -07:00
committed by GitHub
parent 2ea3338b8e
commit d1c954b36f
5 changed files with 153 additions and 41 deletions

View File

@@ -46,63 +46,63 @@ class ConfigChainBuilder {
): boolean {
if (!this.filename) return false;
if (ignore) {
if (!Array.isArray(ignore)) {
throw new Error(`.ignore should be an array, was ${JSON.stringify(ignore)}`);
}
for (const pattern of ignore) {
if (this.matchesPattern(pattern, dirname)) return true;
}
if (ignore && !Array.isArray(ignore)) {
throw new Error(`.ignore should be an array, ${JSON.stringify(ignore)} given`);
}
if (only) {
if (!Array.isArray(only)) {
throw new Error(`.only should be an array, was ${JSON.stringify(only)}`);
}
for (const pattern of only) {
if (this.matchesPattern(pattern, dirname)) return false;
}
return true;
if (only && !Array.isArray(only)) {
throw new Error(`.only should be an array, ${JSON.stringify(only)} given`);
}
return false;
return (ignore && this.matchesPatterns(ignore, dirname)) ||
(only && !this.matchesPatterns(only, dirname));
}
/**
* Returns result of calling function with filename if pattern is a function.
* Otherwise returns result of matching pattern Regex with filename.
*/
matchesPattern(pattern: string | Function | RegExp, dirname: string) {
if (typeof pattern === "string") {
// Lazy-init so we don't initialize this for files that have no glob patterns.
if (!this.possibleDirs) {
this.possibleDirs = [];
matchesPatterns(patterns: Array<string | Function | RegExp>, dirname: string) {
const res = [];
const strings = [];
const fns = [];
if (this.filename) {
this.possibleDirs.push(this.filename);
patterns.forEach((pattern) => {
const type = typeof pattern;
if (type === "string") strings.push(pattern);
else if (type === "function") fns.push(pattern);
else res.push(pattern);
});
let current = this.filename;
while (true) {
const previous = current;
current = path.dirname(current);
if (previous === current) break;
// Lazy-init so we don't initialize this for files that have no glob patterns.
if (strings.length > 0 && !this.possibleDirs) {
this.possibleDirs = [];
this.possibleDirs.push(current);
}
if (this.filename) {
this.possibleDirs.push(this.filename);
let current = this.filename;
while (true) {
const previous = current;
current = path.dirname(current);
if (previous === current) break;
this.possibleDirs.push(current);
}
}
return this.possibleDirs.some(micromatch.filter(path.resolve(dirname, pattern), {
nocase: true,
nonegate: true,
}));
} else if (typeof pattern === "function") {
return pattern(this.filename);
} else {
return pattern.test(this.filename);
}
return res.some((re) => re.test(this.filename)) ||
fns.some((fn) => fn(this.filename)) ||
this.possibleDirs.some(micromatch.filter(strings.map((pattern) => {
// Preserve the "!" prefix so that micromatch can use it for negation.
const negate = pattern[0] === "!";
if (negate) pattern = pattern.slice(1);
return (negate ? "!" : "") + path.resolve(dirname, pattern);
}, {
nocase: true,
})));
}
findConfigs(loc: string) {

View File

@@ -557,6 +557,104 @@ describe("buildConfigChain", function () {
assert.deepEqual(chain, expected);
});
it("should not ignore file matching negated file pattern", function () {
const chain = buildConfigChain({
filename: fixture("ignore-negate", "src.js"),
});
const expected = [
{
type: "options",
options: {
ignore: [
"root-ignore",
],
},
alias: fixture(".babelignore"),
loc: fixture(".babelignore"),
dirname: fixture(),
},
{
type: "options",
options: {
ignore: [
"*",
"!src.js",
],
},
alias: fixture("ignore-negate", ".babelrc"),
loc: fixture("ignore-negate", ".babelrc"),
dirname: fixture("ignore-negate"),
},
{
type: "arguments",
options: {
filename: fixture("ignore-negate", "src.js"),
},
alias: "base",
loc: "base",
dirname: base(),
},
];
assert.deepEqual(chain, expected);
const chain2 = buildConfigChain({
filename: fixture("ignore-negate", "src2.js"),
});
assert.equal(chain2, null);
});
it("should not ignore file matching negated folder pattern", function () {
const chain = buildConfigChain({
filename: fixture("ignore-negate-folder", "folder", "src.js"),
});
const expected = [
{
type: "options",
options: {
ignore: [
"root-ignore",
],
},
alias: fixture(".babelignore"),
loc: fixture(".babelignore"),
dirname: fixture(),
},
{
type: "options",
options: {
ignore: [
"*",
"!folder",
],
},
alias: fixture("ignore-negate-folder", ".babelrc"),
loc: fixture("ignore-negate-folder", ".babelrc"),
dirname: fixture("ignore-negate-folder"),
},
{
type: "arguments",
options: {
filename: fixture("ignore-negate-folder", "folder", "src.js"),
},
alias: "base",
loc: "base",
dirname: base(),
},
];
assert.deepEqual(chain, expected);
const chain2 = buildConfigChain({
filename: fixture("ignore-negate-folder", "src2.js"),
});
assert.equal(chain2, null);
});
it("js-json-config - should throw an error if both a .babelrc" +
" and a .babelrc.js are present", function () {
assert.throws(

View File

@@ -0,0 +1,6 @@
{
ignore: [
"*",
"!folder",
],
}

View File

@@ -0,0 +1,2 @@
# Blank .gitignore to ensure this directory exists.
!.gitignore

View File

@@ -0,0 +1,6 @@
{
ignore: [
"*",
"!src.js",
],
}