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 isAbsolute from "path-is-absolute";
|
||||
import pathExists from "path-exists";
|
||||
@ -32,6 +36,27 @@ export default class OptionManager {
|
||||
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() {
|
||||
var opts = {};
|
||||
|
||||
@ -43,6 +68,41 @@ export default class OptionManager {
|
||||
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) {
|
||||
if (this.resolvedConfigs.indexOf(loc) >= 0) return;
|
||||
|
||||
@ -61,23 +121,78 @@ export default class OptionManager {
|
||||
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;
|
||||
|
||||
for (let key in opts) {
|
||||
if (key[0] === "_") continue;
|
||||
dirname = dirname || process.cwd();
|
||||
loc = loc || alias;
|
||||
|
||||
for (let key in opts) {
|
||||
let option = config[key];
|
||||
|
||||
// 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
|
||||
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(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) {
|
||||
@ -91,10 +206,6 @@ export default class OptionManager {
|
||||
this.mergeOptions({ ignore: lines }, loc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
findConfigs(loc) {
|
||||
if (!loc) return;
|
||||
|
||||
@ -102,17 +213,33 @@ export default class OptionManager {
|
||||
loc = path.join(process.cwd(), loc);
|
||||
}
|
||||
|
||||
var foundConfig = false;
|
||||
var foundIgnore = false;
|
||||
|
||||
while (loc !== (loc = path.dirname(loc))) {
|
||||
if (this.options.breakConfig) return;
|
||||
if (!foundConfig) {
|
||||
var configLoc = path.join(loc, BABELRC_FILENAME);
|
||||
if (exists(configLoc)) {
|
||||
this.addConfig(configLoc);
|
||||
foundConfig = true;
|
||||
}
|
||||
|
||||
var configLoc = path.join(loc, BABELRC_FILENAME);
|
||||
if (exists(configLoc)) this.addConfig(configLoc);
|
||||
var pkgLoc = path.join(loc, PACKAGE_FILENAME);
|
||||
if (exists(pkgLoc)) {
|
||||
this.addConfig(pkgLoc, "babel", JSON);
|
||||
foundConfig = true;
|
||||
}
|
||||
}
|
||||
|
||||
var pkgLoc = path.join(loc, PACKAGE_FILENAME);
|
||||
if (exists(pkgLoc)) this.addConfig(pkgLoc, "babel", JSON);
|
||||
if (!foundIgnore) {
|
||||
var ignoreLoc = path.join(loc, BABELIGNORE_FILENAME);
|
||||
if (exists(ignoreLoc)) {
|
||||
this.addIgnoreConfig(ignoreLoc);
|
||||
foundIgnore = true;
|
||||
}
|
||||
}
|
||||
|
||||
var ignoreLoc = path.join(loc, BABELIGNORE_FILENAME);
|
||||
if (exists(ignoreLoc)) this.addIgnoreConfig(ignoreLoc);
|
||||
if (foundIgnore && foundConfig) return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,17 +253,7 @@ export default class OptionManager {
|
||||
// optional
|
||||
if (!val && option.optional) continue;
|
||||
|
||||
// deprecated
|
||||
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
|
||||
// aliases
|
||||
if (option.alias) {
|
||||
opts[option.alias] = opts[option.alias] || val;
|
||||
} else {
|
||||
@ -146,23 +263,13 @@ export default class OptionManager {
|
||||
}
|
||||
|
||||
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
|
||||
if (opts.babelrc !== false) {
|
||||
this.findConfigs(opts.filename);
|
||||
}
|
||||
|
||||
// merge in env
|
||||
var envKey = process.env.BABEL_ENV || process.env.NODE_ENV || "development";
|
||||
if (this.options.env) {
|
||||
this.mergeOptions(this.options.env[envKey], `direct.env.${envKey}`);
|
||||
}
|
||||
// merge in base options
|
||||
this.mergeOptions(opts, "base");
|
||||
|
||||
// normalise
|
||||
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