Merge pull request #6834 from loganfsmyth/caching-refactor

Expose `envName` as a top-level Babel option to avoid using environmental variables
This commit is contained in:
Logan Smyth 2017-11-24 17:42:16 -08:00 committed by GitHub
commit a89171910f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 657 additions and 516 deletions

View File

@ -49,6 +49,11 @@ commander.option(
collect, collect,
); );
commander.option("--config-file [path]", "Path a to .babelrc file to use"); commander.option("--config-file [path]", "Path a to .babelrc file to use");
commander.option(
"--env-name [name]",
"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'.",
);
// Basic file input configuration. // Basic file input configuration.
commander.option("--source-type [script|module]", ""); commander.option("--source-type [script|module]", "");

View File

@ -144,10 +144,11 @@ Following is a table of the options you can use:
| `auxiliaryCommentAfter` | `null` | Attach a comment after all non-user injected code | | `auxiliaryCommentAfter` | `null` | Attach a comment after all non-user injected code |
| `auxiliaryCommentBefore` | `null` | Attach a comment before all non-user injected code | | `auxiliaryCommentBefore` | `null` | Attach a comment before all non-user injected code |
| `babelrc` | `true` | Specify whether or not to use .babelrc and .babelignore files. Not available when using the CLI, [use `--no-babelrc` instead](https://babeljs.io/docs/usage/cli/#babel-ignoring-babelrc) | | `babelrc` | `true` | Specify whether or not to use .babelrc and .babelignore files. Not available when using the CLI, [use `--no-babelrc` instead](https://babeljs.io/docs/usage/cli/#babel-ignoring-babelrc) |
| `envName` | env vars | Defaults to environment variable `BABEL_ENV` if set, or else `NODE_ENV` if set, or else it defaults to `"development"` |
| `code` | `true` | Enable code generation | | `code` | `true` | Enable code generation |
| `comments` | `true` | Output comments in generated output | | `comments` | `true` | Output comments in generated output |
| `compact` | `"auto"` | Do not include superfluous whitespace characters and line terminators. When set to `"auto"` compact is set to `true` on input sizes of >500KB | | `compact` | `"auto"` | Do not include superfluous whitespace characters and line terminators. When set to `"auto"` compact is set to `true` on input sizes of >500KB |
| `env` | `{}` | This is an object of keys that represent different environments. For example, you may have: `{ env: { production: { /* specific options */ } } }` which will use those options when the environment variable `BABEL_ENV` is set to `"production"`. If `BABEL_ENV` isn't set then `NODE_ENV` will be used, if it's not set then it defaults to `"development"` | | `env` | `{}` | This is an object of keys that represent different environments. For example, you may have: `{ env: { production: { /* specific options */ } } }` which will use those options when the `envName` is `production` |
| `extends` | `null` | A path to a `.babelrc` file to extend | | `extends` | `null` | A path to a `.babelrc` file to extend |
| `filename` | `"unknown"` | Filename for use in errors etc | | `filename` | `"unknown"` | Filename for use in errors etc |
| `filenameRelative` | `(filename)` | Filename relative to `sourceRoot` | | `filenameRelative` | `(filename)` | Filename relative to `sourceRoot` |

View File

@ -1,6 +1,5 @@
// @flow // @flow
import { getEnv } from "./helpers/environment";
import path from "path"; import path from "path";
import micromatch from "micromatch"; import micromatch from "micromatch";
import buildDebug from "debug"; import buildDebug from "debug";
@ -41,20 +40,20 @@ type ConfigPart =
export default function buildConfigChain( export default function buildConfigChain(
opts: ValidatedOptions, opts: ValidatedOptions,
envName: string,
): Array<ConfigItem> | null { ): Array<ConfigItem> | null {
const filename = opts.filename ? path.resolve(opts.filename) : null; const filename = opts.filename ? path.resolve(opts.filename) : null;
const builder = new ConfigChainBuilder( const builder = new ConfigChainBuilder(
filename ? new LoadedFile(filename) : null, filename ? new LoadedFile(filename) : null,
); );
const envKey = getEnv();
try { try {
builder.mergeConfigArguments(opts, process.cwd(), envKey); builder.mergeConfigArguments(opts, process.cwd(), envName);
// resolve all .babelrc files // resolve all .babelrc files
if (opts.babelrc !== false && filename) { if (opts.babelrc !== false && filename) {
findConfigs(path.dirname(filename)).forEach(configFile => findConfigs(path.dirname(filename), envName).forEach(configFile =>
builder.mergeConfigFile(configFile, envKey), builder.mergeConfigFile(configFile, envName),
); );
} }
} catch (e) { } catch (e) {
@ -85,21 +84,21 @@ class ConfigChainBuilder {
); );
} }
mergeConfigFile(file: ConfigFile, envKey: string) { mergeConfigFile(file: ConfigFile, envName: string) {
if (this.seenFiles.has(file)) { if (this.seenFiles.has(file)) {
throw new Error( throw new Error(
`Cycle detected in Babel configuration file through "${file.filepath}".`, `Cycle detected in Babel configuration file through "${file.filepath}".`,
); );
} }
const parts = flattenFileOptionsParts(file)(envKey); const parts = flattenFileOptionsParts(file)(envName);
this.seenFiles.add(file); this.seenFiles.add(file);
parts.forEach(part => this._processConfigPart(part, envKey)); parts.forEach(part => this._processConfigPart(part, envName));
this.seenFiles.delete(file); this.seenFiles.delete(file);
} }
_processConfigPart(part: ConfigPart, envKey: string) { _processConfigPart(part: ConfigPart, envName: string) {
if (part.part === "config") { if (part.part === "config") {
const { ignore, only } = part; const { ignore, only } = part;
@ -116,7 +115,10 @@ class ConfigChainBuilder {
this.configs.push(part.config); this.configs.push(part.config);
} else { } else {
this.mergeConfigFile(loadConfig(part.path, part.dirname), envKey); this.mergeConfigFile(
loadConfig(part.path, part.dirname, envName),
envName,
);
} }
} }
} }
@ -129,7 +131,7 @@ class ConfigChainBuilder {
function flattenArgumentsOptionsParts( function flattenArgumentsOptionsParts(
opts: ValidatedOptions, opts: ValidatedOptions,
dirname: string, dirname: string,
envKey: string, envName: string,
): Array<ConfigPart> { ): Array<ConfigPart> {
const { const {
env, env,
@ -142,7 +144,7 @@ function flattenArgumentsOptionsParts(
const raw = []; const raw = [];
if (env) { if (env) {
raw.push(...flattenArgumentsEnvOptionsParts(env)(dirname)(envKey)); raw.push(...flattenArgumentsEnvOptionsParts(env)(dirname)(envName));
} }
if (Object.keys(options).length > 0) { if (Object.keys(options).length > 0) {
@ -263,7 +265,7 @@ function flattenOptionsPartsLookup(
}); });
} }
return envKey => lookup.get(envKey) || def; return envName => lookup.get(envName) || def;
} }
/** /**

View File

@ -1,29 +1,33 @@
// @flow // @flow
type CacheConfigurator = CacheConfiguratorFn & CacheConfiguratorObj; type SimpleCacheConfigurator = SimpleCacheConfiguratorFn &
SimpleCacheConfiguratorObj;
type CacheConfiguratorFn = { type SimpleCacheConfiguratorFn = {
(boolean): void, (boolean): void,
<T>(handler: () => T): T, <T>(handler: () => T): T,
}; };
type CacheConfiguratorObj = { type SimpleCacheConfiguratorObj = {
forever: () => void, forever: () => void,
never: () => void, never: () => void,
using: <T>(handler: () => T) => T, using: <T>(handler: () => T) => T,
invalidate: <T>(handler: () => T) => T, invalidate: <T>(handler: () => T) => T,
}; };
type CacheEntry<ResultT> = Array<[ResultT, () => boolean]>; type CacheEntry<ResultT, SideChannel> = Array<
[ResultT, (SideChannel) => boolean],
>;
export type { CacheConfigurator };
/** /**
* Given a function with a single argument, cache its results based on its argument and how it * Given a function with a single argument, cache its results based on its argument and how it
* configures its caching behavior. Cached values are stored strongly. * configures its caching behavior. Cached values are stored strongly.
*/ */
export function makeStrongCache<ArgT, ResultT>( export function makeStrongCache<ArgT, ResultT, SideChannel>(
handler: (ArgT, CacheConfigurator) => ResultT, handler: (ArgT, CacheConfigurator<SideChannel>) => ResultT,
autoPermacache?: boolean, ): (ArgT, SideChannel) => ResultT {
): ArgT => ResultT { return makeCachedFunction(new Map(), handler);
return makeCachedFunction(new Map(), handler, autoPermacache);
} }
/** /**
@ -31,118 +35,170 @@ export function makeStrongCache<ArgT, ResultT>(
* configures its caching behavior. Cached values are stored weakly and the function argument must be * configures its caching behavior. Cached values are stored weakly and the function argument must be
* an object type. * an object type.
*/ */
export function makeWeakCache<ArgT: {} | Array<*> | $ReadOnlyArray<*>, ResultT>( export function makeWeakCache<
handler: (ArgT, CacheConfigurator) => ResultT, ArgT: {} | Array<*> | $ReadOnlyArray<*>,
autoPermacache?: boolean, ResultT,
): ArgT => ResultT { SideChannel,
return makeCachedFunction(new WeakMap(), handler, autoPermacache); >(
handler: (ArgT, CacheConfigurator<SideChannel>) => ResultT,
): (ArgT, SideChannel) => ResultT {
return makeCachedFunction(new WeakMap(), handler);
} }
type CacheMap<ArgT, ResultT> = type CacheMap<ArgT, ResultT, SideChannel> =
| Map<ArgT, CacheEntry<ResultT>> | Map<ArgT, CacheEntry<ResultT, SideChannel>>
| WeakMap<ArgT, CacheEntry<ResultT>>; | WeakMap<ArgT, CacheEntry<ResultT, SideChannel>>;
function makeCachedFunction<ArgT, ResultT, Cache: CacheMap<ArgT, ResultT>>( function makeCachedFunction<
ArgT,
ResultT,
SideChannel,
Cache: CacheMap<ArgT, ResultT, SideChannel>,
>(
callCache: Cache, callCache: Cache,
handler: (ArgT, CacheConfigurator) => ResultT, handler: (ArgT, CacheConfigurator<SideChannel>) => ResultT,
autoPermacache: boolean = true, ): (ArgT, SideChannel) => ResultT {
): ArgT => ResultT { return function cachedFunction(arg, data) {
return function cachedFunction(arg) { let cachedValue: CacheEntry<ResultT, SideChannel> | void = callCache.get(
let cachedValue: CacheEntry<ResultT> | void = callCache.get(arg); arg,
);
if (cachedValue) { if (cachedValue) {
for (const [value, valid] of cachedValue) { for (const [value, valid] of cachedValue) {
if (valid()) return value; if (valid(data)) return value;
} }
} }
const { cache, result, deactivate } = makeCacheConfig(); const cache = new CacheConfigurator(data);
const value = handler(arg, cache); const value = handler(arg, cache);
if (autoPermacache && !result.configured) cache.forever(); if (!cache.configured()) cache.forever();
deactivate(); cache.deactivate();
if (!result.configured) { switch (cache.mode()) {
// eslint-disable-next-line max-len case "forever":
throw new Error(
[
"Caching was left unconfigured. Babel's plugins, presets, and .babelrc.js files can be configured",
"for various types of caching, using the first param of their handler functions:",
"",
"module.exports = function(api) {",
" // The API exposes the following:",
"",
" // Cache the returned value forever and don't call this function again.",
" api.cache(true);",
"",
" // Don't cache at all. Not recommended because it will be very slow.",
" api.cache(false);",
"",
" // Cached based on the value of some function. If this function returns a value different from",
" // a previously-encountered value, the plugins will re-evaluate.",
" var env = api.cache(() => process.env.NODE_ENV);",
"",
" // If testing for a specific env, we recommend specifics to avoid instantiating a plugin for",
" // any possible NODE_ENV value that might come up during plugin execution.",
' var isProd = api.cache(() => process.env.NODE_ENV === "production");',
"",
" // .cache(fn) will perform a linear search though instances to find the matching plugin based",
" // based on previous instantiated plugins. If you want to recreate the plugin and discard the",
" // previous instance whenever something changes, you may use:",
' var isProd = api.cache.invalidate(() => process.env.NODE_ENV === "production");',
"",
" // Note, we also expose the following more-verbose versions of the above examples:",
" api.cache.forever(); // api.cache(true)",
" api.cache.never(); // api.cache(false)",
" api.cache.using(fn); // api.cache(fn)",
"",
" // Return the value that will be cached.",
" return { };",
"};",
].join("\n"),
);
}
if (!result.never) {
if (result.forever) {
cachedValue = [[value, () => true]]; cachedValue = [[value, () => true]];
} else if (result.invalidate) {
cachedValue = [[value, result.valid]];
} else {
cachedValue = cachedValue || [];
cachedValue.push([value, result.valid]);
}
callCache.set(arg, cachedValue); callCache.set(arg, cachedValue);
break;
case "invalidate":
cachedValue = [[value, cache.validator()]];
callCache.set(arg, cachedValue);
break;
case "valid":
if (cachedValue) {
cachedValue.push([value, cache.validator()]);
} else {
cachedValue = [[value, cache.validator()]];
callCache.set(arg, cachedValue);
}
} }
return value; return value;
}; };
} }
function makeCacheConfig(): { class CacheConfigurator<SideChannel = void> {
cache: CacheConfigurator, _active: boolean = true;
result: *, _never: boolean = false;
deactivate: () => void, _forever: boolean = false;
} { _invalidate: boolean = false;
const pairs = [];
const result = { _configured: boolean = false;
configured: false,
never: false,
forever: false,
invalidate: false,
valid: () => pairs.every(([key, fn]) => key === fn()),
};
let active = true; _pairs: Array<[mixed, (SideChannel) => mixed]> = [];
const deactivate = () => {
active = false;
};
const cache: CacheConfigurator = Object.assign( _data: SideChannel;
(function cacheFn(val) {
constructor(data: SideChannel) {
this._data = data;
}
simple() {
return makeSimpleConfigurator(this);
}
mode() {
if (this._never) return "never";
if (this._forever) return "forever";
if (this._invalidate) return "invalidate";
return "valid";
}
forever() {
if (!this._active) {
throw new Error("Cannot change caching after evaluation has completed.");
}
if (this._never) {
throw new Error("Caching has already been configured with .never()");
}
this._forever = true;
this._configured = true;
}
never() {
if (!this._active) {
throw new Error("Cannot change caching after evaluation has completed.");
}
if (this._forever) {
throw new Error("Caching has already been configured with .forever()");
}
this._never = true;
this._configured = true;
}
using<T>(handler: SideChannel => T): T {
if (!this._active) {
throw new Error("Cannot change caching after evaluation has completed.");
}
if (this._never || this._forever) {
throw new Error(
"Caching has already been configured with .never or .forever()",
);
}
this._configured = true;
const key = handler(this._data);
this._pairs.push([key, handler]);
return key;
}
invalidate<T>(handler: SideChannel => T): T {
if (!this._active) {
throw new Error("Cannot change caching after evaluation has completed.");
}
if (this._never || this._forever) {
throw new Error(
"Caching has already been configured with .never or .forever()",
);
}
this._invalidate = true;
this._configured = true;
const key = handler(this._data);
this._pairs.push([key, handler]);
return key;
}
validator(): SideChannel => boolean {
const pairs = this._pairs;
return (data: SideChannel) => pairs.every(([key, fn]) => key === fn(data));
}
deactivate() {
this._active = false;
}
configured() {
return this._configured;
}
}
function makeSimpleConfigurator(
cache: CacheConfigurator<any>,
): SimpleCacheConfigurator {
function cacheFn(val) {
if (typeof val === "boolean") { if (typeof val === "boolean") {
if (val) cache.forever(); if (val) cache.forever();
else cache.never(); else cache.never();
@ -150,71 +206,11 @@ function makeCacheConfig(): {
} }
return cache.using(val); return cache.using(val);
}: any),
({
forever() {
if (!active) {
throw new Error(
"Cannot change caching after evaluation has completed.",
);
} }
if (result.never) { cacheFn.forever = () => cache.forever();
throw new Error("Caching has already been configured with .never()"); cacheFn.never = () => cache.never();
} cacheFn.using = cb => cache.using(() => cb());
result.forever = true; cacheFn.invalidate = cb => cache.invalidate(() => cb());
result.configured = true;
},
never() {
if (!active) {
throw new Error(
"Cannot change caching after evaluation has completed.",
);
}
if (result.forever) {
throw new Error(
"Caching has already been configured with .forever()",
);
}
result.never = true;
result.configured = true;
},
using<T>(handler: () => T): T {
if (!active) {
throw new Error(
"Cannot change caching after evaluation has completed.",
);
}
if (result.never || result.forever) {
throw new Error(
"Caching has already been configured with .never or .forever()",
);
}
result.configured = true;
const key = handler(); return (cacheFn: any);
pairs.push([key, handler]);
return key;
},
invalidate<T>(handler: () => T): T {
if (!active) {
throw new Error(
"Cannot change caching after evaluation has completed.",
);
}
if (result.never || result.forever) {
throw new Error(
"Caching has already been configured with .never or .forever()",
);
}
result.invalidate = true;
result.configured = true;
const key = handler();
pairs.push([key, handler]);
return key;
},
}: CacheConfiguratorObj),
);
return { cache, result, deactivate };
} }

View File

@ -5,8 +5,7 @@ import path from "path";
import fs from "fs"; import fs from "fs";
import json5 from "json5"; import json5 from "json5";
import resolve from "resolve"; import resolve from "resolve";
import { getEnv } from "../../helpers/environment"; import { makeStrongCache, type CacheConfigurator } from "../../caching";
import { makeStrongCache } from "../../caching";
const debug = buildDebug("babel:config:loading:files:configuration"); const debug = buildDebug("babel:config:loading:files:configuration");
@ -21,7 +20,10 @@ const BABELRC_JS_FILENAME = ".babelrc.js";
const PACKAGE_FILENAME = "package.json"; const PACKAGE_FILENAME = "package.json";
const BABELIGNORE_FILENAME = ".babelignore"; const BABELIGNORE_FILENAME = ".babelignore";
export function findConfigs(dirname: string): Array<ConfigFile> { export function findConfigs(
dirname: string,
envName: string,
): Array<ConfigFile> {
let foundConfig = false; let foundConfig = false;
let foundIgnore = false; let foundIgnore = false;
@ -47,7 +49,7 @@ export function findConfigs(dirname: string): Array<ConfigFile> {
PACKAGE_FILENAME, PACKAGE_FILENAME,
].reduce((previousConfig: ConfigFile | null, name) => { ].reduce((previousConfig: ConfigFile | null, name) => {
const filepath = path.join(loc, name); const filepath = path.join(loc, name);
const config = readConfig(filepath); const config = readConfig(filepath, envName);
if (config && previousConfig) { if (config && previousConfig) {
throw new Error( throw new Error(
@ -77,10 +79,14 @@ export function findConfigs(dirname: string): Array<ConfigFile> {
return confs; return confs;
} }
export function loadConfig(name: string, dirname: string): ConfigFile { export function loadConfig(
name: string,
dirname: string,
envName: string,
): ConfigFile {
const filepath = resolve.sync(name, { basedir: dirname }); const filepath = resolve.sync(name, { basedir: dirname });
const conf = readConfig(filepath); const conf = readConfig(filepath, envName);
if (!conf) { if (!conf) {
throw new Error(`Config file ${filepath} contains no configuration data`); throw new Error(`Config file ${filepath} contains no configuration data`);
} }
@ -93,14 +99,16 @@ export function loadConfig(name: string, dirname: string): ConfigFile {
* Read the given config file, returning the result. Returns null if no config was found, but will * Read the given config file, returning the result. Returns null if no config was found, but will
* throw if there are parsing errors while loading a config. * throw if there are parsing errors while loading a config.
*/ */
function readConfig(filepath) { function readConfig(filepath, envName) {
return path.extname(filepath) === ".js" return path.extname(filepath) === ".js"
? readConfigJS(filepath) ? readConfigJS(filepath, { envName })
: readConfigFile(filepath); : readConfigFile(filepath);
} }
const LOADING_CONFIGS = new Set(); const LOADING_CONFIGS = new Set();
const readConfigJS = makeStrongCache((filepath, cache) => {
const readConfigJS = makeStrongCache(
(filepath, cache: CacheConfigurator<{ envName: string }>) => {
if (!fs.existsSync(filepath)) { if (!fs.existsSync(filepath)) {
cache.forever(); cache.forever();
return null; return null;
@ -139,13 +147,13 @@ const readConfigJS = makeStrongCache((filepath, cache) => {
if (typeof options === "function") { if (typeof options === "function") {
options = options({ options = options({
cache, cache: cache.simple(),
// Expose ".env()" so people can easily get the same env that we expose using the "env" key. // Expose ".env()" so people can easily get the same env that we expose using the "env" key.
env: () => cache.using(() => getEnv()), env: () => cache.using(data => data.envName),
async: () => false, async: () => false,
}); });
} else {
cache.forever(); if (!cache.configured()) throwConfigError();
} }
if (!options || typeof options !== "object" || Array.isArray(options)) { if (!options || typeof options !== "object" || Array.isArray(options)) {
@ -169,7 +177,8 @@ const readConfigJS = makeStrongCache((filepath, cache) => {
dirname: path.dirname(filepath), dirname: path.dirname(filepath),
options, options,
}; };
}, false /* autoPermacache */); },
);
const readConfigFile = makeStaticFileCache((filepath, content) => { const readConfigFile = makeStaticFileCache((filepath, content) => {
let options; let options;
@ -239,3 +248,40 @@ function fileMtime(filepath: string): number | null {
return null; return null;
} }
function throwConfigError() {
throw new Error(`\
Caching was left unconfigured. Babel's plugins, presets, and .babelrc.js files can be configured
for various types of caching, using the first param of their handler functions:
module.exports = function(api) {
// The API exposes the following:
// Cache the returned value forever and don't call this function again.
api.cache(true);
// Don't cache at all. Not recommended because it will be very slow.
api.cache(false);
// Cached based on the value of some function. If this function returns a value different from
// a previously-encountered value, the plugins will re-evaluate.
var env = api.cache(() => process.env.NODE_ENV);
// If testing for a specific env, we recommend specifics to avoid instantiating a plugin for
// any possible NODE_ENV value that might come up during plugin execution.
var isProd = api.cache(() => process.env.NODE_ENV === "production");
// .cache(fn) will perform a linear search though instances to find the matching plugin based
// based on previous instantiated plugins. If you want to recreate the plugin and discard the
// previous instance whenever something changes, you may use:
var isProd = api.cache.invalidate(() => process.env.NODE_ENV === "production");
// Note, we also expose the following more-verbose versions of the above examples:
api.cache.forever(); // api.cache(true)
api.cache.never(); // api.cache(false)
api.cache.using(fn); // api.cache(fn)
// Return the value that will be cached.
return { };
};`);
}

View File

@ -6,7 +6,7 @@ import merge from "lodash/merge";
import buildConfigChain, { type ConfigItem } from "./build-config-chain"; import buildConfigChain, { type ConfigItem } from "./build-config-chain";
import traverse from "@babel/traverse"; import traverse from "@babel/traverse";
import clone from "lodash/clone"; import clone from "lodash/clone";
import { makeWeakCache } from "./caching"; import { makeWeakCache, type CacheConfigurator } from "./caching";
import { getEnv } from "./helpers/environment"; import { getEnv } from "./helpers/environment";
import { validate, type ValidatedOptions, type PluginItem } from "./options"; import { validate, type ValidatedOptions, type PluginItem } from "./options";
@ -46,14 +46,14 @@ class OptionManager {
* - `dirname` is used to resolve plugins relative to it. * - `dirname` is used to resolve plugins relative to it.
*/ */
mergeOptions(config: MergeOptions, pass?: Array<Plugin>) { mergeOptions(config: MergeOptions, pass?: Array<Plugin>, envName: string) {
const result = loadConfig(config); const result = loadConfig(config);
const plugins = result.plugins.map(descriptor => const plugins = result.plugins.map(descriptor =>
loadPluginDescriptor(descriptor), loadPluginDescriptor(descriptor, envName),
); );
const presets = result.presets.map(descriptor => const presets = result.presets.map(descriptor =>
loadPresetDescriptor(descriptor), loadPresetDescriptor(descriptor, envName),
); );
const passPerPreset = config.options.passPerPreset; const passPerPreset = config.options.passPerPreset;
@ -70,7 +70,11 @@ class OptionManager {
} }
presets.forEach((presetConfig, i) => { presets.forEach((presetConfig, i) => {
this.mergeOptions(presetConfig, presetPasses ? presetPasses[i] : pass); this.mergeOptions(
presetConfig,
presetPasses ? presetPasses[i] : pass,
envName,
);
}); });
} }
@ -99,12 +103,14 @@ class OptionManager {
init(inputOpts: {}) { init(inputOpts: {}) {
const args = validate("arguments", inputOpts); const args = validate("arguments", inputOpts);
const configChain = buildConfigChain(args); const { envName = getEnv() } = args;
const configChain = buildConfigChain(args, envName);
if (!configChain) return null; if (!configChain) return null;
try { try {
for (const config of configChain) { for (const config of configChain) {
this.mergeOptions(config); this.mergeOptions(config, undefined, envName);
} }
} catch (e) { } catch (e) {
// There are a few case where thrown errors will try to annotate themselves multiple times, so // There are a few case where thrown errors will try to annotate themselves multiple times, so
@ -127,6 +133,7 @@ class OptionManager {
.filter(plugins => plugins.length > 0) .filter(plugins => plugins.length > 0)
.map(plugins => ({ plugins })); .map(plugins => ({ plugins }));
opts.passPerPreset = opts.presets.length > 0; opts.passPerPreset = opts.presets.length > 0;
opts.envName = envName;
return { return {
options: opts, options: opts,
@ -182,13 +189,13 @@ const loadConfig = makeWeakCache((config: MergeOptions): {
const loadDescriptor = makeWeakCache( const loadDescriptor = makeWeakCache(
( (
{ value, options = {}, dirname, alias }: BasicDescriptor, { value, options = {}, dirname, alias }: BasicDescriptor,
cache, cache: CacheConfigurator<{ envName: string }>,
): LoadedDescriptor => { ): LoadedDescriptor => {
let item = value; let item = value;
if (typeof value === "function") { if (typeof value === "function") {
const api = Object.assign(Object.create(context), { const api = Object.assign(Object.create(context), {
cache, cache: cache.simple(),
env: () => cache.using(() => getEnv()), env: () => cache.using(data => data.envName),
async: () => false, async: () => false,
}); });
@ -222,7 +229,10 @@ const loadDescriptor = makeWeakCache(
/** /**
* Instantiate a plugin for the given descriptor, returning the plugin/options pair. * Instantiate a plugin for the given descriptor, returning the plugin/options pair.
*/ */
function loadPluginDescriptor(descriptor: BasicDescriptor): Plugin { function loadPluginDescriptor(
descriptor: BasicDescriptor,
envName: string,
): Plugin {
if (descriptor.value instanceof Plugin) { if (descriptor.value instanceof Plugin) {
if (descriptor.options) { if (descriptor.options) {
throw new Error( throw new Error(
@ -233,11 +243,16 @@ function loadPluginDescriptor(descriptor: BasicDescriptor): Plugin {
return descriptor.value; return descriptor.value;
} }
return instantiatePlugin(loadDescriptor(descriptor)); return instantiatePlugin(loadDescriptor(descriptor, { envName }), {
envName,
});
} }
const instantiatePlugin = makeWeakCache( const instantiatePlugin = makeWeakCache(
({ value, options, dirname, alias }: LoadedDescriptor, cache): Plugin => { (
{ value, options, dirname, alias }: LoadedDescriptor,
cache: CacheConfigurator<{ envName: string }>,
): Plugin => {
const pluginObj = validatePluginObject(value); const pluginObj = validatePluginObject(value);
const plugin = Object.assign({}, pluginObj); const plugin = Object.assign({}, pluginObj);
@ -254,8 +269,8 @@ const instantiatePlugin = makeWeakCache(
}; };
// If the inherited plugin changes, reinstantiate this plugin. // If the inherited plugin changes, reinstantiate this plugin.
const inherits = cache.invalidate(() => const inherits = cache.invalidate(data =>
loadPluginDescriptor(inheritsDescriptor), loadPluginDescriptor(inheritsDescriptor, data.envName),
); );
plugin.pre = chain(inherits.pre, plugin.pre); plugin.pre = chain(inherits.pre, plugin.pre);
@ -277,8 +292,11 @@ const instantiatePlugin = makeWeakCache(
/** /**
* Generate a config object that will act as the root of a new nested config. * Generate a config object that will act as the root of a new nested config.
*/ */
const loadPresetDescriptor = (descriptor: BasicDescriptor): MergeOptions => { const loadPresetDescriptor = (
return instantiatePreset(loadDescriptor(descriptor)); descriptor: BasicDescriptor,
envName: string,
): MergeOptions => {
return instantiatePreset(loadDescriptor(descriptor, { envName }));
}; };
const instantiatePreset = makeWeakCache( const instantiatePreset = makeWeakCache(

View File

@ -28,6 +28,10 @@ const ROOT_VALIDATORS: ValidatorSet = {
>), >),
code: (assertBoolean: Validator<$PropertyType<ValidatedOptions, "code">>), code: (assertBoolean: Validator<$PropertyType<ValidatedOptions, "code">>),
ast: (assertBoolean: Validator<$PropertyType<ValidatedOptions, "ast">>), ast: (assertBoolean: Validator<$PropertyType<ValidatedOptions, "ast">>),
envName: (assertString: Validator<
$PropertyType<ValidatedOptions, "envName">,
>),
}; };
const NONPRESET_VALIDATORS: ValidatorSet = { const NONPRESET_VALIDATORS: ValidatorSet = {
@ -130,6 +134,7 @@ export type ValidatedOptions = {
code?: boolean, code?: boolean,
ast?: boolean, ast?: boolean,
inputSourceMap?: RootInputSourceMapOption, inputSourceMap?: RootInputSourceMapOption,
envName?: string,
extends?: string, extends?: string,
env?: EnvSet<ValidatedOptions>, env?: EnvSet<ValidatedOptions>,

View File

@ -19,23 +19,6 @@ describe("caching API", () => {
assert.notEqual(fn("one"), fn("two")); assert.notEqual(fn("one"), fn("two"));
}); });
it("should allow permacaching with cache(true)", () => {
let count = 0;
const fn = makeStrongCache((arg, cache) => {
cache(true);
return { arg, count: count++ };
});
assert.deepEqual(fn("one"), { arg: "one", count: 0 });
assert.equal(fn("one"), fn("one"));
assert.deepEqual(fn("two"), { arg: "two", count: 1 });
assert.equal(fn("two"), fn("two"));
assert.notEqual(fn("one"), fn("two"));
});
it("should allow disabling caching with .never()", () => { it("should allow disabling caching with .never()", () => {
let count = 0; let count = 0;
@ -55,25 +38,6 @@ describe("caching API", () => {
assert.notEqual(fn("one"), fn("two")); assert.notEqual(fn("one"), fn("two"));
}); });
it("should allow disabling caching with cache(false)", () => {
let count = 0;
const fn = makeStrongCache((arg, cache) => {
cache(false);
return { arg, count: count++ };
});
assert.deepEqual(fn("one"), { arg: "one", count: 0 });
assert.deepEqual(fn("one"), { arg: "one", count: 1 });
assert.notEqual(fn("one"), fn("one"));
assert.deepEqual(fn("two"), { arg: "two", count: 4 });
assert.deepEqual(fn("two"), { arg: "two", count: 5 });
assert.notEqual(fn("two"), fn("two"));
assert.notEqual(fn("one"), fn("two"));
});
it("should allow caching based on a value with .using(fn)", () => { it("should allow caching based on a value with .using(fn)", () => {
let count = 0; let count = 0;
let other = "default"; let other = "default";
@ -115,47 +79,6 @@ describe("caching API", () => {
assert.equal(fn("two"), fn("two")); assert.equal(fn("two"), fn("two"));
}); });
it("should allow caching based on a value with cache(fn)", () => {
let count = 0;
let other = "default";
const fn = makeStrongCache((arg, cache) => {
const val = cache(() => other);
return { arg, val, count: count++ };
});
assert.deepEqual(fn("one"), { arg: "one", val: "default", count: 0 });
assert.equal(fn("one"), fn("one"));
assert.deepEqual(fn("two"), { arg: "two", val: "default", count: 1 });
assert.equal(fn("two"), fn("two"));
other = "new";
assert.deepEqual(fn("one"), { arg: "one", val: "new", count: 2 });
assert.equal(fn("one"), fn("one"));
assert.deepEqual(fn("two"), { arg: "two", val: "new", count: 3 });
assert.equal(fn("two"), fn("two"));
other = "default";
assert.deepEqual(fn("one"), { arg: "one", val: "default", count: 0 });
assert.equal(fn("one"), fn("one"));
assert.deepEqual(fn("two"), { arg: "two", val: "default", count: 1 });
assert.equal(fn("two"), fn("two"));
other = "new";
assert.deepEqual(fn("one"), { arg: "one", val: "new", count: 2 });
assert.equal(fn("one"), fn("one"));
assert.deepEqual(fn("two"), { arg: "two", val: "new", count: 3 });
assert.equal(fn("two"), fn("two"));
});
it("should allow invalidation based on a value with .invalidate(fn)", () => { it("should allow invalidation based on a value with .invalidate(fn)", () => {
let count = 0; let count = 0;
let other = "default"; let other = "default";
@ -298,12 +221,6 @@ describe("caching API", () => {
assert.equal(fn("two"), fn("two")); assert.equal(fn("two"), fn("two"));
}); });
it("should throw if caching is never configured and not defaulting", () => {
const fn = makeStrongCache(() => {}, false /* autoPermacache */);
assert.throws(() => fn(), /Error: Caching was left unconfigured./);
});
it("should auto-permacache by default", () => { it("should auto-permacache by default", () => {
let count = 0; let count = 0;
@ -410,4 +327,89 @@ describe("caching API", () => {
/Cannot change caching after evaluation/, /Cannot change caching after evaluation/,
); );
}); });
describe("simple", () => {
it("should allow permacaching with cache(true)", () => {
let count = 0;
const fn = makeStrongCache((arg, cache) => {
cache = cache.simple();
cache(true);
return { arg, count: count++ };
});
assert.deepEqual(fn("one"), { arg: "one", count: 0 });
assert.equal(fn("one"), fn("one"));
assert.deepEqual(fn("two"), { arg: "two", count: 1 });
assert.equal(fn("two"), fn("two"));
assert.notEqual(fn("one"), fn("two"));
});
it("should allow disabling caching with cache(false)", () => {
let count = 0;
const fn = makeStrongCache((arg, cache) => {
cache = cache.simple();
cache(false);
return { arg, count: count++ };
});
assert.deepEqual(fn("one"), { arg: "one", count: 0 });
assert.deepEqual(fn("one"), { arg: "one", count: 1 });
assert.notEqual(fn("one"), fn("one"));
assert.deepEqual(fn("two"), { arg: "two", count: 4 });
assert.deepEqual(fn("two"), { arg: "two", count: 5 });
assert.notEqual(fn("two"), fn("two"));
assert.notEqual(fn("one"), fn("two"));
});
it("should allow caching based on a value with cache(fn)", () => {
let count = 0;
let other = "default";
const fn = makeStrongCache((arg, cache) => {
cache = cache.simple();
const val = cache(() => other);
return { arg, val, count: count++ };
});
assert.deepEqual(fn("one"), { arg: "one", val: "default", count: 0 });
assert.equal(fn("one"), fn("one"));
assert.deepEqual(fn("two"), { arg: "two", val: "default", count: 1 });
assert.equal(fn("two"), fn("two"));
other = "new";
assert.deepEqual(fn("one"), { arg: "one", val: "new", count: 2 });
assert.equal(fn("one"), fn("one"));
assert.deepEqual(fn("two"), { arg: "two", val: "new", count: 3 });
assert.equal(fn("two"), fn("two"));
other = "default";
assert.deepEqual(fn("one"), { arg: "one", val: "default", count: 0 });
assert.equal(fn("one"), fn("one"));
assert.deepEqual(fn("two"), { arg: "two", val: "default", count: 1 });
assert.equal(fn("two"), fn("two"));
other = "new";
assert.deepEqual(fn("one"), { arg: "one", val: "new", count: 2 });
assert.equal(fn("one"), fn("one"));
assert.deepEqual(fn("two"), { arg: "two", val: "new", count: 3 });
assert.equal(fn("two"), fn("two"));
});
});
}); });

View File

@ -3,6 +3,8 @@ import fs from "fs";
import path from "path"; import path from "path";
import buildConfigChain from "../lib/config/build-config-chain"; import buildConfigChain from "../lib/config/build-config-chain";
const DEFAULT_ENV = "development";
function fixture() { function fixture() {
const args = [__dirname, "fixtures", "config"]; const args = [__dirname, "fixtures", "config"];
for (let i = 0; i < arguments.length; i++) { for (let i = 0; i < arguments.length; i++) {
@ -16,25 +18,10 @@ function base() {
} }
describe("buildConfigChain", function() { describe("buildConfigChain", function() {
let oldBabelEnv;
let oldNodeEnv;
beforeEach(function() {
oldBabelEnv = process.env.BABEL_ENV;
oldNodeEnv = process.env.NODE_ENV;
delete process.env.BABEL_ENV;
delete process.env.NODE_ENV;
});
afterEach(function() {
process.env.BABEL_ENV = oldBabelEnv;
process.env.NODE_ENV = oldNodeEnv;
});
describe("ignore", () => { describe("ignore", () => {
it("should ignore files that match", () => { it("should ignore files that match", () => {
const chain = buildConfigChain({ const chain = buildConfigChain(
{
filename: fixture("nonexistant-fake", "src.js"), filename: fixture("nonexistant-fake", "src.js"),
babelrc: false, babelrc: false,
ignore: [ ignore: [
@ -45,20 +32,25 @@ describe("buildConfigChain", function() {
fixture("nonexistant-fake", "other.js"), fixture("nonexistant-fake", "other.js"),
fixture("nonexistant-fake", "misc.js"), fixture("nonexistant-fake", "misc.js"),
], ],
}); },
DEFAULT_ENV,
);
assert.equal(chain, null); assert.equal(chain, null);
}); });
it("should not ignore files that don't match", () => { it("should not ignore files that don't match", () => {
const chain = buildConfigChain({ const chain = buildConfigChain(
{
filename: fixture("nonexistant-fake", "src.js"), filename: fixture("nonexistant-fake", "src.js"),
babelrc: false, babelrc: false,
ignore: [ ignore: [
fixture("nonexistant-fake", "other.js"), fixture("nonexistant-fake", "other.js"),
fixture("nonexistant-fake", "misc.js"), fixture("nonexistant-fake", "misc.js"),
], ],
}); },
DEFAULT_ENV,
);
const expected = [ const expected = [
{ {
@ -82,27 +74,33 @@ describe("buildConfigChain", function() {
describe("only", () => { describe("only", () => {
it("should ignore files that don't match", () => { it("should ignore files that don't match", () => {
const chain = buildConfigChain({ const chain = buildConfigChain(
{
filename: fixture("nonexistant-fake", "src.js"), filename: fixture("nonexistant-fake", "src.js"),
babelrc: false, babelrc: false,
only: [ only: [
fixture("nonexistant-fake", "other.js"), fixture("nonexistant-fake", "other.js"),
fixture("nonexistant-fake", "misc.js"), fixture("nonexistant-fake", "misc.js"),
], ],
}); },
DEFAULT_ENV,
);
assert.equal(chain, null); assert.equal(chain, null);
}); });
it("should not ignore files that match", () => { it("should not ignore files that match", () => {
const chain = buildConfigChain({ const chain = buildConfigChain(
{
filename: fixture("nonexistant-fake", "src.js"), filename: fixture("nonexistant-fake", "src.js"),
babelrc: false, babelrc: false,
only: [ only: [
fixture("nonexistant-fake", "src.js"), fixture("nonexistant-fake", "src.js"),
fixture("nonexistant-fake", "misc.js"), fixture("nonexistant-fake", "misc.js"),
], ],
}); },
DEFAULT_ENV,
);
const expected = [ const expected = [
{ {
@ -126,12 +124,15 @@ describe("buildConfigChain", function() {
describe("ignore/only", () => { describe("ignore/only", () => {
it("should ignore files that match ignore and don't match only", () => { it("should ignore files that match ignore and don't match only", () => {
const chain = buildConfigChain({ const chain = buildConfigChain(
{
filename: fixture("nonexistant-fake", "src.js"), filename: fixture("nonexistant-fake", "src.js"),
babelrc: false, babelrc: false,
ignore: [fixture("nonexistant-fake", "src.js")], ignore: [fixture("nonexistant-fake", "src.js")],
only: [fixture("nonexistant-fake", "src.js")], only: [fixture("nonexistant-fake", "src.js")],
}); },
DEFAULT_ENV,
);
assert.equal(chain, null); assert.equal(chain, null);
}); });
@ -142,8 +143,8 @@ describe("buildConfigChain", function() {
it("should not cache the input options by identity", () => { it("should not cache the input options by identity", () => {
const comments = false; const comments = false;
const chain1 = buildConfigChain({ comments }); const chain1 = buildConfigChain({ comments }, DEFAULT_ENV);
const chain2 = buildConfigChain({ comments }); const chain2 = buildConfigChain({ comments }, DEFAULT_ENV);
assert.equal(chain1.length, 1); assert.equal(chain1.length, 1);
assert.equal(chain2.length, 1); assert.equal(chain2.length, 1);
@ -151,15 +152,14 @@ describe("buildConfigChain", function() {
}); });
it("should cache the env options by identity", () => { it("should cache the env options by identity", () => {
process.env.NODE_ENV = "foo";
const env = { const env = {
foo: { foo: {
comments: false, comments: false,
}, },
}; };
const chain1 = buildConfigChain({ env }); const chain1 = buildConfigChain({ env }, "foo");
const chain2 = buildConfigChain({ env }); const chain2 = buildConfigChain({ env }, "foo");
assert.equal(chain1.length, 2); assert.equal(chain1.length, 2);
assert.equal(chain2.length, 2); assert.equal(chain2.length, 2);
@ -170,8 +170,8 @@ describe("buildConfigChain", function() {
it("should cache the plugin options by identity", () => { it("should cache the plugin options by identity", () => {
const plugins = []; const plugins = [];
const chain1 = buildConfigChain({ plugins }); const chain1 = buildConfigChain({ plugins }, DEFAULT_ENV);
const chain2 = buildConfigChain({ plugins }); const chain2 = buildConfigChain({ plugins }, DEFAULT_ENV);
assert.equal(chain1.length, 1); assert.equal(chain1.length, 1);
assert.equal(chain2.length, 1); assert.equal(chain2.length, 1);
@ -181,8 +181,8 @@ describe("buildConfigChain", function() {
it("should cache the presets options by identity", () => { it("should cache the presets options by identity", () => {
const presets = []; const presets = [];
const chain1 = buildConfigChain({ presets }); const chain1 = buildConfigChain({ presets }, DEFAULT_ENV);
const chain2 = buildConfigChain({ presets }); const chain2 = buildConfigChain({ presets }, DEFAULT_ENV);
assert.equal(chain1.length, 1); assert.equal(chain1.length, 1);
assert.equal(chain2.length, 1); assert.equal(chain2.length, 1);
@ -192,9 +192,15 @@ describe("buildConfigChain", function() {
it("should not cache the presets options with passPerPreset", () => { it("should not cache the presets options with passPerPreset", () => {
const presets = []; const presets = [];
const chain1 = buildConfigChain({ presets }); const chain1 = buildConfigChain({ presets }, DEFAULT_ENV);
const chain2 = buildConfigChain({ presets, passPerPreset: true }); const chain2 = buildConfigChain(
const chain3 = buildConfigChain({ presets, passPerPreset: false }); { presets, passPerPreset: true },
DEFAULT_ENV,
);
const chain3 = buildConfigChain(
{ presets, passPerPreset: false },
DEFAULT_ENV,
);
assert.equal(chain1.length, 1); assert.equal(chain1.length, 1);
assert.equal(chain2.length, 1); assert.equal(chain2.length, 1);
@ -229,13 +235,13 @@ describe("buildConfigChain", function() {
"package.json", "package.json",
); );
const chain1 = buildConfigChain({ filename }); const chain1 = buildConfigChain({ filename }, DEFAULT_ENV);
const chain2 = buildConfigChain({ filename }); const chain2 = buildConfigChain({ filename }, DEFAULT_ENV);
touch(pkgJSON); touch(pkgJSON);
const chain3 = buildConfigChain({ filename }); const chain3 = buildConfigChain({ filename }, DEFAULT_ENV);
const chain4 = buildConfigChain({ filename }); const chain4 = buildConfigChain({ filename }, DEFAULT_ENV);
assert.equal(chain1.length, 3); assert.equal(chain1.length, 3);
assert.equal(chain2.length, 3); assert.equal(chain2.length, 3);
@ -266,13 +272,13 @@ describe("buildConfigChain", function() {
".babelrc", ".babelrc",
); );
const chain1 = buildConfigChain({ filename }); const chain1 = buildConfigChain({ filename }, DEFAULT_ENV);
const chain2 = buildConfigChain({ filename }); const chain2 = buildConfigChain({ filename }, DEFAULT_ENV);
touch(babelrcFile); touch(babelrcFile);
const chain3 = buildConfigChain({ filename }); const chain3 = buildConfigChain({ filename }, DEFAULT_ENV);
const chain4 = buildConfigChain({ filename }); const chain4 = buildConfigChain({ filename }, DEFAULT_ENV);
assert.equal(chain1.length, 3); assert.equal(chain1.length, 3);
assert.equal(chain2.length, 3); assert.equal(chain2.length, 3);
@ -303,13 +309,13 @@ describe("buildConfigChain", function() {
".babelignore", ".babelignore",
); );
const chain1 = buildConfigChain({ filename }); const chain1 = buildConfigChain({ filename }, DEFAULT_ENV);
const chain2 = buildConfigChain({ filename }); const chain2 = buildConfigChain({ filename }, DEFAULT_ENV);
touch(babelignoreFile); touch(babelignoreFile);
const chain3 = buildConfigChain({ filename }); const chain3 = buildConfigChain({ filename }, DEFAULT_ENV);
const chain4 = buildConfigChain({ filename }); const chain4 = buildConfigChain({ filename }, DEFAULT_ENV);
assert.equal(chain1.length, 6); assert.equal(chain1.length, 6);
assert.equal(chain2.length, 6); assert.equal(chain2.length, 6);
@ -340,13 +346,11 @@ describe("buildConfigChain", function() {
".babelrc.js", ".babelrc.js",
); );
const chain1 = buildConfigChain({ filename }); const chain1 = buildConfigChain({ filename }, DEFAULT_ENV);
const chain2 = buildConfigChain({ filename }); const chain2 = buildConfigChain({ filename }, DEFAULT_ENV);
process.env.NODE_ENV = "new-env"; const chain3 = buildConfigChain({ filename }, "new-env");
const chain4 = buildConfigChain({ filename }, "new-env");
const chain3 = buildConfigChain({ filename });
const chain4 = buildConfigChain({ filename });
assert.equal(chain1.length, 3); assert.equal(chain1.length, 3);
assert.equal(chain2.length, 3); assert.equal(chain2.length, 3);
@ -358,7 +362,7 @@ describe("buildConfigChain", function() {
assert.equal(chain4[1].alias, babelrcFile); assert.equal(chain4[1].alias, babelrcFile);
assert.strictEqual(chain1[1], chain2[1]); assert.strictEqual(chain1[1], chain2[1]);
// Identity changed after changing the NODE_ENV. // Identity changed after changing the envName.
assert.notStrictEqual(chain3[1], chain1[1]); assert.notStrictEqual(chain3[1], chain1[1]);
assert.strictEqual(chain3[1], chain4[1]); assert.strictEqual(chain3[1], chain4[1]);
}); });
@ -366,9 +370,12 @@ describe("buildConfigChain", function() {
}); });
it("dir1", function() { it("dir1", function() {
const chain = buildConfigChain({ const chain = buildConfigChain(
{
filename: fixture("dir1", "src.js"), filename: fixture("dir1", "src.js"),
}); },
DEFAULT_ENV,
);
const expected = [ const expected = [
{ {
@ -410,9 +417,12 @@ describe("buildConfigChain", function() {
}); });
it("dir2", function() { it("dir2", function() {
const chain = buildConfigChain({ const chain = buildConfigChain(
{
filename: fixture("dir2", "src.js"), filename: fixture("dir2", "src.js"),
}); },
DEFAULT_ENV,
);
const expected = [ const expected = [
{ {
@ -445,9 +455,12 @@ describe("buildConfigChain", function() {
}); });
it("dir3", function() { it("dir3", function() {
const chain = buildConfigChain({ const chain = buildConfigChain(
{
filename: fixture("dir3", "src.js"), filename: fixture("dir3", "src.js"),
}); },
DEFAULT_ENV,
);
const expected = [ const expected = [
{ {
@ -489,9 +502,12 @@ describe("buildConfigChain", function() {
}); });
it("env - base", function() { it("env - base", function() {
const chain = buildConfigChain({ const chain = buildConfigChain(
{
filename: fixture("env", "src.js"), filename: fixture("env", "src.js"),
}); },
DEFAULT_ENV,
);
const expected = [ const expected = [
{ {
@ -532,11 +548,12 @@ describe("buildConfigChain", function() {
}); });
it("env - foo", function() { it("env - foo", function() {
process.env.NODE_ENV = "foo"; const chain = buildConfigChain(
{
const chain = buildConfigChain({
filename: fixture("env", "src.js"), filename: fixture("env", "src.js"),
}); },
"foo",
);
const expected = [ const expected = [
{ {
@ -585,12 +602,12 @@ describe("buildConfigChain", function() {
}); });
it("env - bar", function() { it("env - bar", function() {
process.env.NODE_ENV = "foo"; // overridden const chain = buildConfigChain(
process.env.NODE_ENV = "bar"; {
const chain = buildConfigChain({
filename: fixture("env", "src.js"), filename: fixture("env", "src.js"),
}); },
"bar",
);
const expected = [ const expected = [
{ {
@ -639,11 +656,12 @@ describe("buildConfigChain", function() {
}); });
it("env - foo", function() { it("env - foo", function() {
process.env.NODE_ENV = "foo"; const chain = buildConfigChain(
{
const chain = buildConfigChain({
filename: fixture("pkg", "src.js"), filename: fixture("pkg", "src.js"),
}); },
"foo",
);
const expected = [ const expected = [
{ {
@ -676,9 +694,12 @@ describe("buildConfigChain", function() {
}); });
it("js-config", function() { it("js-config", function() {
const chain = buildConfigChain({ const chain = buildConfigChain(
{
filename: fixture("js-config", "src.js"), filename: fixture("js-config", "src.js"),
}); },
DEFAULT_ENV,
);
const expected = [ const expected = [
{ {
@ -711,9 +732,12 @@ describe("buildConfigChain", function() {
}); });
it("js-config-function", function() { it("js-config-function", function() {
const chain = buildConfigChain({ const chain = buildConfigChain(
{
filename: fixture("js-config-function", "src.js"), filename: fixture("js-config-function", "src.js"),
}); },
DEFAULT_ENV,
);
const expected = [ const expected = [
{ {
@ -746,9 +770,12 @@ describe("buildConfigChain", function() {
}); });
it("js-config-default - should read transpiled export default", function() { it("js-config-default - should read transpiled export default", function() {
const chain = buildConfigChain({ const chain = buildConfigChain(
{
filename: fixture("js-config-default", "src.js"), filename: fixture("js-config-default", "src.js"),
}); },
DEFAULT_ENV,
);
const expected = [ const expected = [
{ {
@ -780,9 +807,12 @@ describe("buildConfigChain", function() {
assert.deepEqual(chain, expected); assert.deepEqual(chain, expected);
}); });
it("js-config-extended", function() { it("js-config-extended", function() {
const chain = buildConfigChain({ const chain = buildConfigChain(
{
filename: fixture("js-config-extended", "src.js"), filename: fixture("js-config-extended", "src.js"),
}); },
DEFAULT_ENV,
);
const expected = [ const expected = [
{ {
@ -827,9 +857,12 @@ describe("buildConfigChain", function() {
"json-pkg-config-no-babel - should not throw if" + "json-pkg-config-no-babel - should not throw if" +
" package.json doesn't contain a `babel` field", " package.json doesn't contain a `babel` field",
function() { function() {
const chain = buildConfigChain({ const chain = buildConfigChain(
{
filename: fixture("json-pkg-config-no-babel", "src.js"), filename: fixture("json-pkg-config-no-babel", "src.js"),
}); },
DEFAULT_ENV,
);
const expected = [ const expected = [
{ {
@ -863,9 +896,12 @@ describe("buildConfigChain", function() {
); );
it("should not ignore file matching negated file pattern", function() { it("should not ignore file matching negated file pattern", function() {
const chain = buildConfigChain({ const chain = buildConfigChain(
{
filename: fixture("ignore-negate", "src.js"), filename: fixture("ignore-negate", "src.js"),
}); },
DEFAULT_ENV,
);
const expected = [ const expected = [
{ {
@ -896,17 +932,23 @@ describe("buildConfigChain", function() {
assert.deepEqual(chain, expected); assert.deepEqual(chain, expected);
const chain2 = buildConfigChain({ const chain2 = buildConfigChain(
{
filename: fixture("ignore-negate", "src2.js"), filename: fixture("ignore-negate", "src2.js"),
}); },
DEFAULT_ENV,
);
assert.equal(chain2, null); assert.equal(chain2, null);
}); });
it("should not ignore file matching negated folder pattern", function() { it("should not ignore file matching negated folder pattern", function() {
const chain = buildConfigChain({ const chain = buildConfigChain(
{
filename: fixture("ignore-negate-folder", "folder", "src.js"), filename: fixture("ignore-negate-folder", "folder", "src.js"),
}); },
DEFAULT_ENV,
);
const expected = [ const expected = [
{ {
@ -937,9 +979,12 @@ describe("buildConfigChain", function() {
assert.deepEqual(chain, expected); assert.deepEqual(chain, expected);
const chain2 = buildConfigChain({ const chain2 = buildConfigChain(
{
filename: fixture("ignore-negate-folder", "src2.js"), filename: fixture("ignore-negate-folder", "src2.js"),
}); },
DEFAULT_ENV,
);
assert.equal(chain2, null); assert.equal(chain2, null);
}); });
@ -949,9 +994,12 @@ describe("buildConfigChain", function() {
" and a .babelrc.js are present", " and a .babelrc.js are present",
function() { function() {
assert.throws(function() { assert.throws(function() {
buildConfigChain({ buildConfigChain(
{
filename: fixture("js-json-config", "src.js"), filename: fixture("js-json-config", "src.js"),
}); },
DEFAULT_ENV,
);
}, /Multiple configuration files found\.(.|\n)*\.babelrc(.|\n)*\.babelrc\.js/); }, /Multiple configuration files found\.(.|\n)*\.babelrc(.|\n)*\.babelrc\.js/);
}, },
); );
@ -961,9 +1009,12 @@ describe("buildConfigChain", function() {
" and a package.json with a babel field are present", " and a package.json with a babel field are present",
function() { function() {
assert.throws(function() { assert.throws(function() {
buildConfigChain({ buildConfigChain(
{
filename: fixture("js-pkg-config", "src.js"), filename: fixture("js-pkg-config", "src.js"),
}); },
DEFAULT_ENV,
);
}, /Multiple configuration files found\.(.|\n)*\.babelrc\.js(.|\n)*package\.json/); }, /Multiple configuration files found\.(.|\n)*\.babelrc\.js(.|\n)*package\.json/);
}, },
); );
@ -973,42 +1024,57 @@ describe("buildConfigChain", function() {
" and a package.json with a babel field are present", " and a package.json with a babel field are present",
function() { function() {
assert.throws(function() { assert.throws(function() {
buildConfigChain({ buildConfigChain(
{
filename: fixture("json-pkg-config", "src.js"), filename: fixture("json-pkg-config", "src.js"),
}); },
DEFAULT_ENV,
);
}, /Multiple configuration files found\.(.|\n)*\.babelrc(.|\n)*package\.json/); }, /Multiple configuration files found\.(.|\n)*\.babelrc(.|\n)*package\.json/);
}, },
); );
it("js-config-error", function() { it("js-config-error", function() {
assert.throws(function() { assert.throws(function() {
buildConfigChain({ buildConfigChain(
{
filename: fixture("js-config-error", "src.js"), filename: fixture("js-config-error", "src.js"),
}); },
DEFAULT_ENV,
);
}, /Error while loading config/); }, /Error while loading config/);
}); });
it("js-config-error2", function() { it("js-config-error2", function() {
assert.throws(function() { assert.throws(function() {
buildConfigChain({ buildConfigChain(
{
filename: fixture("js-config-error2", "src.js"), filename: fixture("js-config-error2", "src.js"),
}); },
DEFAULT_ENV,
);
}, /Configuration should be an exported JavaScript object/); }, /Configuration should be an exported JavaScript object/);
}); });
it("js-config-error3", function() { it("js-config-error3", function() {
assert.throws(function() { assert.throws(function() {
buildConfigChain({ buildConfigChain(
{
filename: fixture("js-config-error3", "src.js"), filename: fixture("js-config-error3", "src.js"),
}); },
DEFAULT_ENV,
);
}, /Configuration should be an exported JavaScript object/); }, /Configuration should be an exported JavaScript object/);
}); });
it("json-config-error", function() { it("json-config-error", function() {
assert.throws(function() { assert.throws(function() {
buildConfigChain({ buildConfigChain(
{
filename: fixture("json-config-error", "src.js"), filename: fixture("json-config-error", "src.js"),
}); },
DEFAULT_ENV,
);
}, /Error while parsing config/); }, /Error while parsing config/);
}); });
}); });