From 2854a187bea804b34611382fddc65cda9528ae00 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Sat, 14 Jul 2018 12:41:17 -0700 Subject: [PATCH 1/3] Remove dependency on barely-used package. --- packages/babel-traverse/package.json | 1 - packages/babel-traverse/src/path/index.js | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/babel-traverse/package.json b/packages/babel-traverse/package.json index 1fd1aa5660..83056808c0 100644 --- a/packages/babel-traverse/package.json +++ b/packages/babel-traverse/package.json @@ -16,7 +16,6 @@ "@babel/types": "7.0.0-beta.53", "debug": "^3.1.0", "globals": "^11.1.0", - "invariant": "^2.2.0", "lodash": "^4.17.5" }, "devDependencies": { diff --git a/packages/babel-traverse/src/path/index.js b/packages/babel-traverse/src/path/index.js index 63c70fecb1..dbdc0a08af 100644 --- a/packages/babel-traverse/src/path/index.js +++ b/packages/babel-traverse/src/path/index.js @@ -2,7 +2,6 @@ import type Hub from "../hub"; import type TraversalContext from "../context"; import * as virtualTypes from "./lib/virtual-types"; import buildDebug from "debug"; -import invariant from "invariant"; import traverse from "../index"; import Scope from "../scope"; import * as t from "@babel/types"; @@ -76,7 +75,9 @@ export default class NodePath { hub = parentPath.hub; } - invariant(parent, "To get a node path the parent needs to exist"); + if (!parent) { + throw new Error("To get a node path the parent needs to exist"); + } const targetNode = container[key]; From c4f67bfa577a1f5ca3fd21df49dd640b922e8602 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Sat, 14 Jul 2018 12:47:37 -0700 Subject: [PATCH 2/3] Leave it to users to clear the require cache if they want to. --- packages/babel-register/src/node.js | 1 - packages/babel-register/test/index.js | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-register/src/node.js b/packages/babel-register/src/node.js index fe9a46c15d..c73ddef062 100644 --- a/packages/babel-register/src/node.js +++ b/packages/babel-register/src/node.js @@ -101,7 +101,6 @@ function hookExtensions(exts) { export function revert() { if (piratesRevert) piratesRevert(); - delete require.cache[require.resolve(__filename)]; } register(); diff --git a/packages/babel-register/test/index.js b/packages/babel-register/test/index.js index 5643bdcf6f..1f54094e9d 100644 --- a/packages/babel-register/test/index.js +++ b/packages/babel-register/test/index.js @@ -52,6 +52,7 @@ describe("@babel/register", function() { function revertRegister() { if (babelRegister) { babelRegister.revert(); + delete require.cache[registerFile]; babelRegister = null; } } From 6d177ba4c554136e9d6175b4162d6d84fbc85c52 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Thu, 12 Jul 2018 19:29:26 -0700 Subject: [PATCH 3/3] Remove micromatch and use a simple pattern matching utility. --- packages/babel-core/package.json | 1 - .../babel-core/src/config/config-chain.js | 155 +++++------------- .../src/config/files/configuration.js | 14 +- packages/babel-core/src/config/files/types.js | 2 +- .../babel-core/src/config/pattern-to-regex.js | 51 ++++++ .../config/validation/option-assertions.js | 10 +- .../src/config/validation/options.js | 2 +- packages/babel-core/test/api.js | 14 +- 8 files changed, 121 insertions(+), 128 deletions(-) create mode 100644 packages/babel-core/src/config/pattern-to-regex.js diff --git a/packages/babel-core/package.json b/packages/babel-core/package.json index e93d8c73a9..815834f531 100644 --- a/packages/babel-core/package.json +++ b/packages/babel-core/package.json @@ -41,7 +41,6 @@ "debug": "^3.1.0", "json5": "^0.5.0", "lodash": "^4.17.5", - "micromatch": "^2.3.11", "resolve": "^1.3.2", "semver": "^5.4.1", "source-map": "^0.5.0" diff --git a/packages/babel-core/src/config/config-chain.js b/packages/babel-core/src/config/config-chain.js index aafebf0c8f..f160f8a253 100644 --- a/packages/babel-core/src/config/config-chain.js +++ b/packages/babel-core/src/config/config-chain.js @@ -1,7 +1,6 @@ // @flow import path from "path"; -import micromatch from "micromatch"; import buildDebug from "debug"; import { validate, @@ -10,6 +9,7 @@ import { type ConfigApplicableTest, type BabelrcSearch, } from "./validation/options"; +import pathPatternToRegex from "./pattern-to-regex"; const debug = buildDebug("babel:config:config-chain"); @@ -52,11 +52,6 @@ export type ConfigContext = { envName: string, }; -type ConfigContextNamed = { - ...ConfigContext, - filename: string, -}; - /** * Build a config chain for a given preset. */ @@ -217,7 +212,7 @@ function babelrcLoadEnabled( 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. if (babelrcRoots === undefined) { return pkgData.directories.indexOf(absoluteRoot) !== -1; @@ -225,15 +220,23 @@ function babelrcLoadEnabled( let babelrcPatterns = babelrcRoots; 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. if (babelrcPatterns.length === 1 && babelrcPatterns[0] === absoluteRoot) { 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( @@ -583,20 +586,9 @@ function configFieldIsApplicable( test: ConfigApplicableTest, dirname: string, ): 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]; - // Disabling negation here because it's a bit buggy from - // 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 */); + return matchesPatterns(context, patterns, dirname); } /** @@ -608,43 +600,24 @@ function shouldIgnore( only: ?IgnoreList, dirname: string, ): boolean { - if (ignore) { - if (typeof context.filename !== "string") { - throw new Error( - `Configuration contains ignore checks, but no filename was passed to Babel`, - ); - } - // $FlowIgnore - Flow refinements aren't quite smart enough for this :( - const ctx: ConfigContextNamed = context; - if (matchesPatterns(ctx, ignore, dirname)) { - debug( - "Ignored %o because it matched one of %O from %o", - context.filename, - ignore, - dirname, - ); - return true; - } + if (ignore && matchesPatterns(context, ignore, dirname)) { + debug( + "Ignored %o because it matched one of %O from %o", + context.filename, + ignore, + dirname, + ); + return true; } - if (only) { - if (typeof context.filename !== "string") { - throw new Error( - `Configuration contains ignore checks, but no filename was passed to Babel`, - ); - } - // $FlowIgnore - Flow refinements aren't quite smart enough for this :( - const ctx: ConfigContextNamed = context; - - if (!matchesPatterns(ctx, only, dirname)) { - debug( - "Ignored %o because it failed to match one of %O from %o", - context.filename, - only, - dirname, - ); - return true; - } + if (only && !matchesPatterns(context, only, dirname)) { + debug( + "Ignored %o because it failed to match one of %O from %o", + context.filename, + only, + dirname, + ); + return true; } return false; @@ -655,64 +628,26 @@ function shouldIgnore( * Otherwise returns result of matching pattern Regex with filename. */ function matchesPatterns( - context: ConfigContextNamed, + context: ConfigContext, patterns: IgnoreList, dirname: string, - allowNegation?: boolean = true, ): boolean { - const res = []; - const strings = []; - 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; + return patterns.some(pattern => + matchPattern(pattern, dirname, context.filename), + ); } -const getPossibleDirs = makeWeakCache((context: ConfigContextNamed) => { - let current = context.filename; - if (typeof current !== "string") return []; +function matchPattern(pattern, dirname, pathToTest): boolean { + if (typeof pattern === "function") return !!pattern(pathToTest); - const possibleDirs = [current]; - while (true) { - const previous = current; - current = path.dirname(current); - if (previous === current) break; - - possibleDirs.push(current); + if (typeof pathToTest !== "string") { + throw new Error( + `Configuration contains string/RegExp pattern, but no filename was passed to Babel`, + ); } - return possibleDirs; -}); + if (typeof pattern === "string") { + pattern = pathPatternToRegex(pattern, dirname); + } + return pattern.test(pathToTest); +} diff --git a/packages/babel-core/src/config/files/configuration.js b/packages/babel-core/src/config/files/configuration.js index 4547f5e4ec..66b39cb2c0 100644 --- a/packages/babel-core/src/config/files/configuration.js +++ b/packages/babel-core/src/config/files/configuration.js @@ -12,6 +12,7 @@ import { } from "../caching"; import makeAPI from "../helpers/config-api"; import { makeStaticFileCache } from "./utils"; +import pathPatternToRegex from "../pattern-to-regex"; import type { FilePackageData, RelativeConfig, ConfigFile } from "./types"; const debug = buildDebug("babel:config:loading:files:configuration"); @@ -240,15 +241,24 @@ const readConfigJSON5 = makeStaticFileCache((filepath, content) => { }); const readIgnoreConfig = makeStaticFileCache((filepath, content) => { - const ignore = content + const ignoreDir = path.dirname(filepath); + const ignorePatterns = content .split("\n") .map(line => line.replace(/#(.*?)$/, "").trim()) .filter(line => !!line); + for (const pattern of ignorePatterns) { + if (pattern[0] === "!") { + throw new Error(`Negation of file paths is not supported.`); + } + } + return { filepath, dirname: path.dirname(filepath), - ignore, + ignore: ignorePatterns.map(pattern => + pathPatternToRegex(pattern, ignoreDir), + ), }; }); diff --git a/packages/babel-core/src/config/files/types.js b/packages/babel-core/src/config/files/types.js index fa9d96fe5d..24649d010f 100644 --- a/packages/babel-core/src/config/files/types.js +++ b/packages/babel-core/src/config/files/types.js @@ -9,7 +9,7 @@ export type ConfigFile = { export type IgnoreFile = { filepath: string, dirname: string, - ignore: Array, + ignore: Array, }; export type RelativeConfig = { diff --git a/packages/babel-core/src/config/pattern-to-regex.js b/packages/babel-core/src/config/pattern-to-regex.js new file mode 100644 index 0000000000..d796617f89 --- /dev/null +++ b/packages/babel-core/src/config/pattern-to-regex.js @@ -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(""), + ); +} diff --git a/packages/babel-core/src/config/validation/option-assertions.js b/packages/babel-core/src/config/validation/option-assertions.js index a637651700..95fca208b3 100644 --- a/packages/babel-core/src/config/validation/option-assertions.js +++ b/packages/babel-core/src/config/validation/option-assertions.js @@ -192,14 +192,14 @@ export function assertBabelrcSearch( if (Array.isArray(value)) { value.forEach((item, i) => { - if (typeof item !== "string") { - throw new Error(`.${key}[${i}] must be a string.`); + if (!checkValidTest(value)) { + throw new Error(`.${key}[${i}] must be a string/Function/RegExp.`); } }); - } else if (typeof value !== "string") { + } else if (!checkValidTest(value)) { throw new Error( - `.${key} must be a undefined, a boolean, a string, ` + - `or an array of strings, got ${JSON.stringify(value)}`, + `.${key} must be a undefined, a boolean, a string/Function/RegExp ` + + `or an array of those, got ${JSON.stringify(value)}`, ); } return (value: any); diff --git a/packages/babel-core/src/config/validation/options.js b/packages/babel-core/src/config/validation/options.js index 2556948578..5b4585be02 100644 --- a/packages/babel-core/src/config/validation/options.js +++ b/packages/babel-core/src/config/validation/options.js @@ -242,7 +242,7 @@ export type OverridesList = Array; export type ConfigApplicableTest = IgnoreItem | Array; export type ConfigFileSearch = string | boolean; -export type BabelrcSearch = boolean | string | Array; +export type BabelrcSearch = boolean | IgnoreItem | IgnoreList; export type SourceMapsOption = boolean | "inline" | "both"; export type SourceTypeOption = "module" | "script" | "unambiguous"; export type CompactOption = boolean | "auto"; diff --git a/packages/babel-core/test/api.js b/packages/babel-core/test/api.js index 6ea97ebdd1..a3b4568a7a 100644 --- a/packages/babel-core/test/api.js +++ b/packages/babel-core/test/api.js @@ -5,11 +5,11 @@ import Plugin from "../lib/config/plugin"; import generator from "@babel/generator"; function assertIgnored(result) { - expect(result).toBeFalsy(); + expect(result).toBeNull(); } function assertNotIgnored(result) { - expect(result.ignored).toBeFalsy(); + expect(result).not.toBeNull(); } function transform(code, opts) { @@ -36,13 +36,11 @@ function transformFileSync(filename, opts) { }); } -// shim function transformAsync(code, opts) { - return { - then: function(resolve) { - resolve(transform(code, opts)); - }, - }; + return babel.transformAsync(code, { + cwd: __dirname, + ...opts, + }); } describe("parser and generator options", function() {