diff --git a/packages/babel-cli/src/babel/index.js b/packages/babel-cli/src/babel/index.js index 48682c11fa..696c42fe18 100755 --- a/packages/babel-cli/src/babel/index.js +++ b/packages/babel-cli/src/babel/index.js @@ -238,10 +238,6 @@ if (errors.length) { // const opts = commander.opts(); -//the configFile CLI option maps to the extends option in the node API -if (opts.configFile) { - opts.extends = opts.configFile; -} // Delete options that are specific to @babel/cli and shouldn't be passed to @babel/core. delete opts.version; @@ -253,7 +249,6 @@ delete opts.outDir; delete opts.copyFiles; delete opts.includeDotfiles; delete opts.verbose; -delete opts.configFile; delete opts.deleteDirOnStart; delete opts.keepFileExtension; delete opts.relative; diff --git a/packages/babel-core/src/config/config-chain.js b/packages/babel-core/src/config/config-chain.js index 2ca2a5c3bf..ef607ccdd0 100644 --- a/packages/babel-core/src/config/config-chain.js +++ b/packages/babel-core/src/config/config-chain.js @@ -15,6 +15,7 @@ const debug = buildDebug("babel:config:config-chain"); import { findPackageData, findRelativeConfig, + findRootConfig, loadConfig, type ConfigFile, type IgnoreFile, @@ -107,6 +108,7 @@ const loadPresetOverridesEnvDescriptors = makeWeakCache( export type RootConfigChain = ConfigChain & { babelrc: ConfigFile | void, + config: ConfigFile | void, ignore: IgnoreFile | void, }; @@ -126,6 +128,26 @@ export function buildRootChain( ); if (!programmaticChain) return null; + const { root: rootDir = ".", configFile: configFileName } = opts; + + let configFile; + if (typeof configFileName === "string") { + configFile = loadConfig(configFileName, context.cwd, context.envName); + } else if (configFileName === undefined || configFileName === true) { + configFile = findRootConfig( + path.resolve(context.cwd, rootDir), + context.envName, + ); + } + + const configFileChain = emptyChain(); + if (configFile) { + const result = loadFileChain(configFile, context); + if (!result) return null; + + mergeChain(configFileChain, result); + } + const pkgData = typeof context.filename === "string" ? findPackageData(context.filename) @@ -155,7 +177,7 @@ export function buildRootChain( // Insert file chain in front so programmatic options have priority // over configuration file chain items. const chain = mergeChain( - mergeChain(emptyChain(), fileChain), + mergeChain(mergeChain(emptyChain(), configFileChain), fileChain), programmaticChain, ); @@ -165,6 +187,7 @@ export function buildRootChain( options: chain.options.map(o => normalizeOptions(o)), ignore: ignore || undefined, babelrc: babelrc || undefined, + config: configFile || undefined, }; } diff --git a/packages/babel-core/src/config/files/configuration.js b/packages/babel-core/src/config/files/configuration.js index ccc300ae48..e7022ce2bc 100644 --- a/packages/babel-core/src/config/files/configuration.js +++ b/packages/babel-core/src/config/files/configuration.js @@ -16,6 +16,8 @@ import type { FilePackageData, RelativeConfig, ConfigFile } from "./types"; const debug = buildDebug("babel:config:loading:files:configuration"); +const BABEL_CONFIG_JS_FILENAME = "babel.config.js"; + const BABELRC_FILENAME = ".babelrc"; const BABELRC_JS_FILENAME = ".babelrc.js"; const BABELIGNORE_FILENAME = ".babelignore"; @@ -85,6 +87,19 @@ export function findRelativeConfig( return { config, ignore }; } +export function findRootConfig( + dirname: string, + envName: string, +): ConfigFile | null { + const filepath = path.resolve(dirname, BABEL_CONFIG_JS_FILENAME); + + const conf = readConfig(filepath, envName); + if (conf) { + debug("Found root config %o in $o.", BABEL_CONFIG_JS_FILENAME, dirname); + } + return conf; +} + export function loadConfig( name: string, dirname: string, diff --git a/packages/babel-core/src/config/files/index-browser.js b/packages/babel-core/src/config/files/index-browser.js index 9805b9676c..baa0fd43d8 100644 --- a/packages/babel-core/src/config/files/index-browser.js +++ b/packages/babel-core/src/config/files/index-browser.js @@ -25,6 +25,13 @@ export function findRelativeConfig( return { pkg: null, config: null, ignore: null }; } +export function findRootConfig( + dirname: string, + envName: string, // eslint-disable-line no-unused-vars +): ConfigFile | null { + return null; +} + export function loadConfig( name: string, dirname: string, diff --git a/packages/babel-core/src/config/files/index.js b/packages/babel-core/src/config/files/index.js index 06e8ce1fb2..9f8d9797bb 100644 --- a/packages/babel-core/src/config/files/index.js +++ b/packages/babel-core/src/config/files/index.js @@ -9,7 +9,11 @@ import typeof * as indexType from "./index"; export { findPackageData } from "./package"; -export { findRelativeConfig, loadConfig } from "./configuration"; +export { + findRelativeConfig, + findRootConfig, + loadConfig, +} from "./configuration"; export type { ConfigFile, IgnoreFile, diff --git a/packages/babel-core/src/config/partial.js b/packages/babel-core/src/config/partial.js index 83d0d08086..e0fa76b81a 100644 --- a/packages/babel-core/src/config/partial.js +++ b/packages/babel-core/src/config/partial.js @@ -17,6 +17,7 @@ export default function loadPrivatePartialConfig( context: ConfigContext, ignore: IgnoreFile | void, babelrc: ConfigFile | void, + config: ConfigFile | void, } | null { if ( inputOpts != null && @@ -64,6 +65,7 @@ export default function loadPrivatePartialConfig( context, ignore: configChain.ignore, babelrc: configChain.babelrc, + config: configChain.config, }; } @@ -71,7 +73,7 @@ export function loadPartialConfig(inputOpts: mixed): PartialConfig | null { const result = loadPrivatePartialConfig(inputOpts); if (!result) return null; - const { options, babelrc, ignore } = result; + const { options, babelrc, ignore, config } = result; (options.plugins || []).forEach(item => { if (item.value instanceof Plugin) { @@ -86,6 +88,7 @@ export function loadPartialConfig(inputOpts: mixed): PartialConfig | null { options, babelrc ? babelrc.filepath : undefined, ignore ? ignore.filepath : undefined, + config ? config.filepath : undefined, ); } @@ -99,15 +102,18 @@ class PartialConfig { options: ValidatedOptions; babelrc: string | void; babelignore: string | void; + config: string | void; constructor( options: ValidatedOptions, babelrc: string | void, ignore: string | void, + config: string | void, ) { this.options = options; this.babelignore = ignore; this.babelrc = babelrc; + this.config = config; // Freeze since this is a public API and it should be extremely obvious that // reassigning properties on here does nothing. diff --git a/packages/babel-core/src/config/validation/option-assertions.js b/packages/babel-core/src/config/validation/option-assertions.js index 97b860f016..b59454e875 100644 --- a/packages/babel-core/src/config/validation/option-assertions.js +++ b/packages/babel-core/src/config/validation/option-assertions.js @@ -1,6 +1,7 @@ // @flow import type { + ConfigFileSearch, IgnoreList, IgnoreItem, PluginList, @@ -164,6 +165,24 @@ function checkValidTest(value: mixed): boolean { ); } +export function assertConfigFileSearch( + key: string, + value: mixed, +): ConfigFileSearch | void { + if ( + value !== undefined && + typeof value !== "boolean" && + typeof value !== "string" + ) { + throw new Error( + `.${key} must be a undefined, a boolean, a string, ` + + `got ${JSON.stringify(value)}`, + ); + } + + return value; +} + export function assertPluginList(key: string, value: mixed): PluginList | void { const arr = assertArray(key, value); if (arr) { diff --git a/packages/babel-core/src/config/validation/options.js b/packages/babel-core/src/config/validation/options.js index 290d326d5b..51fe8c9c0d 100644 --- a/packages/babel-core/src/config/validation/options.js +++ b/packages/babel-core/src/config/validation/options.js @@ -13,6 +13,7 @@ import { assertIgnoreList, assertPluginList, assertConfigApplicableTest, + assertConfigFileSearch, assertFunction, assertSourceMaps, assertCompact, @@ -23,6 +24,11 @@ import { const ROOT_VALIDATORS: ValidatorSet = { cwd: (assertString: Validator<$PropertyType>), + root: (assertString: Validator<$PropertyType>), + configFile: (assertConfigFileSearch: Validator< + $PropertyType, + >), + filename: (assertString: Validator< $PropertyType, >), @@ -152,6 +158,8 @@ export type ValidatedOptions = { filenameRelative?: string, babelrc?: boolean, code?: boolean, + configFile?: ConfigFileSearch, + root?: string, ast?: boolean, inputSourceMap?: RootInputSourceMapOption, envName?: string, @@ -223,6 +231,7 @@ export type PluginList = $ReadOnlyArray; export type OverridesList = Array; export type ConfigApplicableTest = IgnoreItem | Array; +export type ConfigFileSearch = string | boolean; export type SourceMapsOption = boolean | "inline" | "both"; export type SourceTypeOption = "module" | "script" | "unambiguous"; export type CompactOption = boolean | "auto";