| /* |
| MIT License http://www.opensource.org/licenses/mit-license.php |
| Author Tobias Koppers @sokra |
| */ |
| "use strict"; |
| |
| const util = require("util"); |
| |
| const deprecateContext = util.deprecate(() => {}, |
| "Hook.context is deprecated and will be removed"); |
| |
| const CALL_DELEGATE = function(...args) { |
| this.call = this._createCall("sync"); |
| return this.call(...args); |
| }; |
| const CALL_ASYNC_DELEGATE = function(...args) { |
| this.callAsync = this._createCall("async"); |
| return this.callAsync(...args); |
| }; |
| const PROMISE_DELEGATE = function(...args) { |
| this.promise = this._createCall("promise"); |
| return this.promise(...args); |
| }; |
| |
| class Hook { |
| constructor(args = [], name = undefined) { |
| this._args = args; |
| this.name = name; |
| this.taps = []; |
| this.interceptors = []; |
| this._call = CALL_DELEGATE; |
| this.call = CALL_DELEGATE; |
| this._callAsync = CALL_ASYNC_DELEGATE; |
| this.callAsync = CALL_ASYNC_DELEGATE; |
| this._promise = PROMISE_DELEGATE; |
| this.promise = PROMISE_DELEGATE; |
| this._x = undefined; |
| |
| this.compile = this.compile; |
| this.tap = this.tap; |
| this.tapAsync = this.tapAsync; |
| this.tapPromise = this.tapPromise; |
| } |
| |
| compile(options) { |
| throw new Error("Abstract: should be overridden"); |
| } |
| |
| _createCall(type) { |
| return this.compile({ |
| taps: this.taps, |
| interceptors: this.interceptors, |
| args: this._args, |
| type: type |
| }); |
| } |
| |
| _tap(type, options, fn) { |
| if (typeof options === "string") { |
| options = { |
| name: options.trim() |
| }; |
| } else if (typeof options !== "object" || options === null) { |
| throw new Error("Invalid tap options"); |
| } |
| if (typeof options.name !== "string" || options.name === "") { |
| throw new Error("Missing name for tap"); |
| } |
| if (typeof options.context !== "undefined") { |
| deprecateContext(); |
| } |
| options = Object.assign({ type, fn }, options); |
| options = this._runRegisterInterceptors(options); |
| this._insert(options); |
| } |
| |
| tap(options, fn) { |
| this._tap("sync", options, fn); |
| } |
| |
| tapAsync(options, fn) { |
| this._tap("async", options, fn); |
| } |
| |
| tapPromise(options, fn) { |
| this._tap("promise", options, fn); |
| } |
| |
| _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); |
| |
| return { |
| name: this.name, |
| tap: (opt, fn) => this.tap(mergeOptions(opt), fn), |
| tapAsync: (opt, fn) => this.tapAsync(mergeOptions(opt), fn), |
| tapPromise: (opt, fn) => this.tapPromise(mergeOptions(opt), fn), |
| intercept: interceptor => this.intercept(interceptor), |
| isUsed: () => this.isUsed(), |
| withOptions: opt => this.withOptions(mergeOptions(opt)) |
| }; |
| } |
| |
| 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; |
| } |
| } |
| |
| Object.setPrototypeOf(Hook.prototype, null); |
| |
| module.exports = Hook; |