Merge pull request #5586 from loganfsmyth/config-dependency-cycles
Handle cycles of plugins compiling themselves and .babelrc.js files loading themselves
This commit is contained in:
commit
d79a7920a1
@ -3,6 +3,9 @@
|
|||||||
import { getEnv } from "./helpers/environment";
|
import { getEnv } from "./helpers/environment";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import micromatch from "micromatch";
|
import micromatch from "micromatch";
|
||||||
|
import buildDebug from "debug";
|
||||||
|
|
||||||
|
const debug = buildDebug("babel:config:config-chain");
|
||||||
|
|
||||||
import { findConfigs, loadConfig } from "./loading/files";
|
import { findConfigs, loadConfig } from "./loading/files";
|
||||||
|
|
||||||
@ -67,7 +70,15 @@ class ConfigChainBuilder {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.matchesPatterns(ignore, dirname)) return true;
|
if (this.matchesPatterns(ignore, dirname)) {
|
||||||
|
debug(
|
||||||
|
"Ignored %o because it matched one of %O from %o",
|
||||||
|
this.filename,
|
||||||
|
ignore,
|
||||||
|
dirname,
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (only) {
|
if (only) {
|
||||||
@ -77,7 +88,15 @@ class ConfigChainBuilder {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.matchesPatterns(only, dirname)) return true;
|
if (!this.matchesPatterns(only, dirname)) {
|
||||||
|
debug(
|
||||||
|
"Ignored %o because it failed to match one of %O from %o",
|
||||||
|
this.filename,
|
||||||
|
only,
|
||||||
|
dirname,
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
|
import buildDebug from "debug";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import json5 from "json5";
|
import json5 from "json5";
|
||||||
@ -7,6 +8,8 @@ import resolve from "resolve";
|
|||||||
import { getEnv } from "../../helpers/environment";
|
import { getEnv } from "../../helpers/environment";
|
||||||
import { makeStrongCache } from "../../caching";
|
import { makeStrongCache } from "../../caching";
|
||||||
|
|
||||||
|
const debug = buildDebug("babel:config:loading:files:configuration");
|
||||||
|
|
||||||
type ConfigFile = {
|
type ConfigFile = {
|
||||||
filepath: string,
|
filepath: string,
|
||||||
dirname: string,
|
dirname: string,
|
||||||
@ -31,6 +34,7 @@ export function findConfigs(dirname: string): Array<ConfigFile> {
|
|||||||
const ignore = readIgnoreConfig(ignoreLoc);
|
const ignore = readIgnoreConfig(ignoreLoc);
|
||||||
|
|
||||||
if (ignore) {
|
if (ignore) {
|
||||||
|
debug("Found ignore %o from %o.", ignore.filepath, dirname);
|
||||||
confs.push(ignore);
|
confs.push(ignore);
|
||||||
foundIgnore = true;
|
foundIgnore = true;
|
||||||
}
|
}
|
||||||
@ -57,6 +61,7 @@ export function findConfigs(dirname: string): Array<ConfigFile> {
|
|||||||
}, null);
|
}, null);
|
||||||
|
|
||||||
if (conf) {
|
if (conf) {
|
||||||
|
debug("Found configuration %o from %o.", conf.filepath, dirname);
|
||||||
confs.push(conf);
|
confs.push(conf);
|
||||||
foundConfig = true;
|
foundConfig = true;
|
||||||
}
|
}
|
||||||
@ -80,6 +85,7 @@ export function loadConfig(name: string, dirname: string): ConfigFile {
|
|||||||
throw new Error(`Config file ${filepath} contains no configuration data`);
|
throw new Error(`Config file ${filepath} contains no configuration data`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug("Loaded config %o from $o.", name, dirname);
|
||||||
return conf;
|
return conf;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,14 +99,31 @@ function readConfig(filepath) {
|
|||||||
: readConfigFile(filepath);
|
: readConfigFile(filepath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const LOADING_CONFIGS = new Set();
|
||||||
const readConfigJS = makeStrongCache((filepath, cache) => {
|
const readConfigJS = makeStrongCache((filepath, cache) => {
|
||||||
if (!fs.existsSync(filepath)) {
|
if (!fs.existsSync(filepath)) {
|
||||||
cache.forever();
|
cache.forever();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The `require()` call below can make this code reentrant if a require hook like babel-register has been
|
||||||
|
// loaded into the system. That would cause Babel to attempt to compile the `.babelrc.js` file as it loads
|
||||||
|
// below. To cover this case, we auto-ignore re-entrant config processing.
|
||||||
|
if (LOADING_CONFIGS.has(filepath)) {
|
||||||
|
cache.never();
|
||||||
|
|
||||||
|
debug("Auto-ignoring usage of config %o.", filepath);
|
||||||
|
return {
|
||||||
|
filepath,
|
||||||
|
dirname: path.dirname(filepath),
|
||||||
|
options: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let options;
|
let options;
|
||||||
try {
|
try {
|
||||||
|
LOADING_CONFIGS.add(filepath);
|
||||||
|
|
||||||
// $FlowIssue
|
// $FlowIssue
|
||||||
const configModule = (require(filepath): mixed);
|
const configModule = (require(filepath): mixed);
|
||||||
options =
|
options =
|
||||||
@ -110,6 +133,8 @@ const readConfigJS = makeStrongCache((filepath, cache) => {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
err.message = `${filepath}: Error while loading config - ${err.message}`;
|
err.message = `${filepath}: Error while loading config - ${err.message}`;
|
||||||
throw err;
|
throw err;
|
||||||
|
} finally {
|
||||||
|
LOADING_CONFIGS.delete(filepath);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof options === "function") {
|
if (typeof options === "function") {
|
||||||
|
|||||||
@ -4,9 +4,12 @@
|
|||||||
* This file handles all logic for converting string-based configuration references into loaded objects.
|
* This file handles all logic for converting string-based configuration references into loaded objects.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import buildDebug from "debug";
|
||||||
import resolve from "resolve";
|
import resolve from "resolve";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
|
const debug = buildDebug("babel:config:loading:files:plugins");
|
||||||
|
|
||||||
const EXACT_RE = /^module:/;
|
const EXACT_RE = /^module:/;
|
||||||
const BABEL_PLUGIN_PREFIX_RE = /^(?!@|module:|[^/\/]+[/\/]|babel-plugin-)/;
|
const BABEL_PLUGIN_PREFIX_RE = /^(?!@|module:|[^/\/]+[/\/]|babel-plugin-)/;
|
||||||
const BABEL_PRESET_PREFIX_RE = /^(?!@|module:|[^/\/]+[/\/]|babel-preset-)/;
|
const BABEL_PRESET_PREFIX_RE = /^(?!@|module:|[^/\/]+[/\/]|babel-preset-)/;
|
||||||
@ -32,10 +35,10 @@ export function loadPlugin(
|
|||||||
throw new Error(`Plugin ${name} not found relative to ${dirname}`);
|
throw new Error(`Plugin ${name} not found relative to ${dirname}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
const value = requireModule("plugin", filepath);
|
||||||
filepath,
|
debug("Loaded plugin %o from %o.", name, dirname);
|
||||||
value: requireModule(filepath),
|
|
||||||
};
|
return { filepath, value };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadPreset(
|
export function loadPreset(
|
||||||
@ -47,10 +50,11 @@ export function loadPreset(
|
|||||||
throw new Error(`Preset ${name} not found relative to ${dirname}`);
|
throw new Error(`Preset ${name} not found relative to ${dirname}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
const value = requireModule("preset", filepath);
|
||||||
filepath,
|
|
||||||
value: requireModule(filepath),
|
debug("Loaded preset %o from %o.", name, dirname);
|
||||||
};
|
|
||||||
|
return { filepath, value };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadParser(
|
export function loadParser(
|
||||||
@ -59,7 +63,7 @@ export function loadParser(
|
|||||||
): { filepath: string, value: Function } {
|
): { filepath: string, value: Function } {
|
||||||
const filepath = resolve.sync(name, { basedir: dirname });
|
const filepath = resolve.sync(name, { basedir: dirname });
|
||||||
|
|
||||||
const mod = requireModule(filepath);
|
const mod = requireModule("parser", filepath);
|
||||||
|
|
||||||
if (!mod) {
|
if (!mod) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@ -71,10 +75,13 @@ export function loadParser(
|
|||||||
`Parser ${name} relative to ${dirname} does not export a .parse function`,
|
`Parser ${name} relative to ${dirname} does not export a .parse function`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
const value = mod.parse;
|
||||||
|
|
||||||
|
debug("Loaded parser %o from %o.", name, dirname);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
filepath,
|
filepath,
|
||||||
value: mod.parse,
|
value,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +91,7 @@ export function loadGenerator(
|
|||||||
): { filepath: string, value: Function } {
|
): { filepath: string, value: Function } {
|
||||||
const filepath = resolve.sync(name, { basedir: dirname });
|
const filepath = resolve.sync(name, { basedir: dirname });
|
||||||
|
|
||||||
const mod = requireModule(filepath);
|
const mod = requireModule("generator", filepath);
|
||||||
|
|
||||||
if (!mod) {
|
if (!mod) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@ -96,10 +103,13 @@ export function loadGenerator(
|
|||||||
`Generator ${name} relative to ${dirname} does not export a .print function`,
|
`Generator ${name} relative to ${dirname} does not export a .print function`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
const value = mod.print;
|
||||||
|
|
||||||
|
debug("Loaded generator %o from %o.", name, dirname);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
filepath,
|
filepath,
|
||||||
value: mod.print,
|
value,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,7 +195,20 @@ function resolveStandardizedName(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function requireModule(name: string): mixed {
|
const LOADING_MODULES = new Set();
|
||||||
|
function requireModule(type: string, name: string): mixed {
|
||||||
|
if (LOADING_MODULES.has(name)) {
|
||||||
|
throw new Error(
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
`Reentrant ${type} detected trying to load "${name}". This module is not ignored and is trying to load itself while compiling itself, leading to a dependency cycle. We recommend adding it to your "ignore" list in your babelrc, or to a .babelignore.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
LOADING_MODULES.add(name);
|
||||||
// $FlowIssue
|
// $FlowIssue
|
||||||
return require(name);
|
return require(name);
|
||||||
|
} finally {
|
||||||
|
LOADING_MODULES.delete(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user