diff --git a/packages/babel-core/src/config/config-chain.js b/packages/babel-core/src/config/config-chain.js index 7a62958d32..60e062d3f8 100644 --- a/packages/babel-core/src/config/config-chain.js +++ b/packages/babel-core/src/config/config-chain.js @@ -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 | 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; } diff --git a/packages/babel-core/src/config/index.js b/packages/babel-core/src/config/index.js index 33ab644a8b..6a770e5aca 100644 --- a/packages/babel-core/src/config/index.js +++ b/packages/babel-core/src/config/index.js @@ -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; export type PluginPasses = Array; +// 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, presets: Array, }, pass: Array, - 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, ): 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, ): 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, ); }; diff --git a/packages/babel-core/src/config/validation/option-assertions.js b/packages/babel-core/src/config/validation/option-assertions.js index facdbbf345..c6330e7176 100644 --- a/packages/babel-core/src/config/validation/option-assertions.js +++ b/packages/babel-core/src/config/validation/option-assertions.js @@ -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 { + 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 { - if (value != null && !Array.isArray(value)) { - throw new Error(`.${key} must be an array, or undefined`); - } - return value; -} diff --git a/packages/babel-core/src/config/validation/options.js b/packages/babel-core/src/config/validation/options.js index ff5ba9602f..96b51f73ef 100644 --- a/packages/babel-core/src/config/validation/options.js +++ b/packages/babel-core/src/config/validation/options.js @@ -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, >), only: (assertIgnoreList: Validator<$PropertyType>), + overrides: (assertOverridesList: Validator< + $PropertyType, + >), + + // 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, + >), + include: (assertConfigApplicableTest: Validator< + $PropertyType, + >), + exclude: (assertConfigApplicableTest: Validator< + $PropertyType, + >), }; const COMMON_VALIDATORS: ValidatorSet = { @@ -142,6 +160,12 @@ export type ValidatedOptions = { env?: EnvSet, 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; +export type OverridesList = Array; +export type ConfigApplicableTest = IgnoreItem | Array; + 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 { } 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); +} diff --git a/packages/babel-core/test/config-chain.js b/packages/babel-core/test/config-chain.js index 651639ac36..f40ae9989b 100644 --- a/packages/babel-core/test/config-chain.js +++ b/packages/babel-core/test/config-chain.js @@ -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,