diff --git a/packages/babel-core/src/config/config-chain.js b/packages/babel-core/src/config/config-chain.js index 05faa1cf97..e185f60df7 100644 --- a/packages/babel-core/src/config/config-chain.js +++ b/packages/babel-core/src/config/config-chain.js @@ -130,12 +130,8 @@ export function buildRootChain( ); if (!programmaticChain) return null; - const { - root: rootDir = ".", - babelrc = true, - babelrcRoots, - configFile: configFileName = true, - } = opts; + const { root: rootDir = ".", configFile: configFileName = true } = opts; + let { babelrc, babelrcRoots } = opts; const absoluteRoot = path.resolve(context.cwd, rootDir); @@ -148,9 +144,19 @@ export function buildRootChain( const configFileChain = emptyChain(); if (configFile) { - const result = loadFileChain(configFile, context); + const validatedFile = validateConfigFile(configFile); + const result = loadFileChain(validatedFile, context); if (!result) return null; + // Allow config files to toggle `.babelrc` resolution on and off and + // specify where the roots are. + if (babelrc === undefined) { + babelrc = validatedFile.options.babelrc; + } + if (babelrcRoots === undefined) { + babelrcRoots = validatedFile.options.babelrcRoots; + } + mergeChain(configFileChain, result); } @@ -163,7 +169,7 @@ export function buildRootChain( const fileChain = emptyChain(); // resolve all .babelrc files if ( - babelrc && + (babelrc === true || babelrc === undefined) && pkgData && babelrcLoadEnabled(context, pkgData, babelrcRoots, absoluteRoot) ) { @@ -180,7 +186,7 @@ export function buildRootChain( } if (babelrcFile) { - const result = loadFileChain(babelrcFile, context); + const result = loadFileChain(validateBabelrcFile(babelrcFile), context); if (!result) return null; mergeChain(fileChain, result); @@ -231,11 +237,30 @@ function babelrcLoadEnabled( return micromatch(pkgData.directories, babelrcPatterns).length > 0; } +const validateConfigFile = makeWeakCache((file: ConfigFile): ValidatedFile => ({ + filepath: file.filepath, + dirname: file.dirname, + options: validate("configfile", file.options), +})); + +const validateBabelrcFile = makeWeakCache( + (file: ConfigFile): ValidatedFile => ({ + filepath: file.filepath, + dirname: file.dirname, + options: validate("babelrcfile", file.options), + }), +); + +const validateExtendFile = makeWeakCache((file: ConfigFile): ValidatedFile => ({ + filepath: file.filepath, + dirname: file.dirname, + options: validate("extendsfile", file.options), +})); + /** * Build a config chain for just the programmatic options passed into Babel. */ const loadProgrammaticChain = makeChainWalker({ - init: arg => arg, root: input => buildRootDescriptors(input, "base", createCachedDescriptors), env: (input, envName) => buildEnvDescriptors(input, "base", createCachedDescriptors, envName), @@ -255,18 +280,12 @@ const loadProgrammaticChain = makeChainWalker({ * Build a config chain for a given file. */ const loadFileChain = makeChainWalker({ - init: input => validateFile(input), root: file => loadFileDescriptors(file), env: (file, envName) => loadFileEnvDescriptors(file)(envName), overrides: (file, index) => loadFileOverridesDescriptors(file)(index), overridesEnv: (file, index, envName) => loadFileOverridesEnvDescriptors(file)(index)(envName), }); -const validateFile = makeWeakCache((file: ConfigFile): ValidatedFile => ({ - filepath: file.filepath, - dirname: file.dirname, - options: validate("file", file.options), -})); const loadFileDescriptors = makeWeakCache((file: ValidatedFile) => buildRootDescriptors(file, file.filepath, createUncachedDescriptors), ); @@ -350,25 +369,18 @@ function buildOverrideEnvDescriptors( : null; } -function makeChainWalker< - ArgT, - InnerT: { options: ValidatedOptions, dirname: string }, ->({ - init, +function makeChainWalker({ root, env, overrides, overridesEnv, }: { - init: ArgT => InnerT, - root: InnerT => OptionsAndDescriptors, - env: (InnerT, string) => OptionsAndDescriptors | null, - overrides: (InnerT, number) => OptionsAndDescriptors, - overridesEnv: (InnerT, number, string) => OptionsAndDescriptors | null, + root: ArgT => OptionsAndDescriptors, + env: (ArgT, string) => OptionsAndDescriptors | null, + overrides: (ArgT, number) => OptionsAndDescriptors, + overridesEnv: (ArgT, number, string) => OptionsAndDescriptors | null, }): (ArgT, ConfigContext, Set | void) => ConfigChain | null { - return (arg, context, files = new Set()) => { - const input = init(arg); - + return (input, context, files = new Set()) => { const { dirname } = input; const flattenedConfigs = []; @@ -442,7 +454,7 @@ function mergeExtendsChain( } files.add(file); - const fileChain = loadFileChain(file, context, files); + const fileChain = loadFileChain(validateExtendFile(file), context, files); files.delete(file); if (!fileChain) return false; diff --git a/packages/babel-core/src/config/partial.js b/packages/babel-core/src/config/partial.js index 2a491f5e69..c38ea87c08 100644 --- a/packages/babel-core/src/config/partial.js +++ b/packages/babel-core/src/config/partial.js @@ -49,6 +49,7 @@ export default function loadPrivatePartialConfig( // passed back to Babel a second time, it will be in the right structure // to not change behavior. options.babelrc = false; + options.configFile = false; options.envName = envName; options.cwd = absoluteCwd; options.passPerPreset = false; diff --git a/packages/babel-core/src/config/validation/options.js b/packages/babel-core/src/config/validation/options.js index 4b6024cf03..547211d83e 100644 --- a/packages/babel-core/src/config/validation/options.js +++ b/packages/babel-core/src/config/validation/options.js @@ -36,12 +36,6 @@ const ROOT_VALIDATORS: ValidatorSet = { filenameRelative: (assertString: Validator< $PropertyType, >), - babelrc: (assertBoolean: Validator< - $PropertyType, - >), - babelrcRoots: (assertBabelrcSearch: Validator< - $PropertyType, - >), code: (assertBoolean: Validator<$PropertyType>), ast: (assertBoolean: Validator<$PropertyType>), @@ -50,6 +44,15 @@ const ROOT_VALIDATORS: ValidatorSet = { >), }; +const BABELRC_VALIDATORS: ValidatorSet = { + babelrc: (assertBoolean: Validator< + $PropertyType, + >), + babelrcRoots: (assertBabelrcSearch: Validator< + $PropertyType, + >), +}; + const NONPRESET_VALIDATORS: ValidatorSet = { extends: (assertString: Validator< $PropertyType, @@ -243,7 +246,14 @@ export type SourceTypeOption = "module" | "script" | "unambiguous"; export type CompactOption = boolean | "auto"; export type RootInputSourceMapOption = {} | boolean; -export type OptionsType = "arguments" | "file" | "env" | "preset" | "override"; +export type OptionsType = + | "arguments" + | "env" + | "preset" + | "override" + | "configfile" + | "babelrcfile" + | "extendsfile"; export function validate(type: OptionsType, opts: {}): ValidatedOptions { assertNoDuplicateSourcemap(opts); @@ -255,6 +265,22 @@ export function validate(type: OptionsType, opts: {}): ValidatedOptions { if (type !== "arguments" && ROOT_VALIDATORS[key]) { throw new Error(`.${key} is only allowed in root programmatic options`); } + if ( + type !== "arguments" && + type !== "configfile" && + BABELRC_VALIDATORS[key] + ) { + if (type === "babelrcfile" || type === "extendsfile") { + throw new Error( + `.${key} is not allowed in .babelrc or "extend"ed files, only in root programmatic options, ` + + `or babel.config.js/config file options`, + ); + } + + throw new Error( + `.${key} is only allowed in root programmatic options, or babel.config.js/config file options`, + ); + } if (type === "env" && key === "env") { throw new Error(`.${key} is not allowed inside another env block`); } @@ -268,6 +294,7 @@ export function validate(type: OptionsType, opts: {}): ValidatedOptions { const validator = COMMON_VALIDATORS[key] || NONPRESET_VALIDATORS[key] || + BABELRC_VALIDATORS[key] || ROOT_VALIDATORS[key]; if (validator) validator(key, opts[key]); diff --git a/packages/babel-core/test/config-chain.js b/packages/babel-core/test/config-chain.js index 2e73b1329d..63f22da52b 100644 --- a/packages/babel-core/test/config-chain.js +++ b/packages/babel-core/test/config-chain.js @@ -904,6 +904,7 @@ describe("buildConfigChain", function() { describe("config files", () => { const getDefaults = () => ({ babelrc: false, + configFile: false, cwd: process.cwd(), envName: "development", passPerPreset: false,