/* MIT License http://www.opensource.org/licenses/mit-license.php Author Tobias Koppers @sokra */ "use strict"; class Hook { constructor(args) { if (!Array.isArray(args)) args = []; this._args = args; this.taps = []; this.interceptors = []; this.call = this._call; this.promise = this._promise; this.callAsync = this._callAsync; this._x = undefined; } compile(options) { throw new Error("Abstract: should be overriden"); } _createCall(type) { return this.compile({ taps: this.taps, interceptors: this.interceptors, args: this._args, type: type }); } tap(options, fn) { if (typeof options === "string") options = { name: options }; if (typeof options !== "object" || options === null) throw new Error( "Invalid arguments to tap(options: Object, fn: function)" ); options = Object.assign({ type: "sync", fn: fn }, options); if (typeof options.name !== "string" || options.name === "") throw new Error("Missing name for tap"); options = this._runRegisterInterceptors(options); this._insert(options); } tapAsync(options, fn) { if (typeof options === "string") options = { name: options }; if (typeof options !== "object" || options === null) throw new Error( "Invalid arguments to tapAsync(options: Object, fn: function)" ); options = Object.assign({ type: "async", fn: fn }, options); if (typeof options.name !== "string" || options.name === "") throw new Error("Missing name for tapAsync"); options = this._runRegisterInterceptors(options); this._insert(options); } tapPromise(options, fn) { if (typeof options === "string") options = { name: options }; if (typeof options !== "object" || options === null) throw new Error( "Invalid arguments to tapPromise(options: Object, fn: function)" ); options = Object.assign({ type: "promise", fn: fn }, options); if (typeof options.name !== "string" || options.name === "") throw new Error("Missing name for tapPromise"); options = this._runRegisterInterceptors(options); this._insert(options); } _runRegisterInterceptors(options) { for (const interceptor of this.interceptors) { if (interceptor.register) { const newOptions = interceptor.register(options); if (newOptions !== undefined) options = newOptions; } } return options; } withOptions(options) { const mergeOptions = opt => Object.assign({}, options, typeof opt === "string" ? { name: opt } : opt); // Prevent creating endless prototype chains options = Object.assign({}, options, this._withOptions); const base = this._withOptionsBase || this; const newHook = Object.create(base); (newHook.tapAsync = (opt, fn) => base.tapAsync(mergeOptions(opt), fn)), (newHook.tap = (opt, fn) => base.tap(mergeOptions(opt), fn)); newHook.tapPromise = (opt, fn) => base.tapPromise(mergeOptions(opt), fn); newHook._withOptions = options; newHook._withOptionsBase = base; return newHook; } isUsed() { return this.taps.length > 0 || this.interceptors.length > 0; } intercept(interceptor) { this._resetCompilation(); this.interceptors.push(Object.assign({}, interceptor)); if (interceptor.register) { for (let i = 0; i < this.taps.length; i++) this.taps[i] = interceptor.register(this.taps[i]); } } _resetCompilation() { this.call = this._call; this.callAsync = this._callAsync; this.promise = this._promise; } _insert(item) { this._resetCompilation(); let before; if (typeof item.before === "string") before = new Set([item.before]); else if (Array.isArray(item.before)) { before = new Set(item.before); } let stage = 0; if (typeof item.stage === "number") stage = item.stage; let i = this.taps.length; while (i > 0) { i--; const x = this.taps[i]; this.taps[i + 1] = x; const xStage = x.stage || 0; if (before) { if (before.has(x.name)) { before.delete(x.name); continue; } if (before.size > 0) { continue; } } if (xStage > stage) { continue; } i++; break; } this.taps[i] = item; } } function createCompileDelegate(name, type) { return function lazyCompileHook(...args) { this[name] = this._createCall(type); return this[name](...args); }; } Object.defineProperties(Hook.prototype, { _call: { value: createCompileDelegate("call", "sync"), configurable: true, writable: true }, _promise: { value: createCompileDelegate("promise", "promise"), configurable: true, writable: true }, _callAsync: { value: createCompileDelegate("callAsync", "async"), configurable: true, writable: true } }); module.exports = Hook;