diff --git a/packages/babel-core/src/config/files/configuration.js b/packages/babel-core/src/config/files/configuration.js index b31519b43f97..91a8f689094e 100644 --- a/packages/babel-core/src/config/files/configuration.js +++ b/packages/babel-core/src/config/files/configuration.js @@ -24,6 +24,30 @@ const BABELRC_FILENAME = ".babelrc"; const BABELRC_JS_FILENAME = ".babelrc.js"; const BABELIGNORE_FILENAME = ".babelignore"; +export function findClosestConfig(rootDir: string): string { + let dirname = rootDir; + while (true) { + if (fs.existsSync(path.join(dirname, BABEL_CONFIG_JS_FILENAME))) { + return dirname; + } + + const nextDir = path.dirname(dirname); + if (dirname === nextDir) break; + dirname = nextDir; + } + + throw Object.assign( + (new Error( + `Babel was run with autoRoot:true but a root babel.config.js could not ` + + `be found when searching from "${rootDir}"`, + ): any), + { + code: "BABEL_AUTOROOT_NOT_FOUND", + dirname: rootDir, + }, + ); +} + export function findRelativeConfig( packageData: FilePackageData, envName: string, diff --git a/packages/babel-core/src/config/files/index-browser.js b/packages/babel-core/src/config/files/index-browser.js index 3305c346e43a..d310bf152a3f 100644 --- a/packages/babel-core/src/config/files/index-browser.js +++ b/packages/babel-core/src/config/files/index-browser.js @@ -11,6 +11,14 @@ import type { CallerMetadata } from "../validation/options"; export type { ConfigFile, IgnoreFile, RelativeConfig, FilePackageData }; +export function findClosestConfig( + rootDir: string, // eslint-disable-line no-unused-vars +): string { + throw new Error( + `Cannot search for filesystem config root when running in a browser`, + ); +} + export function findPackageData(filepath: string): FilePackageData { return { filepath, diff --git a/packages/babel-core/src/config/files/index.js b/packages/babel-core/src/config/files/index.js index 9f8d9797bbfb..8caabf0d88e9 100644 --- a/packages/babel-core/src/config/files/index.js +++ b/packages/babel-core/src/config/files/index.js @@ -10,6 +10,7 @@ import typeof * as indexType from "./index"; export { findPackageData } from "./package"; export { + findClosestConfig, findRelativeConfig, findRootConfig, loadConfig, diff --git a/packages/babel-core/src/config/partial.js b/packages/babel-core/src/config/partial.js index 31b2aa0cf432..d8933f59fa93 100644 --- a/packages/babel-core/src/config/partial.js +++ b/packages/babel-core/src/config/partial.js @@ -8,7 +8,7 @@ import { buildRootChain, type ConfigContext } from "./config-chain"; import { getEnv } from "./helpers/environment"; import { validate, type ValidatedOptions } from "./validation/options"; -import type { ConfigFile, IgnoreFile } from "./files"; +import { findClosestConfig, type ConfigFile, type IgnoreFile } from "./files"; export default function loadPrivatePartialConfig( inputOpts: mixed, @@ -28,9 +28,17 @@ export default function loadPrivatePartialConfig( const args = inputOpts ? validate("arguments", inputOpts) : {}; - const { envName = getEnv(), cwd = ".", root: rootDir = ".", caller } = args; + const { + envName = getEnv(), + cwd = ".", + root: rootDir = ".", + caller, + autoRoot = false, + } = args; const absoluteCwd = path.resolve(cwd); - const absoluteRootDir = path.resolve(absoluteCwd, rootDir); + let absoluteRootDir = path.resolve(absoluteCwd, rootDir); + + if (autoRoot) absoluteRootDir = findClosestConfig(absoluteRootDir); const context: ConfigContext = { filename: diff --git a/packages/babel-core/src/config/validation/options.js b/packages/babel-core/src/config/validation/options.js index ee0e04638ea3..8868e8ebe2d9 100644 --- a/packages/babel-core/src/config/validation/options.js +++ b/packages/babel-core/src/config/validation/options.js @@ -33,6 +33,7 @@ const ROOT_VALIDATORS: ValidatorSet = { configFile: (assertConfigFileSearch: Validator< $PropertyType, >), + autoRoot: (assertBoolean: Validator<$PropertyType>), caller: (assertCallerMetadata: Validator< $PropertyType, @@ -176,6 +177,7 @@ export type ValidatedOptions = { babelrcRoots?: BabelrcSearch, configFile?: ConfigFileSearch, root?: string, + autoRoot?: boolean, code?: boolean, ast?: boolean, inputSourceMap?: RootInputSourceMapOption,