Move .inherits handling to OptionManager.

This commit is contained in:
Logan Smyth
2017-04-03 14:38:50 -07:00
parent 2ea3338b8e
commit ded4ca48cd
2 changed files with 60 additions and 90 deletions

View File

@@ -7,6 +7,8 @@ import merge from "./helpers/merge";
import removed from "./removed";
import buildConfigChain from "./build-config-chain";
import path from "path";
import traverse from "babel-traverse";
import clone from "lodash/clone";
import { loadPlugin, loadPreset, loadParser, loadGenerator } from "./loading/files";
@@ -74,6 +76,15 @@ const optionNames = new Set([
"generatorOpts",
]);
const ALLOWED_PLUGIN_KEYS = new Set([
"name",
"manipulateOptions",
"pre",
"post",
"visitor",
"inherits",
]);
export default class OptionManager {
constructor() {
this.options = OptionManager.createBareOptions();
@@ -99,16 +110,51 @@ export default class OptionManager {
obj = fn;
}
if (typeof obj === "object") {
const plugin = new Plugin(obj, alias);
OptionManager.memoisedPlugins.push({
container: fn,
plugin: plugin,
});
return plugin;
} else {
if (typeof obj !== "object") {
throw new TypeError(messages.get("pluginNotObject", loc, i, typeof obj) + loc + i);
}
Object.keys(obj).forEach((key) => {
if (!ALLOWED_PLUGIN_KEYS.has(key)) {
throw new Error(messages.get("pluginInvalidProperty", loc, i, key));
}
});
if (obj.visitor && (obj.visitor.enter || obj.visitor.exit)) {
throw new Error("Plugins aren't allowed to specify catch-all enter/exit handlers. " +
"Please target individual nodes.");
}
obj = Object.assign({}, obj, {
visitor: clone(obj.visitor || {}),
});
traverse.explode(obj.visitor);
if (obj.inherits) {
const inherited = OptionManager.normalisePlugin(obj.inherits, loc, "inherits");
obj.pre = OptionManager.chain(inherited.pre, obj.pre);
obj.post = OptionManager.chain(inherited.post, obj.post);
obj.manipulateOptions = OptionManager.chain(inherited.manipulateOptions, obj.manipulateOptions);
obj.visitor = traverse.visitors.merge([inherited.visitor, obj.visitor]);
}
const plugin = new Plugin(obj, alias);
OptionManager.memoisedPlugins.push({
container: fn,
plugin: plugin,
});
return plugin;
}
static chain(a, b) {
const fns = [a, b].filter(Boolean);
if (fns.length <= 1) return fns[0];
return function(...args) {
for (const fn of fns) {
fn.apply(this, args);
}
};
}
static createBareOptions() {
@@ -137,8 +183,6 @@ export default class OptionManager {
}
}
plugin.init(loc, i);
return plugin;
}

View File

@@ -1,90 +1,16 @@
import OptionManager from "./option-manager";
import * as messages from "babel-messages";
import traverse from "babel-traverse";
import clone from "lodash/clone";
const GLOBAL_VISITOR_PROPS = ["enter", "exit"];
export default class Plugin {
constructor(plugin: Object, key?: string) {
this.initialized = false;
this.raw = Object.assign({}, plugin);
this.key = this.take("name") || key;
this.key = plugin.name || key;
this.manipulateOptions = this.take("manipulateOptions");
this.post = this.take("post");
this.pre = this.take("pre");
this.visitor = this.normaliseVisitor(clone(this.take("visitor")) || {});
this.manipulateOptions = plugin.manipulateOptions;
this.post = plugin.post;
this.pre = plugin.pre;
this.visitor = plugin.visitor;
}
initialized: boolean;
raw: Object;
key: ?string;
manipulateOptions: ?Function;
post: ?Function;
pre: ?Function;
visitor: Object;
take(key) {
const val = this.raw[key];
delete this.raw[key];
return val;
}
chain(target, key) {
if (!target[key]) return this[key];
if (!this[key]) return target[key];
const fns: Array<?Function> = [target[key], this[key]];
return function (...args) {
let val;
for (const fn of fns) {
if (fn) {
const ret = fn.apply(this, args);
if (ret != null) val = ret;
}
}
return val;
};
}
maybeInherit(loc: string) {
let inherits = this.take("inherits");
if (!inherits) return;
inherits = OptionManager.normalisePlugin(inherits, loc, "inherits");
this.manipulateOptions = this.chain(inherits, "manipulateOptions");
this.post = this.chain(inherits, "post");
this.pre = this.chain(inherits, "pre");
this.visitor = traverse.visitors.merge([inherits.visitor, this.visitor]);
}
/**
* We lazy initialise parts of a plugin that rely on contextual information such as
* position on disk and how it was specified.
*/
init(loc: string, i: number) {
if (this.initialized) return;
this.initialized = true;
this.maybeInherit(loc);
for (const key in this.raw) {
throw new Error(messages.get("pluginInvalidProperty", loc, i, key));
}
}
normaliseVisitor(visitor: Object): Object {
for (const key of GLOBAL_VISITOR_PROPS) {
if (visitor[key]) {
throw new Error("Plugins aren't allowed to specify catch-all enter/exit handlers. " +
"Please target individual nodes.");
}
}
traverse.explode(visitor);
return visitor;
}
}