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 = [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; } }