Typecheck much more of the config loading process (#5642)
* Add type declarations for micromatch. * Enable Flowtype on all config loading. * Remove unneeded comments.
This commit is contained in:
parent
c46ef658b5
commit
6af8e64711
@ -9,6 +9,7 @@ packages/*/src
|
|||||||
lib/file.js
|
lib/file.js
|
||||||
lib/parser.js
|
lib/parser.js
|
||||||
lib/types.js
|
lib/types.js
|
||||||
|
lib/third-party-libs.js.flow
|
||||||
|
|
||||||
[options]
|
[options]
|
||||||
strip_root=true
|
strip_root=true
|
||||||
|
|||||||
9
lib/third-party-libs.js.flow
Normal file
9
lib/third-party-libs.js.flow
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* Basic declarations for the npm modules we use.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare module "micromatch" {
|
||||||
|
declare function exports(Array<string>, Array<string>, ?{
|
||||||
|
nocase: boolean,
|
||||||
|
}): Array<string>;
|
||||||
|
}
|
||||||
@ -1,10 +1,24 @@
|
|||||||
import * as babel from "../index";
|
// @flow
|
||||||
|
|
||||||
|
import { getEnv } from "./helpers/environment";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import micromatch from "micromatch";
|
import micromatch from "micromatch";
|
||||||
|
|
||||||
import { findConfigs, loadConfig } from "./loading/files";
|
import { findConfigs, loadConfig } from "./loading/files";
|
||||||
|
|
||||||
export default function buildConfigChain(opts: Object = {}) {
|
type ConfigItem = {
|
||||||
|
type: "options"|"arguments",
|
||||||
|
options: {},
|
||||||
|
dirname: string,
|
||||||
|
alias: string,
|
||||||
|
loc: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function buildConfigChain(opts: {}): Array<ConfigItem>|null {
|
||||||
|
if (typeof opts.filename !== "string" && opts.filename != null) {
|
||||||
|
throw new Error(".filename must be a string, null, or undefined");
|
||||||
|
}
|
||||||
|
|
||||||
const filename = opts.filename ? path.resolve(opts.filename) : null;
|
const filename = opts.filename ? path.resolve(opts.filename) : null;
|
||||||
const builder = new ConfigChainBuilder(filename);
|
const builder = new ConfigChainBuilder(filename);
|
||||||
|
|
||||||
@ -17,7 +31,7 @@ export default function buildConfigChain(opts: Object = {}) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// resolve all .babelrc files
|
// resolve all .babelrc files
|
||||||
if (opts.babelrc !== false) {
|
if (opts.babelrc !== false && filename) {
|
||||||
builder.findConfigs(filename);
|
builder.findConfigs(filename);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -30,6 +44,10 @@ export default function buildConfigChain(opts: Object = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ConfigChainBuilder {
|
class ConfigChainBuilder {
|
||||||
|
filename: string|null;
|
||||||
|
configs: Array<ConfigItem>;
|
||||||
|
possibleDirs: null|Array<string>;
|
||||||
|
|
||||||
constructor(filename) {
|
constructor(filename) {
|
||||||
this.configs = [];
|
this.configs = [];
|
||||||
this.filename = filename;
|
this.filename = filename;
|
||||||
@ -40,59 +58,68 @@ class ConfigChainBuilder {
|
|||||||
* Tests if a filename should be ignored based on "ignore" and "only" options.
|
* Tests if a filename should be ignored based on "ignore" and "only" options.
|
||||||
*/
|
*/
|
||||||
shouldIgnore(
|
shouldIgnore(
|
||||||
ignore: Array<string | RegExp | Function>,
|
ignore: mixed,
|
||||||
only?: Array<string | RegExp | Function>,
|
only: mixed,
|
||||||
dirname: string,
|
dirname: string,
|
||||||
): boolean {
|
): boolean {
|
||||||
if (!this.filename) return false;
|
if (!this.filename) return false;
|
||||||
|
|
||||||
if (ignore && !Array.isArray(ignore)) {
|
if (ignore) {
|
||||||
throw new Error(`.ignore should be an array, ${JSON.stringify(ignore)} given`);
|
if (!Array.isArray(ignore)) {
|
||||||
|
throw new Error(`.ignore should be an array, ${JSON.stringify(ignore)} given`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.matchesPatterns(ignore, dirname)) return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (only && !Array.isArray(only)) {
|
if (only) {
|
||||||
throw new Error(`.only should be an array, ${JSON.stringify(only)} given`);
|
if (!Array.isArray(only)) {
|
||||||
|
throw new Error(`.only should be an array, ${JSON.stringify(only)} given`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.matchesPatterns(only, dirname)) return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (ignore && this.matchesPatterns(ignore, dirname)) ||
|
return false;
|
||||||
(only && !this.matchesPatterns(only, dirname));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns result of calling function with filename if pattern is a function.
|
* Returns result of calling function with filename if pattern is a function.
|
||||||
* Otherwise returns result of matching pattern Regex with filename.
|
* Otherwise returns result of matching pattern Regex with filename.
|
||||||
*/
|
*/
|
||||||
matchesPatterns(patterns: Array<string | Function | RegExp>, dirname: string) {
|
matchesPatterns(patterns: Array<mixed>, dirname: string) {
|
||||||
|
const filename = this.filename;
|
||||||
|
if (!filename) throw new Error("Assertion failure: .filename should always exist here");
|
||||||
|
|
||||||
const res = [];
|
const res = [];
|
||||||
const strings = [];
|
const strings = [];
|
||||||
const fns = [];
|
const fns = [];
|
||||||
|
|
||||||
patterns.forEach((pattern) => {
|
patterns.forEach((pattern) => {
|
||||||
const type = typeof pattern;
|
if (typeof pattern === "string") strings.push(pattern);
|
||||||
if (type === "string") strings.push(pattern);
|
else if (typeof pattern === "function") fns.push(pattern);
|
||||||
else if (type === "function") fns.push(pattern);
|
else if (pattern instanceof RegExp) res.push(pattern);
|
||||||
else res.push(pattern);
|
else throw new Error("Patterns must be a string, function, or regular expression");
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.some((re) => re.test(this.filename))) return true;
|
if (res.some((re) => re.test(filename))) return true;
|
||||||
if (fns.some((fn) => fn(this.filename))) return true;
|
if (fns.some((fn) => fn(filename))) return true;
|
||||||
|
|
||||||
if (strings.length > 0) {
|
if (strings.length > 0) {
|
||||||
|
let possibleDirs = this.possibleDirs;
|
||||||
// Lazy-init so we don't initialize this for files that have no glob patterns.
|
// Lazy-init so we don't initialize this for files that have no glob patterns.
|
||||||
if (!this.possibleDirs) {
|
if (!possibleDirs) {
|
||||||
this.possibleDirs = [];
|
possibleDirs = this.possibleDirs = [];
|
||||||
|
|
||||||
if (this.filename) {
|
possibleDirs.push(filename);
|
||||||
this.possibleDirs.push(this.filename);
|
|
||||||
|
|
||||||
let current = this.filename;
|
let current = filename;
|
||||||
while (true) {
|
while (true) {
|
||||||
const previous = current;
|
const previous = current;
|
||||||
current = path.dirname(current);
|
current = path.dirname(current);
|
||||||
if (previous === current) break;
|
if (previous === current) break;
|
||||||
|
|
||||||
this.possibleDirs.push(current);
|
possibleDirs.push(current);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +131,7 @@ class ConfigChainBuilder {
|
|||||||
return (negate ? "!" : "") + path.resolve(dirname, pattern);
|
return (negate ? "!" : "") + path.resolve(dirname, pattern);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (micromatch(this.possibleDirs, absolutePatterns, { nocase: true }).length > 0) {
|
if (micromatch(possibleDirs, absolutePatterns, { nocase: true }).length > 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -113,12 +140,6 @@ class ConfigChainBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
findConfigs(loc: string) {
|
findConfigs(loc: string) {
|
||||||
if (!loc) return;
|
|
||||||
|
|
||||||
if (!path.isAbsolute(loc)) {
|
|
||||||
loc = path.join(process.cwd(), loc);
|
|
||||||
}
|
|
||||||
|
|
||||||
findConfigs(path.dirname(loc)).forEach(({ filepath, dirname, options }) => {
|
findConfigs(path.dirname(loc)).forEach(({ filepath, dirname, options }) => {
|
||||||
this.mergeConfig({
|
this.mergeConfig({
|
||||||
type: "options",
|
type: "options",
|
||||||
@ -131,31 +152,33 @@ class ConfigChainBuilder {
|
|||||||
|
|
||||||
mergeConfig({
|
mergeConfig({
|
||||||
type,
|
type,
|
||||||
options,
|
options: rawOpts,
|
||||||
alias,
|
alias,
|
||||||
loc,
|
|
||||||
dirname,
|
dirname,
|
||||||
}) {
|
}) {
|
||||||
if (!options) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bail out ASAP if this file is ignored so that we run as little logic as possible on ignored files.
|
// Bail out ASAP if this file is ignored so that we run as little logic as possible on ignored files.
|
||||||
if (this.filename && this.shouldIgnore(options.ignore, options.only, dirname)) {
|
if (this.filename && this.shouldIgnore(rawOpts.ignore || null, rawOpts.only || null, dirname)) {
|
||||||
// TODO(logan): This is a really cross way to bail out. Avoid this in rewrite.
|
// TODO(logan): This is a really cross way to bail out. Avoid this in rewrite.
|
||||||
throw Object.assign(new Error("This file has been ignored."), { code: "BABEL_IGNORED_FILE" });
|
throw Object.assign((new Error("This file has been ignored."): any), { code: "BABEL_IGNORED_FILE" });
|
||||||
}
|
}
|
||||||
|
|
||||||
options = Object.assign({}, options);
|
const options = Object.assign({}, rawOpts);
|
||||||
|
delete options.env;
|
||||||
|
delete options.extends;
|
||||||
|
|
||||||
loc = loc || alias;
|
const envKey = getEnv();
|
||||||
|
|
||||||
// env
|
if (rawOpts.env != null && (typeof rawOpts.env !== "object" || Array.isArray(rawOpts.env))) {
|
||||||
const envKey = babel.getEnv();
|
throw new Error(".env block must be an object, null, or undefined");
|
||||||
if (options.env) {
|
}
|
||||||
const envOpts = options.env[envKey];
|
|
||||||
delete options.env;
|
|
||||||
|
|
||||||
|
const envOpts = rawOpts.env && rawOpts.env[envKey];
|
||||||
|
|
||||||
|
if (envOpts != null && (typeof envOpts !== "object" || Array.isArray(envOpts))) {
|
||||||
|
throw new Error(".env[...] block must be an object, null, or undefined");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (envOpts) {
|
||||||
this.mergeConfig({
|
this.mergeConfig({
|
||||||
type,
|
type,
|
||||||
options: envOpts,
|
options: envOpts,
|
||||||
@ -168,13 +191,14 @@ class ConfigChainBuilder {
|
|||||||
type,
|
type,
|
||||||
options,
|
options,
|
||||||
alias,
|
alias,
|
||||||
loc,
|
loc: alias,
|
||||||
dirname,
|
dirname,
|
||||||
});
|
});
|
||||||
|
|
||||||
// add extends clause
|
if (rawOpts.extends) {
|
||||||
if (options.extends) {
|
if (typeof rawOpts.extends !== "string") throw new Error(".extends must be a string");
|
||||||
const extendsConfig = loadConfig(options.extends, dirname);
|
|
||||||
|
const extendsConfig = loadConfig(rawOpts.extends, dirname);
|
||||||
|
|
||||||
const existingConfig = this.configs.some((config) => {
|
const existingConfig = this.configs.some((config) => {
|
||||||
return config.alias === extendsConfig.filepath;
|
return config.alias === extendsConfig.filepath;
|
||||||
@ -187,7 +211,6 @@ class ConfigChainBuilder {
|
|||||||
dirname: extendsConfig.dirname,
|
dirname: extendsConfig.dirname,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
delete options.extends;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,7 +31,7 @@ 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: Object, ResultT>(
|
export function makeWeakCache<ArgT: {}, ResultT>(
|
||||||
handler: (ArgT, CacheConfigurator) => ResultT,
|
handler: (ArgT, CacheConfigurator) => ResultT,
|
||||||
autoPermacache?: boolean,
|
autoPermacache?: boolean,
|
||||||
): (ArgT) => ResultT {
|
): (ArgT) => ResultT {
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
export function getEnv(defaultValue = "development") {
|
// @flow
|
||||||
|
|
||||||
|
export function getEnv(defaultValue: string = "development"): string {
|
||||||
return process.env.BABEL_ENV
|
return process.env.BABEL_ENV
|
||||||
|| process.env.NODE_ENV
|
|| process.env.NODE_ENV
|
||||||
|| defaultValue;
|
|| defaultValue;
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
import mergeWith from "lodash/mergeWith";
|
import mergeWith from "lodash/mergeWith";
|
||||||
|
|
||||||
export default function (dest?: Object, src?: Object): ?Object {
|
export default function<T: {}>(dest?: T, src?: T) {
|
||||||
if (!dest || !src) return;
|
if (!dest || !src) return;
|
||||||
|
|
||||||
return mergeWith(dest, src, function (a, b) {
|
mergeWith(dest, src, function (a, b) {
|
||||||
if (b && Array.isArray(a)) {
|
if (b && Array.isArray(a)) {
|
||||||
const newArray = b.slice(0);
|
const newArray = b.slice(0);
|
||||||
|
|
||||||
|
|||||||
@ -1,14 +1,20 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
import type Plugin from "./plugin";
|
import type Plugin from "./plugin";
|
||||||
import manageOptions from "./option-manager";
|
import manageOptions from "./option-manager";
|
||||||
|
|
||||||
export type ResolvedConfig = {
|
export type ResolvedConfig = {
|
||||||
options: Object,
|
options: Object,
|
||||||
passes: Array<Array<Plugin>>,
|
passes: Array<Array<[ Plugin, ?{} ]>>,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Standard API for loading Babel configuration data. Not for public consumption.
|
* Standard API for loading Babel configuration data. Not for public consumption.
|
||||||
*/
|
*/
|
||||||
export default function loadConfig(opts: Object): ResolvedConfig|null {
|
export default function loadConfig(opts: mixed): ResolvedConfig|null {
|
||||||
return manageOptions(opts);
|
if (opts != null && typeof opts !== "object") {
|
||||||
|
throw new Error("Babel options must be an object, null, or undefined");
|
||||||
|
}
|
||||||
|
|
||||||
|
return manageOptions(opts || {});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import { makeStrongCache } from "../../caching";
|
|||||||
type ConfigFile = {
|
type ConfigFile = {
|
||||||
filepath: string,
|
filepath: string,
|
||||||
dirname: string,
|
dirname: string,
|
||||||
options: Object,
|
options: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
const BABELRC_FILENAME = ".babelrc";
|
const BABELRC_FILENAME = ".babelrc";
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
type ConfigFile = {
|
type ConfigFile = {
|
||||||
filepath: string,
|
filepath: string,
|
||||||
dirname: string,
|
dirname: string,
|
||||||
options: Object,
|
options: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
import * as context from "../index";
|
import * as context from "../index";
|
||||||
import Plugin from "./plugin";
|
import Plugin from "./plugin";
|
||||||
import * as messages from "babel-messages";
|
import * as messages from "babel-messages";
|
||||||
@ -12,11 +14,11 @@ import clone from "lodash/clone";
|
|||||||
import { loadPlugin, loadPreset, loadParser, loadGenerator } from "./loading/files";
|
import { loadPlugin, loadPreset, loadParser, loadGenerator } from "./loading/files";
|
||||||
|
|
||||||
type MergeOptions = {
|
type MergeOptions = {
|
||||||
type: "arguments"|"options"|"preset",
|
+type: "arguments"|"options"|"preset",
|
||||||
options?: Object,
|
options: {},
|
||||||
alias: string,
|
alias: string,
|
||||||
loc?: string,
|
loc: string,
|
||||||
dirname?: string
|
dirname: string
|
||||||
};
|
};
|
||||||
|
|
||||||
const optionNames = new Set([
|
const optionNames = new Set([
|
||||||
@ -70,7 +72,10 @@ const ALLOWED_PLUGIN_KEYS = new Set([
|
|||||||
"inherits",
|
"inherits",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export default function manageOptions(opts?: Object) {
|
export default function manageOptions(opts: {}): {
|
||||||
|
options: Object,
|
||||||
|
passes: Array<Array<[ Plugin, ?{} ]>>,
|
||||||
|
}|null {
|
||||||
return new OptionManager().init(opts);
|
return new OptionManager().init(opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +86,7 @@ class OptionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
options: Object;
|
options: Object;
|
||||||
passes: Array<Array<Plugin>>;
|
passes: Array<Array<[Plugin, ?{}]>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is called when we want to merge the input `opts` into the
|
* This is called when we want to merge the input `opts` into the
|
||||||
@ -92,12 +97,19 @@ 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, ?{}]>) {
|
||||||
const result = loadConfig(config);
|
const result = loadConfig(config);
|
||||||
|
|
||||||
const plugins = result.plugins.map((descriptor) => loadPluginDescriptor(descriptor));
|
const plugins = result.plugins.map((descriptor) => loadPluginDescriptor(descriptor));
|
||||||
const presets = result.presets.map((descriptor) => loadPresetDescriptor(descriptor));
|
const presets = result.presets.map((descriptor) => loadPresetDescriptor(descriptor));
|
||||||
|
|
||||||
|
|
||||||
|
if (
|
||||||
|
config.options.passPerPreset != null &&
|
||||||
|
typeof config.options.passPerPreset !== "boolean"
|
||||||
|
) {
|
||||||
|
throw new Error(".passPerPreset must be a boolean or undefined");
|
||||||
|
}
|
||||||
const passPerPreset = config.options.passPerPreset;
|
const passPerPreset = config.options.passPerPreset;
|
||||||
pass = pass || this.passes[0];
|
pass = pass || this.passes[0];
|
||||||
|
|
||||||
@ -124,7 +136,7 @@ class OptionManager {
|
|||||||
merge(this.options, result.options);
|
merge(this.options, result.options);
|
||||||
}
|
}
|
||||||
|
|
||||||
init(opts: Object = {}): Object {
|
init(opts: {}) {
|
||||||
const configChain = buildConfigChain(opts);
|
const configChain = buildConfigChain(opts);
|
||||||
if (!configChain) return null;
|
if (!configChain) return null;
|
||||||
|
|
||||||
@ -136,7 +148,8 @@ class OptionManager {
|
|||||||
// 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
|
||||||
// to keep things simple we just bail out if re-wrapping the message.
|
// to keep things simple we just bail out if re-wrapping the message.
|
||||||
if (!/^\[BABEL\]/.test(e.message)) {
|
if (!/^\[BABEL\]/.test(e.message)) {
|
||||||
e.message = `[BABEL] ${opts.filename || "unknown"}: ${e.message}`;
|
const filename = typeof opts.filename === "string" ? opts.filename : null;
|
||||||
|
e.message = `[BABEL] ${filename || "unknown"}: ${e.message}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw e;
|
throw e;
|
||||||
@ -185,12 +198,28 @@ class OptionManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BasicDescriptor = {
|
||||||
|
value: {}|Function,
|
||||||
|
options: ?{},
|
||||||
|
dirname: string,
|
||||||
|
alias: string,
|
||||||
|
loc: string,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load and validate the given config into a set of options, plugins, and presets.
|
* Load and validate the given config into a set of options, plugins, and presets.
|
||||||
*/
|
*/
|
||||||
function loadConfig(config) {
|
function loadConfig(config): {
|
||||||
|
options: {},
|
||||||
|
plugins: Array<BasicDescriptor>,
|
||||||
|
presets: Array<BasicDescriptor>,
|
||||||
|
} {
|
||||||
const options = normalizeOptions(config);
|
const options = normalizeOptions(config);
|
||||||
|
|
||||||
|
if (config.options.plugins != null && !Array.isArray(config.options.plugins)) {
|
||||||
|
throw new Error(".plugins should be an array, null, or undefined");
|
||||||
|
}
|
||||||
|
|
||||||
const plugins = (config.options.plugins || []).map((plugin, index) => {
|
const plugins = (config.options.plugins || []).map((plugin, index) => {
|
||||||
const { filepath, value, options } = normalizePair(plugin, loadPlugin, config.dirname);
|
const { filepath, value, options } = normalizePair(plugin, loadPlugin, config.dirname);
|
||||||
|
|
||||||
@ -202,6 +231,11 @@ function loadConfig(config) {
|
|||||||
dirname: config.dirname,
|
dirname: config.dirname,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (config.options.presets != null && !Array.isArray(config.options.presets)) {
|
||||||
|
throw new Error(".presets should be an array, null, or undefined");
|
||||||
|
}
|
||||||
|
|
||||||
const presets = (config.options.presets || []).map((preset, index) => {
|
const presets = (config.options.presets || []).map((preset, index) => {
|
||||||
const { filepath, value, options } = normalizePair(preset, loadPreset, config.dirname);
|
const { filepath, value, options } = normalizePair(preset, loadPreset, config.dirname);
|
||||||
|
|
||||||
@ -259,19 +293,19 @@ function loadPluginDescriptor(descriptor) {
|
|||||||
return [ result, descriptor.options];
|
return [ result, descriptor.options];
|
||||||
}
|
}
|
||||||
|
|
||||||
function instantiatePlugin({ value: pluginObject, descriptor }) {
|
function instantiatePlugin({ value: pluginObj, descriptor }) {
|
||||||
Object.keys(pluginObject).forEach((key) => {
|
Object.keys(pluginObj).forEach((key) => {
|
||||||
if (!ALLOWED_PLUGIN_KEYS.has(key)) {
|
if (!ALLOWED_PLUGIN_KEYS.has(key)) {
|
||||||
throw new Error(messages.get("pluginInvalidProperty", descriptor.alias, key));
|
throw new Error(messages.get("pluginInvalidProperty", descriptor.alias, key));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (pluginObject.visitor && (pluginObject.visitor.enter || pluginObject.visitor.exit)) {
|
if (pluginObj.visitor && (pluginObj.visitor.enter || pluginObj.visitor.exit)) {
|
||||||
throw new Error("Plugins aren't allowed to specify catch-all enter/exit handlers. " +
|
throw new Error("Plugins aren't allowed to specify catch-all enter/exit handlers. " +
|
||||||
"Please target individual nodes.");
|
"Please target individual nodes.");
|
||||||
}
|
}
|
||||||
|
|
||||||
const plugin = Object.assign({}, pluginObject, {
|
const plugin = Object.assign({}, pluginObj, {
|
||||||
visitor: clone(pluginObject.visitor || {}),
|
visitor: clone(pluginObj.visitor || {}),
|
||||||
});
|
});
|
||||||
|
|
||||||
traverse.explode(plugin.visitor);
|
traverse.explode(plugin.visitor);
|
||||||
@ -284,6 +318,7 @@ function instantiatePlugin({ value: pluginObject, descriptor }) {
|
|||||||
loc: descriptor.loc,
|
loc: descriptor.loc,
|
||||||
value: plugin.inherits,
|
value: plugin.inherits,
|
||||||
options: descriptor.options,
|
options: descriptor.options,
|
||||||
|
dirname: descriptor.dirname,
|
||||||
};
|
};
|
||||||
|
|
||||||
inherits = loadPluginDescriptor(inheritsDescriptor)[0];
|
inherits = loadPluginDescriptor(inheritsDescriptor)[0];
|
||||||
@ -300,7 +335,7 @@ function instantiatePlugin({ value: pluginObject, descriptor }) {
|
|||||||
/**
|
/**
|
||||||
* 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.
|
||||||
*/
|
*/
|
||||||
function loadPresetDescriptor(descriptor) {
|
function loadPresetDescriptor(descriptor): MergeOptions {
|
||||||
return {
|
return {
|
||||||
type: "preset",
|
type: "preset",
|
||||||
options: loadDescriptor(descriptor).value,
|
options: loadDescriptor(descriptor).value,
|
||||||
@ -375,13 +410,6 @@ function normalizeOptions(config) {
|
|||||||
options.generatorOpts.generator = loadGenerator(options.generatorOpts.generator, config.dirname).value;
|
options.generatorOpts.generator = loadGenerator(options.generatorOpts.generator, config.dirname).value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.options.presets && !Array.isArray(config.options.presets)) {
|
|
||||||
throw new Error(`${alias}.presets should be an array`);
|
|
||||||
}
|
|
||||||
if (config.options.plugins && !Array.isArray(config.options.plugins)) {
|
|
||||||
throw new Error(`${alias}.plugins should be an array`);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete options.passPerPreset;
|
delete options.passPerPreset;
|
||||||
delete options.plugins;
|
delete options.plugins;
|
||||||
delete options.presets;
|
delete options.presets;
|
||||||
@ -392,15 +420,19 @@ function normalizeOptions(config) {
|
|||||||
/**
|
/**
|
||||||
* Given a plugin/preset item, resolve it into a standard format.
|
* Given a plugin/preset item, resolve it into a standard format.
|
||||||
*/
|
*/
|
||||||
function normalizePair(pair, resolver, dirname) {
|
function normalizePair(pair: mixed, resolver, dirname): {
|
||||||
|
filepath: string|null,
|
||||||
|
value: {}|Function,
|
||||||
|
options: ?{},
|
||||||
|
} {
|
||||||
let options;
|
let options;
|
||||||
let value = pair;
|
let value = pair;
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(pair)) {
|
||||||
if (value.length > 2) {
|
if (pair.length > 2) {
|
||||||
throw new Error(`Unexpected extra options ${JSON.stringify(value.slice(2))}.`);
|
throw new Error(`Unexpected extra options ${JSON.stringify(pair.slice(2))}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
[value, options] = value;
|
[value, options] = pair;
|
||||||
}
|
}
|
||||||
|
|
||||||
let filepath = null;
|
let filepath = null;
|
||||||
@ -411,6 +443,10 @@ function normalizePair(pair, resolver, dirname) {
|
|||||||
} = resolver(value, dirname));
|
} = resolver(value, dirname));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!value) {
|
||||||
|
throw new Error(`Unexpected falsy value: ${String(value)}`);
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof value === "object" && value.__esModule) {
|
if (typeof value === "object" && value.__esModule) {
|
||||||
if (value.default) {
|
if (value.default) {
|
||||||
value = value.default;
|
value = value.default;
|
||||||
@ -419,13 +455,12 @@ function normalizePair(pair, resolver, dirname) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!value) {
|
if (typeof value !== "object" && typeof value !== "function") {
|
||||||
throw new Error(`Unexpected falsy value: ${value}`);
|
throw new Error(`Unsupported format: ${typeof value}. Expected an object or a function.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const type = typeof value;
|
if (options != null && typeof options !== "object") {
|
||||||
if (type !== "object" && type !== "function") {
|
throw new Error("Plugin/Preset options must be an object, null, or undefined");
|
||||||
throw new Error(`Unsupported format: ${type}. Expected an object or a function.`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return { filepath, value, options };
|
return { filepath, value, options };
|
||||||
|
|||||||
@ -1,5 +1,23 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
export default class Plugin {
|
export default class Plugin {
|
||||||
constructor(plugin: Object, key?: string) {
|
constructor(plugin: {}, key?: string) {
|
||||||
|
if (plugin.name != null && typeof plugin.name !== "string") {
|
||||||
|
throw new Error("Plugin .name must be a string, null, or undefined");
|
||||||
|
}
|
||||||
|
if (plugin.manipulateOptions != null && typeof plugin.manipulateOptions !== "function") {
|
||||||
|
throw new Error("Plugin .manipulateOptions must be a function, null, or undefined");
|
||||||
|
}
|
||||||
|
if (plugin.post != null && typeof plugin.post !== "function") {
|
||||||
|
throw new Error("Plugin .post must be a function, null, or undefined");
|
||||||
|
}
|
||||||
|
if (plugin.pre != null && typeof plugin.pre !== "function") {
|
||||||
|
throw new Error("Plugin .pre must be a function, null, or undefined");
|
||||||
|
}
|
||||||
|
if (plugin.visitor != null && typeof plugin.visitor !== "object") {
|
||||||
|
throw new Error("Plugin .visitor must be an object, null, or undefined");
|
||||||
|
}
|
||||||
|
|
||||||
this.key = plugin.name || key;
|
this.key = plugin.name || key;
|
||||||
|
|
||||||
this.manipulateOptions = plugin.manipulateOptions;
|
this.manipulateOptions = plugin.manipulateOptions;
|
||||||
@ -12,5 +30,5 @@ export default class Plugin {
|
|||||||
manipulateOptions: ?Function;
|
manipulateOptions: ?Function;
|
||||||
post: ?Function;
|
post: ?Function;
|
||||||
pre: ?Function;
|
pre: ?Function;
|
||||||
visitor: Object;
|
visitor: ?{};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
// @flow
|
||||||
/* eslint max-len: "off" */
|
/* eslint max-len: "off" */
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
{
|
{
|
||||||
"plugins": [["transform-react-jsx", "foo.bar"]]
|
"plugins": [
|
||||||
|
["transform-react-jsx", {"pragma": "foo.bar"}]
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user