Projekt

Obecné

Profil

Stáhnout (4.52 KB) Statistiky
| Větev: | Revize:
1
/*
2
	MIT License http://www.opensource.org/licenses/mit-license.php
3
	Author Tobias Koppers @sokra
4
*/
5
"use strict";
6

    
7
class Hook {
8
	constructor(args) {
9
		if (!Array.isArray(args)) args = [];
10
		this._args = args;
11
		this.taps = [];
12
		this.interceptors = [];
13
		this.call = this._call;
14
		this.promise = this._promise;
15
		this.callAsync = this._callAsync;
16
		this._x = undefined;
17
	}
18

    
19
	compile(options) {
20
		throw new Error("Abstract: should be overriden");
21
	}
22

    
23
	_createCall(type) {
24
		return this.compile({
25
			taps: this.taps,
26
			interceptors: this.interceptors,
27
			args: this._args,
28
			type: type
29
		});
30
	}
31

    
32
	tap(options, fn) {
33
		if (typeof options === "string") options = { name: options };
34
		if (typeof options !== "object" || options === null)
35
			throw new Error(
36
				"Invalid arguments to tap(options: Object, fn: function)"
37
			);
38
		options = Object.assign({ type: "sync", fn: fn }, options);
39
		if (typeof options.name !== "string" || options.name === "")
40
			throw new Error("Missing name for tap");
41
		options = this._runRegisterInterceptors(options);
42
		this._insert(options);
43
	}
44

    
45
	tapAsync(options, fn) {
46
		if (typeof options === "string") options = { name: options };
47
		if (typeof options !== "object" || options === null)
48
			throw new Error(
49
				"Invalid arguments to tapAsync(options: Object, fn: function)"
50
			);
51
		options = Object.assign({ type: "async", fn: fn }, options);
52
		if (typeof options.name !== "string" || options.name === "")
53
			throw new Error("Missing name for tapAsync");
54
		options = this._runRegisterInterceptors(options);
55
		this._insert(options);
56
	}
57

    
58
	tapPromise(options, fn) {
59
		if (typeof options === "string") options = { name: options };
60
		if (typeof options !== "object" || options === null)
61
			throw new Error(
62
				"Invalid arguments to tapPromise(options: Object, fn: function)"
63
			);
64
		options = Object.assign({ type: "promise", fn: fn }, options);
65
		if (typeof options.name !== "string" || options.name === "")
66
			throw new Error("Missing name for tapPromise");
67
		options = this._runRegisterInterceptors(options);
68
		this._insert(options);
69
	}
70

    
71
	_runRegisterInterceptors(options) {
72
		for (const interceptor of this.interceptors) {
73
			if (interceptor.register) {
74
				const newOptions = interceptor.register(options);
75
				if (newOptions !== undefined) options = newOptions;
76
			}
77
		}
78
		return options;
79
	}
80

    
81
	withOptions(options) {
82
		const mergeOptions = opt =>
83
			Object.assign({}, options, typeof opt === "string" ? { name: opt } : opt);
84

    
85
		// Prevent creating endless prototype chains
86
		options = Object.assign({}, options, this._withOptions);
87
		const base = this._withOptionsBase || this;
88
		const newHook = Object.create(base);
89

    
90
		(newHook.tapAsync = (opt, fn) => base.tapAsync(mergeOptions(opt), fn)),
91
			(newHook.tap = (opt, fn) => base.tap(mergeOptions(opt), fn));
92
		newHook.tapPromise = (opt, fn) => base.tapPromise(mergeOptions(opt), fn);
93
		newHook._withOptions = options;
94
		newHook._withOptionsBase = base;
95
		return newHook;
96
	}
97

    
98
	isUsed() {
99
		return this.taps.length > 0 || this.interceptors.length > 0;
100
	}
101

    
102
	intercept(interceptor) {
103
		this._resetCompilation();
104
		this.interceptors.push(Object.assign({}, interceptor));
105
		if (interceptor.register) {
106
			for (let i = 0; i < this.taps.length; i++)
107
				this.taps[i] = interceptor.register(this.taps[i]);
108
		}
109
	}
110

    
111
	_resetCompilation() {
112
		this.call = this._call;
113
		this.callAsync = this._callAsync;
114
		this.promise = this._promise;
115
	}
116

    
117
	_insert(item) {
118
		this._resetCompilation();
119
		let before;
120
		if (typeof item.before === "string") before = new Set([item.before]);
121
		else if (Array.isArray(item.before)) {
122
			before = new Set(item.before);
123
		}
124
		let stage = 0;
125
		if (typeof item.stage === "number") stage = item.stage;
126
		let i = this.taps.length;
127
		while (i > 0) {
128
			i--;
129
			const x = this.taps[i];
130
			this.taps[i + 1] = x;
131
			const xStage = x.stage || 0;
132
			if (before) {
133
				if (before.has(x.name)) {
134
					before.delete(x.name);
135
					continue;
136
				}
137
				if (before.size > 0) {
138
					continue;
139
				}
140
			}
141
			if (xStage > stage) {
142
				continue;
143
			}
144
			i++;
145
			break;
146
		}
147
		this.taps[i] = item;
148
	}
149
}
150

    
151
function createCompileDelegate(name, type) {
152
	return function lazyCompileHook(...args) {
153
		this[name] = this._createCall(type);
154
		return this[name](...args);
155
	};
156
}
157

    
158
Object.defineProperties(Hook.prototype, {
159
	_call: {
160
		value: createCompileDelegate("call", "sync"),
161
		configurable: true,
162
		writable: true
163
	},
164
	_promise: {
165
		value: createCompileDelegate("promise", "promise"),
166
		configurable: true,
167
		writable: true
168
	},
169
	_callAsync: {
170
		value: createCompileDelegate("callAsync", "async"),
171
		configurable: true,
172
		writable: true
173
	}
174
});
175

    
176
module.exports = Hook;
(7-7/16)