91 lines
2.3 KiB
JavaScript
91 lines
2.3 KiB
JavaScript
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.manipulateOptions = this.take("manipulateOptions");
|
|
this.post = this.take("post");
|
|
this.pre = this.take("pre");
|
|
this.visitor = this.normaliseVisitor(clone(this.take("visitor")) || {});
|
|
}
|
|
|
|
initialized: boolean;
|
|
raw: Object;
|
|
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;
|
|
}
|
|
}
|