Remove micromatch and use a simple pattern matching utility.

This commit is contained in:
Logan Smyth 2018-07-12 19:29:26 -07:00
parent c4f67bfa57
commit 6d177ba4c5
8 changed files with 121 additions and 128 deletions

View File

@ -41,7 +41,6 @@
"debug": "^3.1.0", "debug": "^3.1.0",
"json5": "^0.5.0", "json5": "^0.5.0",
"lodash": "^4.17.5", "lodash": "^4.17.5",
"micromatch": "^2.3.11",
"resolve": "^1.3.2", "resolve": "^1.3.2",
"semver": "^5.4.1", "semver": "^5.4.1",
"source-map": "^0.5.0" "source-map": "^0.5.0"

View File

@ -1,7 +1,6 @@
// @flow // @flow
import path from "path"; import path from "path";
import micromatch from "micromatch";
import buildDebug from "debug"; import buildDebug from "debug";
import { import {
validate, validate,
@ -10,6 +9,7 @@ import {
type ConfigApplicableTest, type ConfigApplicableTest,
type BabelrcSearch, type BabelrcSearch,
} from "./validation/options"; } from "./validation/options";
import pathPatternToRegex from "./pattern-to-regex";
const debug = buildDebug("babel:config:config-chain"); const debug = buildDebug("babel:config:config-chain");
@ -52,11 +52,6 @@ export type ConfigContext = {
envName: string, envName: string,
}; };
type ConfigContextNamed = {
...ConfigContext,
filename: string,
};
/** /**
* Build a config chain for a given preset. * Build a config chain for a given preset.
*/ */
@ -217,7 +212,7 @@ function babelrcLoadEnabled(
const absoluteRoot = context.root; const absoluteRoot = context.root;
// Fast path to avoid having to load micromatch if the babelrc is just // Fast path to avoid having to match patterns if the babelrc is just
// loading in the standard root directory. // loading in the standard root directory.
if (babelrcRoots === undefined) { if (babelrcRoots === undefined) {
return pkgData.directories.indexOf(absoluteRoot) !== -1; return pkgData.directories.indexOf(absoluteRoot) !== -1;
@ -225,15 +220,23 @@ function babelrcLoadEnabled(
let babelrcPatterns = babelrcRoots; let babelrcPatterns = babelrcRoots;
if (!Array.isArray(babelrcPatterns)) babelrcPatterns = [babelrcPatterns]; if (!Array.isArray(babelrcPatterns)) babelrcPatterns = [babelrcPatterns];
babelrcPatterns = babelrcPatterns.map(pat => path.resolve(context.cwd, pat)); babelrcPatterns = babelrcPatterns.map(pat => {
return typeof pat === "string" ? path.resolve(context.cwd, pat) : pat;
});
// Fast path to avoid having to load micromatch if the babelrc is just // Fast path to avoid having to match patterns if the babelrc is just
// loading in the standard root directory. // loading in the standard root directory.
if (babelrcPatterns.length === 1 && babelrcPatterns[0] === absoluteRoot) { if (babelrcPatterns.length === 1 && babelrcPatterns[0] === absoluteRoot) {
return pkgData.directories.indexOf(absoluteRoot) !== -1; return pkgData.directories.indexOf(absoluteRoot) !== -1;
} }
return micromatch(pkgData.directories, babelrcPatterns).length > 0; return babelrcPatterns.some(pat => {
if (typeof pat === "string") pat = pathPatternToRegex(pat, context.cwd);
return pkgData.directories.some(directory => {
return matchPattern(pat, context.cwd, directory);
});
});
} }
const validateConfigFile = makeWeakCache( const validateConfigFile = makeWeakCache(
@ -583,20 +586,9 @@ function configFieldIsApplicable(
test: ConfigApplicableTest, test: ConfigApplicableTest,
dirname: string, dirname: string,
): boolean { ): boolean {
if (typeof context.filename !== "string") {
throw new Error(
`Configuration contains explicit test/include/exclude checks, but no filename was passed to Babel`,
);
}
// $FlowIgnore - Flow refinements aren't quite smart enough for this :(
const ctx: ConfigContextNamed = context;
const patterns = Array.isArray(test) ? test : [test]; const patterns = Array.isArray(test) ? test : [test];
// Disabling negation here because it's a bit buggy from return matchesPatterns(context, patterns, dirname);
// https://github.com/babel/babel/issues/6907 and it's not clear that it is
// needed since users can use 'exclude' alongside 'test'/'include'.
return matchesPatterns(ctx, patterns, dirname, false /* allowNegation */);
} }
/** /**
@ -608,43 +600,24 @@ function shouldIgnore(
only: ?IgnoreList, only: ?IgnoreList,
dirname: string, dirname: string,
): boolean { ): boolean {
if (ignore) { if (ignore && matchesPatterns(context, ignore, dirname)) {
if (typeof context.filename !== "string") { debug(
throw new Error( "Ignored %o because it matched one of %O from %o",
`Configuration contains ignore checks, but no filename was passed to Babel`, context.filename,
); ignore,
} dirname,
// $FlowIgnore - Flow refinements aren't quite smart enough for this :( );
const ctx: ConfigContextNamed = context; return true;
if (matchesPatterns(ctx, ignore, dirname)) {
debug(
"Ignored %o because it matched one of %O from %o",
context.filename,
ignore,
dirname,
);
return true;
}
} }
if (only) { if (only && !matchesPatterns(context, only, dirname)) {
if (typeof context.filename !== "string") { debug(
throw new Error( "Ignored %o because it failed to match one of %O from %o",
`Configuration contains ignore checks, but no filename was passed to Babel`, context.filename,
); only,
} dirname,
// $FlowIgnore - Flow refinements aren't quite smart enough for this :( );
const ctx: ConfigContextNamed = context; return true;
if (!matchesPatterns(ctx, only, dirname)) {
debug(
"Ignored %o because it failed to match one of %O from %o",
context.filename,
only,
dirname,
);
return true;
}
} }
return false; return false;
@ -655,64 +628,26 @@ function shouldIgnore(
* Otherwise returns result of matching pattern Regex with filename. * Otherwise returns result of matching pattern Regex with filename.
*/ */
function matchesPatterns( function matchesPatterns(
context: ConfigContextNamed, context: ConfigContext,
patterns: IgnoreList, patterns: IgnoreList,
dirname: string, dirname: string,
allowNegation?: boolean = true,
): boolean { ): boolean {
const res = []; return patterns.some(pattern =>
const strings = []; matchPattern(pattern, dirname, context.filename),
const fns = []; );
patterns.forEach(pattern => {
if (typeof pattern === "string") strings.push(pattern);
else if (typeof pattern === "function") fns.push(pattern);
else res.push(pattern);
});
const filename = context.filename;
if (res.some(re => re.test(context.filename))) return true;
if (fns.some(fn => fn(filename))) return true;
if (strings.length > 0) {
const possibleDirs = getPossibleDirs(context);
const absolutePatterns = strings.map(pattern => {
// Preserve the "!" prefix so that micromatch can use it for negation.
const negate = pattern[0] === "!";
if (negate && !allowNegation) {
throw new Error(`Negation of file paths is not supported.`);
}
if (negate) pattern = pattern.slice(1);
return (negate ? "!" : "") + path.resolve(dirname, pattern);
});
if (
micromatch(possibleDirs, absolutePatterns, {
nocase: true,
nonegate: !allowNegation,
}).length > 0
) {
return true;
}
}
return false;
} }
const getPossibleDirs = makeWeakCache((context: ConfigContextNamed) => { function matchPattern(pattern, dirname, pathToTest): boolean {
let current = context.filename; if (typeof pattern === "function") return !!pattern(pathToTest);
if (typeof current !== "string") return [];
const possibleDirs = [current]; if (typeof pathToTest !== "string") {
while (true) { throw new Error(
const previous = current; `Configuration contains string/RegExp pattern, but no filename was passed to Babel`,
current = path.dirname(current); );
if (previous === current) break;
possibleDirs.push(current);
} }
return possibleDirs; if (typeof pattern === "string") {
}); pattern = pathPatternToRegex(pattern, dirname);
}
return pattern.test(pathToTest);
}

View File

@ -12,6 +12,7 @@ import {
} from "../caching"; } from "../caching";
import makeAPI from "../helpers/config-api"; import makeAPI from "../helpers/config-api";
import { makeStaticFileCache } from "./utils"; import { makeStaticFileCache } from "./utils";
import pathPatternToRegex from "../pattern-to-regex";
import type { FilePackageData, RelativeConfig, ConfigFile } from "./types"; import type { FilePackageData, RelativeConfig, ConfigFile } from "./types";
const debug = buildDebug("babel:config:loading:files:configuration"); const debug = buildDebug("babel:config:loading:files:configuration");
@ -240,15 +241,24 @@ const readConfigJSON5 = makeStaticFileCache((filepath, content) => {
}); });
const readIgnoreConfig = makeStaticFileCache((filepath, content) => { const readIgnoreConfig = makeStaticFileCache((filepath, content) => {
const ignore = content const ignoreDir = path.dirname(filepath);
const ignorePatterns = content
.split("\n") .split("\n")
.map(line => line.replace(/#(.*?)$/, "").trim()) .map(line => line.replace(/#(.*?)$/, "").trim())
.filter(line => !!line); .filter(line => !!line);
for (const pattern of ignorePatterns) {
if (pattern[0] === "!") {
throw new Error(`Negation of file paths is not supported.`);
}
}
return { return {
filepath, filepath,
dirname: path.dirname(filepath), dirname: path.dirname(filepath),
ignore, ignore: ignorePatterns.map(pattern =>
pathPatternToRegex(pattern, ignoreDir),
),
}; };
}); });

View File

@ -9,7 +9,7 @@ export type ConfigFile = {
export type IgnoreFile = { export type IgnoreFile = {
filepath: string, filepath: string,
dirname: string, dirname: string,
ignore: Array<string>, ignore: Array<RegExp>,
}; };
export type RelativeConfig = { export type RelativeConfig = {

View File

@ -0,0 +1,51 @@
// @flow
import path from "path";
import escapeRegExp from "lodash/escapeRegExp";
const sep = `\\${path.sep}`;
const endSep = `(?:${sep}|$)`;
const substitution = `[^${sep}]+`;
const starPat = `(?:${substitution}${sep})`;
const starPatLast = `(?:${substitution}${endSep})`;
const starStarPat = `${starPat}*?`;
const starStarPatLast = `${starPat}*?${starPatLast}?`;
/**
* Implement basic pattern matching that will allow users to do the simple
* tests with * and **. If users want full complex pattern matching, then can
* always use regex matching, or function validation.
*/
export default function pathToPattern(
pattern: string,
dirname: string,
): RegExp {
const parts = path.resolve(dirname, pattern).split(path.sep);
return new RegExp(
[
"^",
...parts.map((part, i) => {
const last = i === parts.length - 1;
// ** matches 0 or more path parts.
if (part === "**") return last ? starStarPatLast : starStarPat;
// * matches 1 path part.
if (part === "*") return last ? starPatLast : starPat;
// *.ext matches a wildcard with an extension.
if (part.indexOf("*.") === 0) {
return (
substitution + escapeRegExp(part.slice(1)) + (last ? endSep : sep)
);
}
// Otherwise match the pattern text.
return escapeRegExp(part) + (last ? endSep : sep);
}),
].join(""),
);
}

View File

@ -192,14 +192,14 @@ export function assertBabelrcSearch(
if (Array.isArray(value)) { if (Array.isArray(value)) {
value.forEach((item, i) => { value.forEach((item, i) => {
if (typeof item !== "string") { if (!checkValidTest(value)) {
throw new Error(`.${key}[${i}] must be a string.`); throw new Error(`.${key}[${i}] must be a string/Function/RegExp.`);
} }
}); });
} else if (typeof value !== "string") { } else if (!checkValidTest(value)) {
throw new Error( throw new Error(
`.${key} must be a undefined, a boolean, a string, ` + `.${key} must be a undefined, a boolean, a string/Function/RegExp ` +
`or an array of strings, got ${JSON.stringify(value)}`, `or an array of those, got ${JSON.stringify(value)}`,
); );
} }
return (value: any); return (value: any);

View File

@ -242,7 +242,7 @@ export type OverridesList = Array<ValidatedOptions>;
export type ConfigApplicableTest = IgnoreItem | Array<IgnoreItem>; export type ConfigApplicableTest = IgnoreItem | Array<IgnoreItem>;
export type ConfigFileSearch = string | boolean; export type ConfigFileSearch = string | boolean;
export type BabelrcSearch = boolean | string | Array<string>; export type BabelrcSearch = boolean | IgnoreItem | IgnoreList;
export type SourceMapsOption = boolean | "inline" | "both"; export type SourceMapsOption = boolean | "inline" | "both";
export type SourceTypeOption = "module" | "script" | "unambiguous"; export type SourceTypeOption = "module" | "script" | "unambiguous";
export type CompactOption = boolean | "auto"; export type CompactOption = boolean | "auto";

View File

@ -5,11 +5,11 @@ import Plugin from "../lib/config/plugin";
import generator from "@babel/generator"; import generator from "@babel/generator";
function assertIgnored(result) { function assertIgnored(result) {
expect(result).toBeFalsy(); expect(result).toBeNull();
} }
function assertNotIgnored(result) { function assertNotIgnored(result) {
expect(result.ignored).toBeFalsy(); expect(result).not.toBeNull();
} }
function transform(code, opts) { function transform(code, opts) {
@ -36,13 +36,11 @@ function transformFileSync(filename, opts) {
}); });
} }
// shim
function transformAsync(code, opts) { function transformAsync(code, opts) {
return { return babel.transformAsync(code, {
then: function(resolve) { cwd: __dirname,
resolve(transform(code, opts)); ...opts,
}, });
};
} }
describe("parser and generator options", function() { describe("parser and generator options", function() {