Merge pull request #7091 from loganfsmyth/config-overrides
Allow configs to have an 'overrides' array
This commit is contained in:
commit
a19349a22a
@ -7,6 +7,7 @@ import {
|
||||
validate,
|
||||
type ValidatedOptions,
|
||||
type IgnoreList,
|
||||
type ConfigApplicableTest,
|
||||
} from "./validation/options";
|
||||
|
||||
const debug = buildDebug("babel:config:config-chain");
|
||||
@ -40,7 +41,7 @@ export type PresetInstance = {
|
||||
dirname: string,
|
||||
};
|
||||
|
||||
type ConfigContext = {
|
||||
export type ConfigContext = {
|
||||
filename: string | null,
|
||||
cwd: string,
|
||||
envName: string,
|
||||
@ -54,16 +55,53 @@ type ConfigContextNamed = {
|
||||
/**
|
||||
* Build a config chain for a given preset.
|
||||
*/
|
||||
export const buildPresetChain = makeWeakCache(
|
||||
({ dirname, options, alias }: PresetInstance): ConfigChain => {
|
||||
const result = createUncachedDescriptors(dirname, options, alias);
|
||||
const { plugins, presets } = result;
|
||||
return {
|
||||
plugins: plugins(),
|
||||
presets: presets(),
|
||||
options: [normalizeOptions(result.options)],
|
||||
};
|
||||
},
|
||||
export const buildPresetChain: (
|
||||
arg: PresetInstance,
|
||||
context: *,
|
||||
) => * = makeChainWalker({
|
||||
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),
|
||||
);
|
||||
const loadPresetEnvDescriptors = makeWeakCache((preset: PresetInstance) =>
|
||||
makeStrongCache((envName: string) =>
|
||||
buildEnvDescriptors(
|
||||
preset,
|
||||
preset.alias,
|
||||
createUncachedDescriptors,
|
||||
envName,
|
||||
),
|
||||
),
|
||||
);
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
@ -72,14 +110,8 @@ export const buildPresetChain = makeWeakCache(
|
||||
export function buildRootChain(
|
||||
cwd: string,
|
||||
opts: ValidatedOptions,
|
||||
envName: string,
|
||||
context: ConfigContext,
|
||||
): ConfigChain | null {
|
||||
const context = {
|
||||
filename: opts.filename ? path.resolve(cwd, opts.filename) : null,
|
||||
cwd,
|
||||
envName,
|
||||
};
|
||||
|
||||
const programmaticChain = loadProgrammaticChain(
|
||||
{
|
||||
options: opts,
|
||||
@ -137,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,
|
||||
),
|
||||
});
|
||||
|
||||
/**
|
||||
@ -146,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,
|
||||
@ -165,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);
|
||||
@ -180,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 },
|
||||
@ -187,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);
|
||||
@ -200,11 +304,28 @@ function makeChainWalker<
|
||||
const flattenedConfigs = [];
|
||||
|
||||
const rootOpts = root(input);
|
||||
flattenedConfigs.push(rootOpts);
|
||||
if (configIsApplicable(rootOpts, dirname, context)) {
|
||||
flattenedConfigs.push(rootOpts);
|
||||
|
||||
const envOpts = env(input, context.envName);
|
||||
if (envOpts) {
|
||||
flattenedConfigs.push(envOpts);
|
||||
const envOpts = env(input, context.envName);
|
||||
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
|
||||
@ -351,6 +472,42 @@ function dedupDescriptors(
|
||||
}, []);
|
||||
}
|
||||
|
||||
function configIsApplicable(
|
||||
{ options }: OptionsAndDescriptors,
|
||||
dirname: string,
|
||||
context: ConfigContext,
|
||||
): boolean {
|
||||
return (
|
||||
(options.test === undefined ||
|
||||
configFieldIsApplicable(context, options.test, dirname)) &&
|
||||
(options.include === undefined ||
|
||||
configFieldIsApplicable(context, options.include, dirname)) &&
|
||||
(options.exclude === undefined ||
|
||||
!configFieldIsApplicable(context, options.exclude, dirname))
|
||||
);
|
||||
}
|
||||
|
||||
function configFieldIsApplicable(
|
||||
context: ConfigContext,
|
||||
test: ConfigApplicableTest,
|
||||
dirname: string,
|
||||
): boolean {
|
||||
if (context.filename === null) {
|
||||
throw new Error(
|
||||
`Configuration contains explicit test/include/exclude checks, but no filename was passed to Babel`,
|
||||
);
|
||||
}
|
||||
// $FlowIgnore - Flow refinements aren't quite smart enough for this :(
|
||||
const ctx: ConfigContextNamed = context;
|
||||
|
||||
const patterns = Array.isArray(test) ? test : [test];
|
||||
|
||||
// Disabling negation here because it's a bit buggy from
|
||||
// https://github.com/babel/babel/issues/6907 and it's not clear that it is
|
||||
// needed since users can use 'exclude' alongside 'test'/'include'.
|
||||
return matchesPatterns(ctx, patterns, dirname, false /* allowNegation */);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if a filename should be ignored based on "ignore" and "only" options.
|
||||
*/
|
||||
@ -360,11 +517,14 @@ function shouldIgnore(
|
||||
only: ?IgnoreList,
|
||||
dirname: string,
|
||||
): boolean {
|
||||
if (context.filename === null) return false;
|
||||
// $FlowIgnore - Flow refinements aren't quite smart enough for this :(
|
||||
const ctx: ConfigContextNamed = context;
|
||||
|
||||
if (ignore) {
|
||||
if (context.filename === null) {
|
||||
throw new Error(
|
||||
`Configuration contains ignore checks, but no filename was passed to Babel`,
|
||||
);
|
||||
}
|
||||
// $FlowIgnore - Flow refinements aren't quite smart enough for this :(
|
||||
const ctx: ConfigContextNamed = context;
|
||||
if (matchesPatterns(ctx, ignore, dirname)) {
|
||||
debug(
|
||||
"Ignored %o because it matched one of %O from %o",
|
||||
@ -377,6 +537,14 @@ function shouldIgnore(
|
||||
}
|
||||
|
||||
if (only) {
|
||||
if (context.filename === null) {
|
||||
throw new Error(
|
||||
`Configuration contains ignore checks, but no filename was passed to Babel`,
|
||||
);
|
||||
}
|
||||
// $FlowIgnore - Flow refinements aren't quite smart enough for this :(
|
||||
const ctx: ConfigContextNamed = context;
|
||||
|
||||
if (!matchesPatterns(ctx, only, dirname)) {
|
||||
debug(
|
||||
"Ignored %o because it failed to match one of %O from %o",
|
||||
@ -399,6 +567,7 @@ function matchesPatterns(
|
||||
context: ConfigContextNamed,
|
||||
patterns: IgnoreList,
|
||||
dirname: string,
|
||||
allowNegation?: boolean = true,
|
||||
): boolean {
|
||||
const res = [];
|
||||
const strings = [];
|
||||
@ -420,13 +589,19 @@ function matchesPatterns(
|
||||
const absolutePatterns = strings.map(pattern => {
|
||||
// Preserve the "!" prefix so that micromatch can use it for negation.
|
||||
const negate = pattern[0] === "!";
|
||||
if (negate && !allowNegation) {
|
||||
throw new Error(`Negation of file paths is not supported.`);
|
||||
}
|
||||
if (negate) pattern = pattern.slice(1);
|
||||
|
||||
return (negate ? "!" : "") + path.resolve(dirname, pattern);
|
||||
});
|
||||
|
||||
if (
|
||||
micromatch(possibleDirs, absolutePatterns, { nocase: true }).length > 0
|
||||
micromatch(possibleDirs, absolutePatterns, {
|
||||
nocase: true,
|
||||
nonegate: !allowNegation,
|
||||
}).length > 0
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import merge from "lodash/merge";
|
||||
import {
|
||||
buildRootChain,
|
||||
buildPresetChain,
|
||||
type ConfigContext,
|
||||
type ConfigChain,
|
||||
type PresetInstance,
|
||||
} from "./config-chain";
|
||||
@ -36,6 +37,12 @@ export type { Plugin };
|
||||
export type PluginPassList = Array<Plugin>;
|
||||
export type PluginPasses = Array<PluginPassList>;
|
||||
|
||||
// Context not including filename since it is used in places that cannot
|
||||
// process 'ignore'/'only' and other filename-based logic.
|
||||
type SimpleContext = {
|
||||
envName: string,
|
||||
};
|
||||
|
||||
export default function loadConfig(inputOpts: mixed): ResolvedConfig | null {
|
||||
if (
|
||||
inputOpts != null &&
|
||||
@ -49,27 +56,32 @@ export default function loadConfig(inputOpts: mixed): ResolvedConfig | null {
|
||||
const { envName = getEnv(), cwd = "." } = args;
|
||||
const absoluteCwd = path.resolve(cwd);
|
||||
|
||||
const configChain = buildRootChain(absoluteCwd, args, envName);
|
||||
const context: ConfigContext = {
|
||||
filename: args.filename ? path.resolve(cwd, args.filename) : null,
|
||||
cwd: absoluteCwd,
|
||||
envName,
|
||||
};
|
||||
|
||||
const configChain = buildRootChain(absoluteCwd, args, context);
|
||||
if (!configChain) return null;
|
||||
|
||||
const optionDefaults = {};
|
||||
const options = {};
|
||||
const passes = [[]];
|
||||
try {
|
||||
(function recurseDescriptors(
|
||||
const ignored = (function recurseDescriptors(
|
||||
config: {
|
||||
plugins: Array<UnloadedDescriptor>,
|
||||
presets: Array<UnloadedDescriptor>,
|
||||
},
|
||||
pass: Array<Plugin>,
|
||||
envName: string,
|
||||
) {
|
||||
const plugins = config.plugins.map(descriptor =>
|
||||
loadPluginDescriptor(descriptor, envName),
|
||||
loadPluginDescriptor(descriptor, context),
|
||||
);
|
||||
const presets = config.presets.map(descriptor => {
|
||||
return {
|
||||
preset: loadPresetDescriptor(descriptor, envName),
|
||||
preset: loadPresetDescriptor(descriptor, context),
|
||||
pass: descriptor.ownPass ? [] : pass,
|
||||
};
|
||||
});
|
||||
@ -85,14 +97,16 @@ export default function loadConfig(inputOpts: mixed): ResolvedConfig | null {
|
||||
);
|
||||
|
||||
for (const { preset, pass } of presets) {
|
||||
recurseDescriptors(
|
||||
if (!preset) return true;
|
||||
|
||||
const ignored = recurseDescriptors(
|
||||
{
|
||||
plugins: preset.plugins,
|
||||
presets: preset.presets,
|
||||
},
|
||||
pass,
|
||||
envName,
|
||||
);
|
||||
if (ignored) return true;
|
||||
|
||||
preset.options.forEach(opts => {
|
||||
merge(optionDefaults, opts);
|
||||
@ -110,9 +124,10 @@ export default function loadConfig(inputOpts: mixed): ResolvedConfig | null {
|
||||
presets: configChain.presets,
|
||||
},
|
||||
passes[0],
|
||||
envName,
|
||||
);
|
||||
|
||||
if (ignored) return null;
|
||||
|
||||
configChain.options.forEach(opts => {
|
||||
merge(options, opts);
|
||||
});
|
||||
@ -152,7 +167,7 @@ export default function loadConfig(inputOpts: mixed): ResolvedConfig | null {
|
||||
const loadDescriptor = makeWeakCache(
|
||||
(
|
||||
{ value, options, dirname, alias }: UnloadedDescriptor,
|
||||
cache: CacheConfigurator<{ envName: string }>,
|
||||
cache: CacheConfigurator<SimpleContext>,
|
||||
): LoadedDescriptor => {
|
||||
// Disabled presets should already have been filtered out
|
||||
if (options === false) throw new Error("Assertion failure");
|
||||
@ -199,7 +214,7 @@ const loadDescriptor = makeWeakCache(
|
||||
*/
|
||||
function loadPluginDescriptor(
|
||||
descriptor: UnloadedDescriptor,
|
||||
envName: string,
|
||||
context: SimpleContext,
|
||||
): Plugin {
|
||||
if (descriptor.value instanceof Plugin) {
|
||||
if (descriptor.options) {
|
||||
@ -211,15 +226,13 @@ function loadPluginDescriptor(
|
||||
return descriptor.value;
|
||||
}
|
||||
|
||||
return instantiatePlugin(loadDescriptor(descriptor, { envName }), {
|
||||
envName,
|
||||
});
|
||||
return instantiatePlugin(loadDescriptor(descriptor, context), context);
|
||||
}
|
||||
|
||||
const instantiatePlugin = makeWeakCache(
|
||||
(
|
||||
{ value, options, dirname, alias }: LoadedDescriptor,
|
||||
cache: CacheConfigurator<{ envName: string }>,
|
||||
cache: CacheConfigurator<SimpleContext>,
|
||||
): Plugin => {
|
||||
const pluginObj = validatePluginObject(value);
|
||||
|
||||
@ -239,7 +252,7 @@ const instantiatePlugin = makeWeakCache(
|
||||
|
||||
// If the inherited plugin changes, reinstantiate this plugin.
|
||||
const inherits = cache.invalidate(data =>
|
||||
loadPluginDescriptor(inheritsDescriptor, data.envName),
|
||||
loadPluginDescriptor(inheritsDescriptor, data),
|
||||
);
|
||||
|
||||
plugin.pre = chain(inherits.pre, plugin.pre);
|
||||
@ -263,10 +276,11 @@ const instantiatePlugin = makeWeakCache(
|
||||
*/
|
||||
const loadPresetDescriptor = (
|
||||
descriptor: UnloadedDescriptor,
|
||||
envName: string,
|
||||
): ConfigChain => {
|
||||
context: ConfigContext,
|
||||
): ConfigChain | null => {
|
||||
return buildPresetChain(
|
||||
instantiatePreset(loadDescriptor(descriptor, { envName })),
|
||||
instantiatePreset(loadDescriptor(descriptor, context)),
|
||||
context,
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import type {
|
||||
PluginList,
|
||||
PluginItem,
|
||||
PluginTarget,
|
||||
ConfigApplicableTest,
|
||||
SourceMapsOption,
|
||||
SourceTypeOption,
|
||||
CompactOption,
|
||||
@ -104,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) {
|
||||
@ -122,12 +130,38 @@ function assertIgnoreItem(
|
||||
!(value instanceof RegExp)
|
||||
) {
|
||||
throw new Error(
|
||||
`.${key}[${index}] must be an array of string/Funtion/RegExp values, or or undefined`,
|
||||
`.${key}[${index}] must be an array of string/Funtion/RegExp values, or undefined`,
|
||||
);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export function assertConfigApplicableTest(
|
||||
key: string,
|
||||
value: mixed,
|
||||
): ConfigApplicableTest | void {
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach((item, i) => {
|
||||
if (!checkValidTest(item)) {
|
||||
throw new Error(`.${key}[${i}] must be a string/Function/RegExp.`);
|
||||
}
|
||||
});
|
||||
} else if (!checkValidTest(value)) {
|
||||
throw new Error(
|
||||
`.${key} must be a string/Function/RegExp, or an array of those`,
|
||||
);
|
||||
}
|
||||
return (value: any);
|
||||
}
|
||||
|
||||
function checkValidTest(value: mixed): boolean {
|
||||
return (
|
||||
typeof value === "string" ||
|
||||
typeof value === "function" ||
|
||||
value instanceof RegExp
|
||||
);
|
||||
}
|
||||
|
||||
export function assertPluginList(key: string, value: mixed): PluginList | void {
|
||||
const arr = assertArray(key, value);
|
||||
if (arr) {
|
||||
@ -198,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,9 +5,11 @@ import {
|
||||
assertString,
|
||||
assertBoolean,
|
||||
assertObject,
|
||||
assertArray,
|
||||
assertInputSourceMap,
|
||||
assertIgnoreList,
|
||||
assertPluginList,
|
||||
assertConfigApplicableTest,
|
||||
assertFunction,
|
||||
assertSourceMaps,
|
||||
assertCompact,
|
||||
@ -44,6 +46,22 @@ 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
|
||||
// is a fairly general useful feature.
|
||||
test: (assertConfigApplicableTest: Validator<
|
||||
$PropertyType<ValidatedOptions, "test">,
|
||||
>),
|
||||
include: (assertConfigApplicableTest: Validator<
|
||||
$PropertyType<ValidatedOptions, "include">,
|
||||
>),
|
||||
exclude: (assertConfigApplicableTest: Validator<
|
||||
$PropertyType<ValidatedOptions, "exclude">,
|
||||
>),
|
||||
};
|
||||
|
||||
const COMMON_VALIDATORS: ValidatorSet = {
|
||||
@ -142,6 +160,12 @@ 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,
|
||||
include?: ConfigApplicableTest,
|
||||
exclude?: ConfigApplicableTest,
|
||||
|
||||
presets?: PluginList,
|
||||
plugins?: PluginList,
|
||||
@ -196,12 +220,15 @@ 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";
|
||||
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);
|
||||
@ -216,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] ||
|
||||
@ -266,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);
|
||||
}
|
||||
|
||||
@ -8,6 +8,420 @@ function fixture(...args) {
|
||||
}
|
||||
|
||||
describe("buildConfigChain", function() {
|
||||
describe("test", () => {
|
||||
describe("single", () => {
|
||||
it("should process matching string values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
test: fixture("nonexistant-fake"),
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, true);
|
||||
});
|
||||
|
||||
it("should process matching RegExp values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
test: new RegExp(fixture("nonexistant-fake")),
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, true);
|
||||
});
|
||||
|
||||
it("should process matching function values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
test: p => p.indexOf(fixture("nonexistant-fake")) === 0,
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, true);
|
||||
});
|
||||
|
||||
it("should process non-matching string values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
test: fixture("nonexistant-fake-unknown"),
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, undefined);
|
||||
});
|
||||
|
||||
it("should process non-matching RegExp values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
test: new RegExp(fixture("nonexistant-unknown")),
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, undefined);
|
||||
});
|
||||
|
||||
it("should process non-matching function values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
test: p => p.indexOf(fixture("nonexistant-unknown")) === 0,
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe("array", () => {
|
||||
it("should process matching string values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
test: [fixture("nonexistant-fake")],
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, true);
|
||||
});
|
||||
|
||||
it("should process matching RegExp values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
test: [new RegExp(fixture("nonexistant-fake"))],
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, true);
|
||||
});
|
||||
|
||||
it("should process matching function values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
test: [p => p.indexOf(fixture("nonexistant-fake")) === 0],
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, true);
|
||||
});
|
||||
|
||||
it("should process non-matching string values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
test: [fixture("nonexistant-fake-unknown")],
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, undefined);
|
||||
});
|
||||
|
||||
it("should process non-matching RegExp values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
test: [new RegExp(fixture("nonexistant-unknown"))],
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, undefined);
|
||||
});
|
||||
|
||||
it("should process non-matching function values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
test: [p => p.indexOf(fixture("nonexistant-unknown")) === 0],
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("include", () => {
|
||||
describe("single", () => {
|
||||
it("should process matching string values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
include: fixture("nonexistant-fake"),
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, true);
|
||||
});
|
||||
|
||||
it("should process matching RegExp values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
include: new RegExp(fixture("nonexistant-fake")),
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, true);
|
||||
});
|
||||
|
||||
it("should process matching function values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
include: p => p.indexOf(fixture("nonexistant-fake")) === 0,
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, true);
|
||||
});
|
||||
|
||||
it("should process non-matching string values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
include: fixture("nonexistant-fake-unknown"),
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, undefined);
|
||||
});
|
||||
|
||||
it("should process non-matching RegExp values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
include: new RegExp(fixture("nonexistant-unknown")),
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, undefined);
|
||||
});
|
||||
|
||||
it("should process non-matching function values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
include: p => p.indexOf(fixture("nonexistant-unknown")) === 0,
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe("array", () => {
|
||||
it("should process matching string values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
include: [fixture("nonexistant-fake")],
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, true);
|
||||
});
|
||||
|
||||
it("should process matching RegExp values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
include: [new RegExp(fixture("nonexistant-fake"))],
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, true);
|
||||
});
|
||||
|
||||
it("should process matching function values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
include: [p => p.indexOf(fixture("nonexistant-fake")) === 0],
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, true);
|
||||
});
|
||||
|
||||
it("should process non-matching string values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
include: [fixture("nonexistant-fake-unknown")],
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, undefined);
|
||||
});
|
||||
|
||||
it("should process non-matching RegExp values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
include: [new RegExp(fixture("nonexistant-unknown"))],
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, undefined);
|
||||
});
|
||||
|
||||
it("should process non-matching function values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
include: [p => p.indexOf(fixture("nonexistant-unknown")) === 0],
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("exclude", () => {
|
||||
describe("single", () => {
|
||||
it("should process matching string values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
exclude: fixture("nonexistant-fake"),
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, undefined);
|
||||
});
|
||||
|
||||
it("should process matching RegExp values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
exclude: new RegExp(fixture("nonexistant-fake")),
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, undefined);
|
||||
});
|
||||
|
||||
it("should process matching function values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
exclude: p => p.indexOf(fixture("nonexistant-fake")) === 0,
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, undefined);
|
||||
});
|
||||
|
||||
it("should process non-matching string values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
exclude: fixture("nonexistant-fake-unknown"),
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, true);
|
||||
});
|
||||
|
||||
it("should process non-matching RegExp values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
exclude: new RegExp(fixture("nonexistant-unknown")),
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, true);
|
||||
});
|
||||
|
||||
it("should process non-matching function values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
exclude: p => p.indexOf(fixture("nonexistant-unknown")) === 0,
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("array", () => {
|
||||
it("should process matching string values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
exclude: [fixture("nonexistant-fake")],
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, undefined);
|
||||
});
|
||||
|
||||
it("should process matching RegExp values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
exclude: [new RegExp(fixture("nonexistant-fake"))],
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, undefined);
|
||||
});
|
||||
|
||||
it("should process matching function values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
exclude: [p => p.indexOf(fixture("nonexistant-fake")) === 0],
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, undefined);
|
||||
});
|
||||
|
||||
it("should process non-matching string values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
exclude: [fixture("nonexistant-fake-unknown")],
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, true);
|
||||
});
|
||||
|
||||
it("should process non-matching RegExp values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
exclude: [new RegExp(fixture("nonexistant-unknown"))],
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, true);
|
||||
});
|
||||
|
||||
it("should process non-matching function values", () => {
|
||||
const opts = loadOptions({
|
||||
filename: fixture("nonexistant-fake", "src.js"),
|
||||
babelrc: false,
|
||||
exclude: [p => p.indexOf(fixture("nonexistant-unknown")) === 0],
|
||||
comments: true,
|
||||
});
|
||||
|
||||
assert.equal(opts.comments, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("ignore", () => {
|
||||
it("should ignore files that match", () => {
|
||||
const opts = loadOptions({
|
||||
@ -387,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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user