Allow users to choose how the configuration root is selected. (#8660)

This commit is contained in:
Logan Smyth 2018-09-15 13:03:46 -07:00 committed by GitHub
parent aac8118b7f
commit bd0c62dc0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 96 additions and 4 deletions

View File

@ -28,6 +28,11 @@ commander.option(
"The name of the 'env' to use when loading configs and plugins. " + "The name of the 'env' to use when loading configs and plugins. " +
"Defaults to the value of BABEL_ENV, or else NODE_ENV, or else 'development'.", "Defaults to the value of BABEL_ENV, or else NODE_ENV, or else 'development'.",
); );
commander.option(
"--root-mode [mode]",
"The project-root resolution mode. " +
"One of 'root' (the default), 'upward', or 'upward-optional'.",
);
// Basic file input configuration. // Basic file input configuration.
commander.option("--source-type [script|module]", ""); commander.option("--source-type [script|module]", "");
@ -220,6 +225,7 @@ export default function parseArgv(args: Array<string>) {
babelOptions: { babelOptions: {
presets: opts.presets, presets: opts.presets,
plugins: opts.plugins, plugins: opts.plugins,
rootMode: opts.rootMode,
configFile: opts.configFile, configFile: opts.configFile,
envName: opts.envName, envName: opts.envName,
sourceType: opts.sourceType, sourceType: opts.sourceType,

View File

@ -24,6 +24,21 @@ const BABELRC_FILENAME = ".babelrc";
const BABELRC_JS_FILENAME = ".babelrc.js"; const BABELRC_JS_FILENAME = ".babelrc.js";
const BABELIGNORE_FILENAME = ".babelignore"; const BABELIGNORE_FILENAME = ".babelignore";
export function findConfigUpwards(rootDir: string): string | null {
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;
}
return null;
}
export function findRelativeConfig( export function findRelativeConfig(
packageData: FilePackageData, packageData: FilePackageData,
envName: string, envName: string,

View File

@ -11,6 +11,12 @@ import type { CallerMetadata } from "../validation/options";
export type { ConfigFile, IgnoreFile, RelativeConfig, FilePackageData }; export type { ConfigFile, IgnoreFile, RelativeConfig, FilePackageData };
export function findConfigUpwards(
rootDir: string, // eslint-disable-line no-unused-vars
): string | null {
return null;
}
export function findPackageData(filepath: string): FilePackageData { export function findPackageData(filepath: string): FilePackageData {
return { return {
filepath, filepath,

View File

@ -10,6 +10,7 @@ import typeof * as indexType from "./index";
export { findPackageData } from "./package"; export { findPackageData } from "./package";
export { export {
findConfigUpwards,
findRelativeConfig, findRelativeConfig,
findRootConfig, findRootConfig,
loadConfig, loadConfig,

View File

@ -6,9 +6,43 @@ import { mergeOptions } from "./util";
import { createItemFromDescriptor } from "./item"; import { createItemFromDescriptor } from "./item";
import { buildRootChain, type ConfigContext } from "./config-chain"; import { buildRootChain, type ConfigContext } from "./config-chain";
import { getEnv } from "./helpers/environment"; import { getEnv } from "./helpers/environment";
import { validate, type ValidatedOptions } from "./validation/options"; import {
validate,
type ValidatedOptions,
type RootMode,
} from "./validation/options";
import type { ConfigFile, IgnoreFile } from "./files"; import { findConfigUpwards, type ConfigFile, type IgnoreFile } from "./files";
function resolveRootMode(rootDir: string, rootMode: RootMode): string {
switch (rootMode) {
case "root":
return rootDir;
case "upward-optional": {
const upwardRootDir = findConfigUpwards(rootDir);
return upwardRootDir === null ? rootDir : upwardRootDir;
}
case "upward": {
const upwardRootDir = findConfigUpwards(rootDir);
if (upwardRootDir !== null) return upwardRootDir;
throw Object.assign(
(new Error(
`Babel was run with rootMode:"upward" but a root could not ` +
`be found when searching upward from "${rootDir}"`,
): any),
{
code: "BABEL_ROOT_NOT_FOUND",
dirname: rootDir,
},
);
}
default:
throw new Error(`Assertion failure - unknown rootMode value`);
}
}
export default function loadPrivatePartialConfig( export default function loadPrivatePartialConfig(
inputOpts: mixed, inputOpts: mixed,
@ -28,9 +62,18 @@ export default function loadPrivatePartialConfig(
const args = inputOpts ? validate("arguments", inputOpts) : {}; const args = inputOpts ? validate("arguments", inputOpts) : {};
const { envName = getEnv(), cwd = ".", root: rootDir = ".", caller } = args; const {
envName = getEnv(),
cwd = ".",
root: rootDir = ".",
rootMode = "root",
caller,
} = args;
const absoluteCwd = path.resolve(cwd); const absoluteCwd = path.resolve(cwd);
const absoluteRootDir = path.resolve(absoluteCwd, rootDir); const absoluteRootDir = resolveRootMode(
path.resolve(absoluteCwd, rootDir),
rootMode,
);
const context: ConfigContext = { const context: ConfigContext = {
filename: filename:

View File

@ -15,6 +15,7 @@ import type {
RootInputSourceMapOption, RootInputSourceMapOption,
NestingPath, NestingPath,
CallerMetadata, CallerMetadata,
RootMode,
} from "./options"; } from "./options";
export type ValidatorSet = { export type ValidatorSet = {
@ -60,6 +61,20 @@ type AccessPath = $ReadOnly<{
}>; }>;
type GeneralPath = OptionPath | AccessPath; type GeneralPath = OptionPath | AccessPath;
export function assertRootMode(loc: OptionPath, value: mixed): RootMode | void {
if (
value !== undefined &&
value !== "root" &&
value !== "upward" &&
value !== "upward-optional"
) {
throw new Error(
`${msg(loc)} must be a "root", "upward", "upward-optional" or undefined`,
);
}
return value;
}
export function assertSourceMaps( export function assertSourceMaps(
loc: OptionPath, loc: OptionPath,
value: mixed, value: mixed,

View File

@ -19,6 +19,7 @@ import {
assertConfigFileSearch, assertConfigFileSearch,
assertBabelrcSearch, assertBabelrcSearch,
assertFunction, assertFunction,
assertRootMode,
assertSourceMaps, assertSourceMaps,
assertCompact, assertCompact,
assertSourceType, assertSourceType,
@ -30,6 +31,9 @@ import {
const ROOT_VALIDATORS: ValidatorSet = { const ROOT_VALIDATORS: ValidatorSet = {
cwd: (assertString: Validator<$PropertyType<ValidatedOptions, "cwd">>), cwd: (assertString: Validator<$PropertyType<ValidatedOptions, "cwd">>),
root: (assertString: Validator<$PropertyType<ValidatedOptions, "root">>), root: (assertString: Validator<$PropertyType<ValidatedOptions, "root">>),
rootMode: (assertRootMode: Validator<
$PropertyType<ValidatedOptions, "rootMode">,
>),
configFile: (assertConfigFileSearch: Validator< configFile: (assertConfigFileSearch: Validator<
$PropertyType<ValidatedOptions, "configFile">, $PropertyType<ValidatedOptions, "configFile">,
>), >),
@ -176,6 +180,7 @@ export type ValidatedOptions = {
babelrcRoots?: BabelrcSearch, babelrcRoots?: BabelrcSearch,
configFile?: ConfigFileSearch, configFile?: ConfigFileSearch,
root?: string, root?: string,
rootMode?: RootMode,
code?: boolean, code?: boolean,
ast?: boolean, ast?: boolean,
inputSourceMap?: RootInputSourceMapOption, inputSourceMap?: RootInputSourceMapOption,
@ -260,6 +265,7 @@ export type SourceMapsOption = boolean | "inline" | "both";
export type SourceTypeOption = "module" | "script" | "unambiguous"; export type SourceTypeOption = "module" | "script" | "unambiguous";
export type CompactOption = boolean | "auto"; export type CompactOption = boolean | "auto";
export type RootInputSourceMapOption = {} | boolean; export type RootInputSourceMapOption = {} | boolean;
export type RootMode = "root" | "upward" | "upward-optional";
export type OptionsSource = export type OptionsSource =
| "arguments" | "arguments"