source: frontend/node_modules/tapable/lib/Hook.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 5.8 KB
RevLine 
[9af201e]1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5"use strict";
6
7const util = require("util");
8
9const deprecateContext = util.deprecate(
10 () => {},
11 "Hook.context is deprecated and will be removed"
12);
13
14function CALL_DELEGATE(...args) {
15 this.call = this._createCall("sync");
16 return this.call(...args);
17}
18
19function CALL_ASYNC_DELEGATE(...args) {
20 this.callAsync = this._createCall("async");
21 return this.callAsync(...args);
22}
23
24function PROMISE_DELEGATE(...args) {
25 this.promise = this._createCall("promise");
26 return this.promise(...args);
27}
28
29class Hook {
30 constructor(args = [], name = undefined) {
31 this._args = args;
32 this.name = name;
33 this.taps = [];
34 this.interceptors = [];
35 this._call = CALL_DELEGATE;
36 this.call = CALL_DELEGATE;
37 this._callAsync = CALL_ASYNC_DELEGATE;
38 this.callAsync = CALL_ASYNC_DELEGATE;
39 this._promise = PROMISE_DELEGATE;
40 this.promise = PROMISE_DELEGATE;
41 this._x = undefined;
42
43 // eslint-disable-next-line no-self-assign
44 this.compile = this.compile;
45 // eslint-disable-next-line no-self-assign
46 this.tap = this.tap;
47 // eslint-disable-next-line no-self-assign
48 this.tapAsync = this.tapAsync;
49 // eslint-disable-next-line no-self-assign
50 this.tapPromise = this.tapPromise;
51 }
52
53 compile(_options) {
54 throw new Error("Abstract: should be overridden");
55 }
56
57 _createCall(type) {
58 return this.compile({
59 taps: this.taps,
60 interceptors: this.interceptors,
61 args: this._args,
62 type
63 });
64 }
65
66 _tap(type, options, fn) {
67 if (typeof options === "string") {
68 // Fast path: a string options ("name") is by far the most common
69 // case. Build the final descriptor in a single allocation instead
70 // of creating `{ name }` and then `Object.assign`ing it.
71 const name = options.trim();
72 if (name === "") {
73 throw new Error("Missing name for tap");
74 }
75 options = { type, fn, name };
76 } else {
77 if (typeof options !== "object" || options === null) {
78 throw new Error("Invalid tap options");
79 }
80 let { name } = options;
81 if (typeof name === "string") {
82 name = name.trim();
83 }
84 if (typeof name !== "string" || name === "") {
85 throw new Error("Missing name for tap");
86 }
87 if (typeof options.context !== "undefined") {
88 deprecateContext();
89 }
90 // Fast path: only `name` is set. Build the descriptor as a literal
91 // so `_insert` and downstream consumers see the same hidden class
92 // as the string-options path, avoiding a polymorphic call site.
93 // Scan with `for...in` (cheaper than allocating `Object.keys`)
94 // to verify no other user-provided properties exist - e.g.
95 // webpack's `additionalAssets` - otherwise they'd be dropped.
96 let onlyName = true;
97 for (const key in options) {
98 if (key !== "name") {
99 onlyName = false;
100 break;
101 }
102 }
103 if (onlyName) {
104 options = { type, fn, name };
105 } else {
106 options.name = name;
107 // Preserve previous precedence: user-provided keys win over the internal `type`/`fn`.
108 options = Object.assign({ type, fn }, options);
109 }
110 }
111 options = this._runRegisterInterceptors(options);
112 this._insert(options);
113 }
114
115 tap(options, fn) {
116 this._tap("sync", options, fn);
117 }
118
119 tapAsync(options, fn) {
120 this._tap("async", options, fn);
121 }
122
123 tapPromise(options, fn) {
124 this._tap("promise", options, fn);
125 }
126
127 _runRegisterInterceptors(options) {
128 const { interceptors } = this;
129 const { length } = interceptors;
130 // Common case: no interceptors.
131 if (length === 0) return options;
132 for (let i = 0; i < length; i++) {
133 const interceptor = interceptors[i];
134 if (interceptor.register) {
135 const newOptions = interceptor.register(options);
136 if (newOptions !== undefined) {
137 options = newOptions;
138 }
139 }
140 }
141 return options;
142 }
143
144 withOptions(options) {
145 const mergeOptions = (opt) =>
146 Object.assign({}, options, typeof opt === "string" ? { name: opt } : opt);
147
148 return {
149 name: this.name,
150 tap: (opt, fn) => this.tap(mergeOptions(opt), fn),
151 tapAsync: (opt, fn) => this.tapAsync(mergeOptions(opt), fn),
152 tapPromise: (opt, fn) => this.tapPromise(mergeOptions(opt), fn),
153 intercept: (interceptor) => this.intercept(interceptor),
154 isUsed: () => this.isUsed(),
155 withOptions: (opt) => this.withOptions(mergeOptions(opt))
156 };
157 }
158
159 isUsed() {
160 return this.taps.length > 0 || this.interceptors.length > 0;
161 }
162
163 intercept(interceptor) {
164 this._resetCompilation();
165 this.interceptors.push(Object.assign({}, interceptor));
166 if (interceptor.register) {
167 for (let i = 0; i < this.taps.length; i++) {
168 this.taps[i] = interceptor.register(this.taps[i]);
169 }
170 }
171 }
172
173 _resetCompilation() {
174 this.call = this._call;
175 this.callAsync = this._callAsync;
176 this.promise = this._promise;
177 }
178
179 _insert(item) {
180 this._resetCompilation();
181 const { taps } = this;
182 const stage = typeof item.stage === "number" ? item.stage : 0;
183
184 // Fast path: the overwhelmingly common `hook.tap("name", fn)` case
185 // has no `before` and default stage 0. If the list is empty or the
186 // last tap's stage is <= the new item's stage the item belongs at
187 // the end - append in O(1), skipping the Set allocation and the
188 // shift loop.
189 if (!(typeof item.before === "string" || Array.isArray(item.before))) {
190 const n = taps.length;
191 if (n === 0 || (taps[n - 1].stage || 0) <= stage) {
192 taps[n] = item;
193 return;
194 }
195 }
196
197 let before;
198
199 if (typeof item.before === "string") {
200 before = new Set([item.before]);
201 } else if (Array.isArray(item.before)) {
202 before = new Set(item.before);
203 }
204
205 let i = taps.length;
206
207 while (i > 0) {
208 i--;
209 const tap = taps[i];
210 taps[i + 1] = tap;
211 const xStage = tap.stage || 0;
212 if (before) {
213 if (before.has(tap.name)) {
214 before.delete(tap.name);
215 continue;
216 }
217 if (before.size > 0) {
218 continue;
219 }
220 }
221 if (xStage > stage) {
222 continue;
223 }
224 i++;
225 break;
226 }
227 taps[i] = item;
228 }
229}
230
231Object.setPrototypeOf(Hook.prototype, null);
232
233module.exports = Hook;
Note: See TracBrowser for help on using the repository browser.