Implement an 'overrides' config array to process in order for overrides.
This commit is contained in:
@@ -62,6 +62,9 @@ export const buildPresetChain: (
|
||||
init: arg => arg,
|
||||
root: preset => loadPresetDescriptors(preset),
|
||||
env: (preset, envName) => loadPresetEnvDescriptors(preset)(envName),
|
||||
overrides: (preset, index) => loadPresetOverridesDescriptors(preset)(index),
|
||||
overridesEnv: (preset, index, envName) =>
|
||||
loadPresetOverridesEnvDescriptors(preset)(index)(envName),
|
||||
});
|
||||
const loadPresetDescriptors = makeWeakCache((preset: PresetInstance) =>
|
||||
buildRootDescriptors(preset, preset.alias, createUncachedDescriptors),
|
||||
@@ -76,6 +79,30 @@ const loadPresetEnvDescriptors = makeWeakCache((preset: PresetInstance) =>
|
||||
),
|
||||
),
|
||||
);
|
||||
const loadPresetOverridesDescriptors = makeWeakCache((preset: PresetInstance) =>
|
||||
makeStrongCache((index: number) =>
|
||||
buildOverrideDescriptors(
|
||||
preset,
|
||||
preset.alias,
|
||||
createUncachedDescriptors,
|
||||
index,
|
||||
),
|
||||
),
|
||||
);
|
||||
const loadPresetOverridesEnvDescriptors = makeWeakCache(
|
||||
(preset: PresetInstance) =>
|
||||
makeStrongCache((index: number) =>
|
||||
makeStrongCache((envName: string) =>
|
||||
buildOverrideEnvDescriptors(
|
||||
preset,
|
||||
preset.alias,
|
||||
createUncachedDescriptors,
|
||||
index,
|
||||
envName,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Build a config chain for Babel's full root configuration.
|
||||
@@ -142,6 +169,16 @@ const loadProgrammaticChain = makeChainWalker({
|
||||
root: input => buildRootDescriptors(input, "base", createCachedDescriptors),
|
||||
env: (input, envName) =>
|
||||
buildEnvDescriptors(input, "base", createCachedDescriptors, envName),
|
||||
overrides: (input, index) =>
|
||||
buildOverrideDescriptors(input, "base", createCachedDescriptors, index),
|
||||
overridesEnv: (input, index, envName) =>
|
||||
buildOverrideEnvDescriptors(
|
||||
input,
|
||||
"base",
|
||||
createCachedDescriptors,
|
||||
index,
|
||||
envName,
|
||||
),
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -151,6 +188,9 @@ 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,
|
||||
@@ -170,6 +210,29 @@ const loadFileEnvDescriptors = makeWeakCache((file: ValidatedFile) =>
|
||||
),
|
||||
),
|
||||
);
|
||||
const loadFileOverridesDescriptors = makeWeakCache((file: ValidatedFile) =>
|
||||
makeStrongCache((index: number) =>
|
||||
buildOverrideDescriptors(
|
||||
file,
|
||||
file.filepath,
|
||||
createUncachedDescriptors,
|
||||
index,
|
||||
),
|
||||
),
|
||||
);
|
||||
const loadFileOverridesEnvDescriptors = makeWeakCache((file: ValidatedFile) =>
|
||||
makeStrongCache((index: number) =>
|
||||
makeStrongCache((envName: string) =>
|
||||
buildOverrideEnvDescriptors(
|
||||
file,
|
||||
file.filepath,
|
||||
createUncachedDescriptors,
|
||||
index,
|
||||
envName,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
function buildRootDescriptors({ dirname, options }, alias, descriptors) {
|
||||
return descriptors(dirname, options, alias);
|
||||
@@ -185,6 +248,38 @@ function buildEnvDescriptors(
|
||||
return opts ? descriptors(dirname, opts, `${alias}.env["${envName}"]`) : null;
|
||||
}
|
||||
|
||||
function buildOverrideDescriptors(
|
||||
{ dirname, options },
|
||||
alias,
|
||||
descriptors,
|
||||
index,
|
||||
) {
|
||||
const opts = options.overrides && options.overrides[index];
|
||||
if (!opts) throw new Error("Assertion failure - missing override");
|
||||
|
||||
return descriptors(dirname, opts, `${alias}.overrides[${index}]`);
|
||||
}
|
||||
|
||||
function buildOverrideEnvDescriptors(
|
||||
{ dirname, options },
|
||||
alias,
|
||||
descriptors,
|
||||
index,
|
||||
envName,
|
||||
) {
|
||||
const override = options.overrides && options.overrides[index];
|
||||
if (!override) throw new Error("Assertion failure - missing override");
|
||||
|
||||
const opts = override.env && override.env[envName];
|
||||
return opts
|
||||
? descriptors(
|
||||
dirname,
|
||||
opts,
|
||||
`${alias}.overrides[${index}].env["${envName}"]`,
|
||||
)
|
||||
: null;
|
||||
}
|
||||
|
||||
function makeChainWalker<
|
||||
ArgT,
|
||||
InnerT: { options: ValidatedOptions, dirname: string },
|
||||
@@ -192,10 +287,14 @@ function makeChainWalker<
|
||||
init,
|
||||
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,
|
||||
}): (ArgT, ConfigContext, Set<ConfigFile> | void) => ConfigChain | null {
|
||||
return (arg, context, files = new Set()) => {
|
||||
const input = init(arg);
|
||||
@@ -212,6 +311,21 @@ function makeChainWalker<
|
||||
if (envOpts && configIsApplicable(envOpts, dirname, context)) {
|
||||
flattenedConfigs.push(envOpts);
|
||||
}
|
||||
|
||||
(rootOpts.options.overrides || []).forEach((_, index) => {
|
||||
const overrideOps = overrides(input, index);
|
||||
if (configIsApplicable(overrideOps, dirname, context)) {
|
||||
flattenedConfigs.push(overrideOps);
|
||||
|
||||
const overrideEnvOpts = overridesEnv(input, index, context.envName);
|
||||
if (
|
||||
overrideEnvOpts &&
|
||||
configIsApplicable(overrideEnvOpts, dirname, context)
|
||||
) {
|
||||
flattenedConfigs.push(overrideEnvOpts);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Process 'ignore' and 'only' before 'extends' items are processed so
|
||||
|
||||
@@ -105,6 +105,13 @@ export function assertObject(key: string, value: mixed): {} | void {
|
||||
return value;
|
||||
}
|
||||
|
||||
export function assertArray(key: string, value: mixed): ?$ReadOnlyArray<mixed> {
|
||||
if (value != null && !Array.isArray(value)) {
|
||||
throw new Error(`.${key} must be an array, or undefined`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export function assertIgnoreList(key: string, value: mixed): IgnoreList | void {
|
||||
const arr = assertArray(key, value);
|
||||
if (arr) {
|
||||
@@ -225,10 +232,3 @@ function assertPluginTarget(
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function assertArray(key: string, value: mixed): ?$ReadOnlyArray<mixed> {
|
||||
if (value != null && !Array.isArray(value)) {
|
||||
throw new Error(`.${key} must be an array, or undefined`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
assertString,
|
||||
assertBoolean,
|
||||
assertObject,
|
||||
assertArray,
|
||||
assertInputSourceMap,
|
||||
assertIgnoreList,
|
||||
assertPluginList,
|
||||
@@ -45,6 +46,9 @@ const NONPRESET_VALIDATORS: ValidatorSet = {
|
||||
$PropertyType<ValidatedOptions, "ignore">,
|
||||
>),
|
||||
only: (assertIgnoreList: Validator<$PropertyType<ValidatedOptions, "only">>),
|
||||
overrides: (assertOverridesList: Validator<
|
||||
$PropertyType<ValidatedOptions, "overrides">,
|
||||
>),
|
||||
|
||||
// We could limit these to 'overrides' blocks, but it's not clear why we'd
|
||||
// bother, when the ability to limit a config to a specific set of files
|
||||
@@ -156,6 +160,7 @@ export type ValidatedOptions = {
|
||||
env?: EnvSet<ValidatedOptions>,
|
||||
ignore?: IgnoreList,
|
||||
only?: IgnoreList,
|
||||
overrides?: OverridesList,
|
||||
|
||||
// Generally verify if a given config object should be applied to the given file.
|
||||
test?: ConfigApplicableTest,
|
||||
@@ -215,6 +220,7 @@ export type PluginItem =
|
||||
| [PluginTarget, PluginOptions, string];
|
||||
export type PluginList = $ReadOnlyArray<PluginItem>;
|
||||
|
||||
export type OverridesList = Array<ValidatedOptions>;
|
||||
export type ConfigApplicableTest = IgnoreItem | Array<IgnoreItem>;
|
||||
|
||||
export type SourceMapsOption = boolean | "inline" | "both";
|
||||
@@ -222,7 +228,7 @@ export type SourceTypeOption = "module" | "script" | "unambiguous";
|
||||
export type CompactOption = boolean | "auto";
|
||||
export type RootInputSourceMapOption = {} | boolean;
|
||||
|
||||
export type OptionsType = "arguments" | "file" | "env" | "preset";
|
||||
export type OptionsType = "arguments" | "file" | "env" | "preset" | "override";
|
||||
|
||||
export function validate(type: OptionsType, opts: {}): ValidatedOptions {
|
||||
assertNoDuplicateSourcemap(opts);
|
||||
@@ -237,6 +243,12 @@ export function validate(type: OptionsType, opts: {}): ValidatedOptions {
|
||||
if (type === "env" && key === "env") {
|
||||
throw new Error(`.${key} is not allowed inside another env block`);
|
||||
}
|
||||
if (type === "env" && key === "overrides") {
|
||||
throw new Error(`.${key} is not allowed inside an env block`);
|
||||
}
|
||||
if (type === "override" && key === "overrides") {
|
||||
throw new Error(`.${key} is not allowed inside an overrides block`);
|
||||
}
|
||||
|
||||
const validator =
|
||||
COMMON_VALIDATORS[key] ||
|
||||
@@ -287,3 +299,16 @@ function assertEnvSet(key: string, value: mixed): EnvSet<ValidatedOptions> {
|
||||
}
|
||||
return (obj: any);
|
||||
}
|
||||
|
||||
function assertOverridesList(key: string, value: mixed): OverridesList {
|
||||
const arr = assertArray(key, value);
|
||||
if (arr) {
|
||||
for (const [index, item] of arr.entries()) {
|
||||
const env = assertObject(`${index}`, item);
|
||||
if (!env) throw new Error(`.${key}[${index}] must be an object`);
|
||||
|
||||
validate("override", env);
|
||||
}
|
||||
}
|
||||
return (arr: any);
|
||||
}
|
||||
|
||||
@@ -801,6 +801,40 @@ describe("buildConfigChain", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("overrides merging", () => {
|
||||
it("should apply matching overrides over base configs", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
comments: true,
|
||||
overrides: [
|
||||
{
|
||||
test: fixture("nonexistant-fake"),
|
||||
comments: false,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, false);
|
||||
});
|
||||
|
||||
it("should not apply non-matching overrides over base configs", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
comments: true,
|
||||
overrides: [
|
||||
{
|
||||
test: fixture("nonexistant-unknown"),
|
||||
comments: false,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("config files", () => {
|
||||
const getDefaults = () => ({
|
||||
babelrc: false,
|
||||
|
||||
Reference in New Issue
Block a user