merge plugin manager into option manager
This commit is contained in:
parent
4924a9adea
commit
69f67c9ec3
@ -1,4 +1,8 @@
|
|||||||
import { validateOption, normaliseOptions } from "./index";
|
import Plugin from "../../plugin";
|
||||||
|
import * as messages from "babel-messages";
|
||||||
|
import * as context from "../../../api/node";
|
||||||
|
import { normaliseOptions } from "./index";
|
||||||
|
import resolve from "../../../helpers/resolve";
|
||||||
import json5 from "json5";
|
import json5 from "json5";
|
||||||
import isAbsolute from "path-is-absolute";
|
import isAbsolute from "path-is-absolute";
|
||||||
import pathExists from "path-exists";
|
import pathExists from "path-exists";
|
||||||
@ -32,6 +36,27 @@ export default class OptionManager {
|
|||||||
this.log = log;
|
this.log = log;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static memoisedPlugins = [];
|
||||||
|
|
||||||
|
static memoisePluginContainer(fn, loc, i) {
|
||||||
|
for (var cache of (OptionManager.memoisedPlugins: Array)) {
|
||||||
|
if (cache.container === fn) return cache.plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
var obj = fn(context);
|
||||||
|
|
||||||
|
if (typeof obj === "object") {
|
||||||
|
var plugin = new Plugin(obj);
|
||||||
|
OptionManager.memoisedPlugins.push({
|
||||||
|
container: fn,
|
||||||
|
plugin: plugin
|
||||||
|
});
|
||||||
|
return plugin;
|
||||||
|
} else {
|
||||||
|
throw new TypeError(messages.get("pluginNotObject", loc, i, typeof obj));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static createBareOptions() {
|
static createBareOptions() {
|
||||||
var opts = {};
|
var opts = {};
|
||||||
|
|
||||||
@ -43,6 +68,41 @@ export default class OptionManager {
|
|||||||
return opts;
|
return opts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static normalisePlugins(loc, dirname, plugins) {
|
||||||
|
return plugins.map(function (val, i) {
|
||||||
|
var plugin, options;
|
||||||
|
|
||||||
|
// destructure plugins
|
||||||
|
if (Array.isArray(val)) {
|
||||||
|
[plugin, options] = val;
|
||||||
|
} else {
|
||||||
|
plugin = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// allow plugins to be specified as strings
|
||||||
|
if (typeof plugin === "string") {
|
||||||
|
var pluginLoc = resolve(`babel-plugin-${plugin}`, dirname) || resolve(plugin, dirname);
|
||||||
|
if (pluginLoc) {
|
||||||
|
plugin = require(pluginLoc);
|
||||||
|
} else {
|
||||||
|
throw new ReferenceError(messages.get("pluginUnknown", plugin, loc, i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// allow plugin containers to be specified so they don't have to manually require
|
||||||
|
if (typeof plugin === "function") {
|
||||||
|
plugin = OptionManager.memoisePluginContainer(plugin, loc, i);
|
||||||
|
} else {
|
||||||
|
throw new TypeError(messages.get("pluginNotFunction", loc, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate
|
||||||
|
plugin.validate(loc, i);
|
||||||
|
|
||||||
|
return [plugin, options];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
addConfig(loc, key?, json=json5) {
|
addConfig(loc, key?, json=json5) {
|
||||||
if (this.resolvedConfigs.indexOf(loc) >= 0) return;
|
if (this.resolvedConfigs.indexOf(loc) >= 0) return;
|
||||||
|
|
||||||
@ -61,23 +121,78 @@ export default class OptionManager {
|
|||||||
this.resolvedConfigs.push(loc);
|
this.resolvedConfigs.push(loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
mergeOptions(opts, alias = "foreign") {
|
/**
|
||||||
|
* This is called when we want to merge the input `opts` into our
|
||||||
|
* base options.
|
||||||
|
*
|
||||||
|
* - `alias` is used to output pretty traces back to the original source.
|
||||||
|
* - `loc` is used to point to the original config.
|
||||||
|
* - `dirname` is used to resolve plugins relative to it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
mergeOptions(opts, alias = "foreign", loc, dirname) {
|
||||||
if (!opts) return;
|
if (!opts) return;
|
||||||
|
|
||||||
for (let key in opts) {
|
dirname = dirname || process.cwd();
|
||||||
if (key[0] === "_") continue;
|
loc = loc || alias;
|
||||||
|
|
||||||
|
for (let key in opts) {
|
||||||
let option = config[key];
|
let option = config[key];
|
||||||
|
|
||||||
// check for an unknown option
|
// check for an unknown option
|
||||||
if (!option) this.log.error(`Unknown option: ${alias}.${key}`, ReferenceError);
|
if (!option && this.log) this.log.error(`Unknown option: ${alias}.${key}`, ReferenceError);
|
||||||
}
|
}
|
||||||
|
|
||||||
// normalise options
|
// normalise options
|
||||||
normaliseOptions(opts);
|
normaliseOptions(opts);
|
||||||
|
|
||||||
|
// resolve plugins
|
||||||
|
if (opts.plugins) {
|
||||||
|
opts.plugins = OptionManager.normalisePlugins(loc, dirname, opts.plugins);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add extends clause
|
||||||
|
if (opts.extends) {
|
||||||
|
this.addConfig(resolve(opts.extends, dirname));
|
||||||
|
delete opts.extends;
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve presets
|
||||||
|
if (opts.presets) {
|
||||||
|
this.mergePresets(opts.presets, dirname);
|
||||||
|
delete opts.presets;
|
||||||
|
}
|
||||||
|
|
||||||
|
var envOpts;
|
||||||
|
var envKey = process.env.BABEL_ENV || process.env.NODE_ENV || "development";
|
||||||
|
if (opts.env) {
|
||||||
|
envOpts = opts.env[envKey];
|
||||||
|
delete opts.env;
|
||||||
|
}
|
||||||
|
|
||||||
// merge them into this current files options
|
// merge them into this current files options
|
||||||
merge(this.options, opts);
|
merge(this.options, opts);
|
||||||
|
|
||||||
|
// merge in env options
|
||||||
|
this.mergeOptions(envOpts, `${alias}.env.${envKey}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
mergePresets(presets: Array, dirname) {
|
||||||
|
for (var val of presets) {
|
||||||
|
if (typeof val === "string") {
|
||||||
|
var presetLoc = resolve(`babel-preset-${val}`, dirname) || resolve(val, dirname);
|
||||||
|
if (presetLoc) {
|
||||||
|
var presetOpts = require(presetLoc);
|
||||||
|
this.mergeOptions(presetOpts, presetLoc, presetLoc, path.dirname(presetLoc));
|
||||||
|
} else {
|
||||||
|
throw new Error("todo");
|
||||||
|
}
|
||||||
|
} else if (typeof val === "object") {
|
||||||
|
this.mergeOptions(val);
|
||||||
|
} else {
|
||||||
|
throw new Error("todo");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addIgnoreConfig(loc) {
|
addIgnoreConfig(loc) {
|
||||||
@ -91,10 +206,6 @@ export default class OptionManager {
|
|||||||
this.mergeOptions({ ignore: lines }, loc);
|
this.mergeOptions({ ignore: lines }, loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Description
|
|
||||||
*/
|
|
||||||
|
|
||||||
findConfigs(loc) {
|
findConfigs(loc) {
|
||||||
if (!loc) return;
|
if (!loc) return;
|
||||||
|
|
||||||
@ -102,17 +213,33 @@ export default class OptionManager {
|
|||||||
loc = path.join(process.cwd(), loc);
|
loc = path.join(process.cwd(), loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (loc !== (loc = path.dirname(loc))) {
|
var foundConfig = false;
|
||||||
if (this.options.breakConfig) return;
|
var foundIgnore = false;
|
||||||
|
|
||||||
|
while (loc !== (loc = path.dirname(loc))) {
|
||||||
|
if (!foundConfig) {
|
||||||
var configLoc = path.join(loc, BABELRC_FILENAME);
|
var configLoc = path.join(loc, BABELRC_FILENAME);
|
||||||
if (exists(configLoc)) this.addConfig(configLoc);
|
if (exists(configLoc)) {
|
||||||
|
this.addConfig(configLoc);
|
||||||
|
foundConfig = true;
|
||||||
|
}
|
||||||
|
|
||||||
var pkgLoc = path.join(loc, PACKAGE_FILENAME);
|
var pkgLoc = path.join(loc, PACKAGE_FILENAME);
|
||||||
if (exists(pkgLoc)) this.addConfig(pkgLoc, "babel", JSON);
|
if (exists(pkgLoc)) {
|
||||||
|
this.addConfig(pkgLoc, "babel", JSON);
|
||||||
|
foundConfig = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundIgnore) {
|
||||||
var ignoreLoc = path.join(loc, BABELIGNORE_FILENAME);
|
var ignoreLoc = path.join(loc, BABELIGNORE_FILENAME);
|
||||||
if (exists(ignoreLoc)) this.addIgnoreConfig(ignoreLoc);
|
if (exists(ignoreLoc)) {
|
||||||
|
this.addIgnoreConfig(ignoreLoc);
|
||||||
|
foundIgnore = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundIgnore && foundConfig) return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,17 +253,7 @@ export default class OptionManager {
|
|||||||
// optional
|
// optional
|
||||||
if (!val && option.optional) continue;
|
if (!val && option.optional) continue;
|
||||||
|
|
||||||
// deprecated
|
// aliases
|
||||||
if (this.log && val && option.deprecated) {
|
|
||||||
this.log.deprecate(`Deprecated option ${key}: ${option.deprecated}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate
|
|
||||||
if (this.pipeline && val) {
|
|
||||||
val = validateOption(key, val, this.pipeline);
|
|
||||||
}
|
|
||||||
|
|
||||||
// aaliases
|
|
||||||
if (option.alias) {
|
if (option.alias) {
|
||||||
opts[option.alias] = opts[option.alias] || val;
|
opts[option.alias] = opts[option.alias] || val;
|
||||||
} else {
|
} else {
|
||||||
@ -146,23 +263,13 @@ export default class OptionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init(opts) {
|
init(opts) {
|
||||||
this.mergeOptions(opts, "direct");
|
|
||||||
|
|
||||||
// babelrc option
|
|
||||||
if (opts.babelrc) {
|
|
||||||
for (var loc of (opts.babelrc: Array)) this.addConfig(loc);
|
|
||||||
}
|
|
||||||
|
|
||||||
// resolve all .babelrc files
|
// resolve all .babelrc files
|
||||||
if (opts.babelrc !== false) {
|
if (opts.babelrc !== false) {
|
||||||
this.findConfigs(opts.filename);
|
this.findConfigs(opts.filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
// merge in env
|
// merge in base options
|
||||||
var envKey = process.env.BABEL_ENV || process.env.NODE_ENV || "development";
|
this.mergeOptions(opts, "base");
|
||||||
if (this.options.env) {
|
|
||||||
this.mergeOptions(this.options.env[envKey], `direct.env.${envKey}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// normalise
|
// normalise
|
||||||
this.normaliseOptions(opts);
|
this.normaliseOptions(opts);
|
||||||
|
|||||||
@ -1,120 +0,0 @@
|
|||||||
import Plugin from "../plugin";
|
|
||||||
import * as types from "babel-types";
|
|
||||||
import * as messages from "babel-messages";
|
|
||||||
import resolve from "try-resolve";
|
|
||||||
import traverse from "babel-traverse";
|
|
||||||
import parse from "../../helpers/parse";
|
|
||||||
|
|
||||||
var context = {
|
|
||||||
messages,
|
|
||||||
Plugin,
|
|
||||||
types,
|
|
||||||
parse,
|
|
||||||
traverse
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class PluginManager {
|
|
||||||
static memoisedPlugins = [];
|
|
||||||
|
|
||||||
static memoisePluginContainer(fn) {
|
|
||||||
for (var i = 0; i < PluginManager.memoisedPlugins.length; i++) {
|
|
||||||
var plugin = PluginManager.memoisedPlugins[i];
|
|
||||||
if (plugin.container === fn) return plugin.transformer;
|
|
||||||
}
|
|
||||||
|
|
||||||
var transformer = fn(context);
|
|
||||||
PluginManager.memoisedPlugins.push({
|
|
||||||
container: fn,
|
|
||||||
transformer: transformer
|
|
||||||
});
|
|
||||||
return transformer;
|
|
||||||
}
|
|
||||||
|
|
||||||
static positions = ["before", "after"];
|
|
||||||
|
|
||||||
constructor({ file, transformers, before, after } = { transformers: {}, before: [], after: [] }) {
|
|
||||||
this.transformers = transformers;
|
|
||||||
this.file = file;
|
|
||||||
this.before = before;
|
|
||||||
this.after = after;
|
|
||||||
}
|
|
||||||
|
|
||||||
subnormaliseString(name, position) {
|
|
||||||
// this is a plugin in the form of "foobar" or "foobar:after"
|
|
||||||
// where the optional colon is the delimiter for plugin position in the transformer stack
|
|
||||||
|
|
||||||
var match = name.match(/^(.*?):(after|before)$/);
|
|
||||||
if (match) [, name, position] = match;
|
|
||||||
|
|
||||||
var loc = resolve.relative(`babel-plugin-${name}`) || resolve.relative(name);
|
|
||||||
if (loc) {
|
|
||||||
var plugin = require(loc);
|
|
||||||
return {
|
|
||||||
position: position,
|
|
||||||
plugin: plugin.default || plugin
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
throw new ReferenceError(messages.get("pluginUnknown", name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
validate(name, plugin) {
|
|
||||||
// validate transformer key
|
|
||||||
var key = plugin.key;
|
|
||||||
if (this.transformers[key]) {
|
|
||||||
throw new ReferenceError(messages.get("pluginKeyCollision", key));
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate Transformer instance
|
|
||||||
if (!plugin.buildPass || plugin.constructor.name !== "Plugin") {
|
|
||||||
throw new TypeError(messages.get("pluginNotTransformer", name));
|
|
||||||
}
|
|
||||||
|
|
||||||
// register as a plugin
|
|
||||||
plugin.metadata.plugin = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
add(name) {
|
|
||||||
var position;
|
|
||||||
var plugin;
|
|
||||||
|
|
||||||
if (name) {
|
|
||||||
if (typeof name === "object" && name.transformer) {
|
|
||||||
({ transformer: plugin, position } = name);
|
|
||||||
} else if (typeof name !== "string") {
|
|
||||||
// not a string so we'll just assume that it's a direct Transformer instance, if not then
|
|
||||||
// the checks later on will complain
|
|
||||||
plugin = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof name === "string") {
|
|
||||||
({ plugin, position } = this.subnormaliseString(name, position));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new TypeError(messages.get("pluginIllegalKind", typeof name, name));
|
|
||||||
}
|
|
||||||
|
|
||||||
// default position
|
|
||||||
position = position || "before";
|
|
||||||
|
|
||||||
// validate position
|
|
||||||
if (PluginManager.positions.indexOf(position) < 0) {
|
|
||||||
throw new TypeError(messages.get("pluginIllegalPosition", position, name));
|
|
||||||
}
|
|
||||||
|
|
||||||
// allow plugin containers to be specified so they don't have to manually require
|
|
||||||
if (typeof plugin === "function") {
|
|
||||||
plugin = PluginManager.memoisePluginContainer(plugin);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
this.validate(name, plugin);
|
|
||||||
|
|
||||||
// build!
|
|
||||||
var pass = this.transformers[plugin.key] = plugin.buildPass(this.file);
|
|
||||||
if (pass.canTransform()) {
|
|
||||||
var stack = position === "before" ? this.before : this.after;
|
|
||||||
stack.push(pass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user