UNPKG

14.6 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const SortableSet = require("./SortableSet");
9
10/** @typedef {import("../Compilation")} Compilation */
11/** @typedef {import("../Entrypoint").EntryOptions} EntryOptions */
12
13/** @typedef {string | SortableSet<string> | undefined} RuntimeSpec */
14/** @typedef {RuntimeSpec | boolean} RuntimeCondition */
15
16/**
17 * @param {Compilation} compilation the compilation
18 * @param {string} name name of the entry
19 * @param {EntryOptions=} options optionally already received entry options
20 * @returns {RuntimeSpec} runtime
21 */
22exports.getEntryRuntime = (compilation, name, options) => {
23 let dependOn;
24 let runtime;
25 if (options) {
26 ({ dependOn, runtime } = options);
27 } else {
28 const entry = compilation.entries.get(name);
29 if (!entry) return name;
30 ({ dependOn, runtime } = entry.options);
31 }
32 if (dependOn) {
33 /** @type {RuntimeSpec} */
34 let result = undefined;
35 const queue = new Set(dependOn);
36 for (const name of queue) {
37 const dep = compilation.entries.get(name);
38 if (!dep) continue;
39 const { dependOn, runtime } = dep.options;
40 if (dependOn) {
41 for (const name of dependOn) {
42 queue.add(name);
43 }
44 } else {
45 result = mergeRuntimeOwned(result, runtime || name);
46 }
47 }
48 return result || name;
49 } else {
50 return runtime || name;
51 }
52};
53
54/**
55 * @param {RuntimeSpec} runtime runtime
56 * @param {function(string): void} fn functor
57 * @param {boolean} deterministicOrder enforce a deterministic order
58 * @returns {void}
59 */
60exports.forEachRuntime = (runtime, fn, deterministicOrder = false) => {
61 if (runtime === undefined) {
62 fn(undefined);
63 } else if (typeof runtime === "string") {
64 fn(runtime);
65 } else {
66 if (deterministicOrder) runtime.sort();
67 for (const r of runtime) {
68 fn(r);
69 }
70 }
71};
72
73const getRuntimesKey = set => {
74 set.sort();
75 return Array.from(set).join("\n");
76};
77
78/**
79 * @param {RuntimeSpec} runtime runtime(s)
80 * @returns {string} key of runtimes
81 */
82const getRuntimeKey = runtime => {
83 if (runtime === undefined) return "*";
84 if (typeof runtime === "string") return runtime;
85 return runtime.getFromUnorderedCache(getRuntimesKey);
86};
87exports.getRuntimeKey = getRuntimeKey;
88
89/**
90 * @param {string} key key of runtimes
91 * @returns {RuntimeSpec} runtime(s)
92 */
93const keyToRuntime = key => {
94 if (key === "*") return undefined;
95 const items = key.split("\n");
96 if (items.length === 1) return items[0];
97 return new SortableSet(items);
98};
99exports.keyToRuntime = keyToRuntime;
100
101const getRuntimesString = set => {
102 set.sort();
103 return Array.from(set).join("+");
104};
105
106/**
107 * @param {RuntimeSpec} runtime runtime(s)
108 * @returns {string} readable version
109 */
110const runtimeToString = runtime => {
111 if (runtime === undefined) return "*";
112 if (typeof runtime === "string") return runtime;
113 return runtime.getFromUnorderedCache(getRuntimesString);
114};
115exports.runtimeToString = runtimeToString;
116
117/**
118 * @param {RuntimeCondition} runtimeCondition runtime condition
119 * @returns {string} readable version
120 */
121exports.runtimeConditionToString = runtimeCondition => {
122 if (runtimeCondition === true) return "true";
123 if (runtimeCondition === false) return "false";
124 return runtimeToString(runtimeCondition);
125};
126
127/**
128 * @param {RuntimeSpec} a first
129 * @param {RuntimeSpec} b second
130 * @returns {boolean} true, when they are equal
131 */
132const runtimeEqual = (a, b) => {
133 if (a === b) {
134 return true;
135 } else if (
136 a === undefined ||
137 b === undefined ||
138 typeof a === "string" ||
139 typeof b === "string"
140 ) {
141 return false;
142 } else if (a.size !== b.size) {
143 return false;
144 } else {
145 a.sort();
146 b.sort();
147 const aIt = a[Symbol.iterator]();
148 const bIt = b[Symbol.iterator]();
149 for (;;) {
150 const aV = aIt.next();
151 if (aV.done) return true;
152 const bV = bIt.next();
153 if (aV.value !== bV.value) return false;
154 }
155 }
156};
157exports.runtimeEqual = runtimeEqual;
158
159/**
160 * @param {RuntimeSpec} a first
161 * @param {RuntimeSpec} b second
162 * @returns {-1|0|1} compare
163 */
164exports.compareRuntime = (a, b) => {
165 if (a === b) {
166 return 0;
167 } else if (a === undefined) {
168 return -1;
169 } else if (b === undefined) {
170 return 1;
171 } else {
172 const aKey = getRuntimeKey(a);
173 const bKey = getRuntimeKey(b);
174 if (aKey < bKey) return -1;
175 if (aKey > bKey) return 1;
176 return 0;
177 }
178};
179
180/**
181 * @param {RuntimeSpec} a first
182 * @param {RuntimeSpec} b second
183 * @returns {RuntimeSpec} merged
184 */
185const mergeRuntime = (a, b) => {
186 if (a === undefined) {
187 return b;
188 } else if (b === undefined) {
189 return a;
190 } else if (a === b) {
191 return a;
192 } else if (typeof a === "string") {
193 if (typeof b === "string") {
194 const set = new SortableSet();
195 set.add(a);
196 set.add(b);
197 return set;
198 } else if (b.has(a)) {
199 return b;
200 } else {
201 const set = new SortableSet(b);
202 set.add(a);
203 return set;
204 }
205 } else {
206 if (typeof b === "string") {
207 if (a.has(b)) return a;
208 const set = new SortableSet(a);
209 set.add(b);
210 return set;
211 } else {
212 const set = new SortableSet(a);
213 for (const item of b) set.add(item);
214 if (set.size === a.size) return a;
215 return set;
216 }
217 }
218};
219exports.mergeRuntime = mergeRuntime;
220
221/**
222 * @param {RuntimeCondition} a first
223 * @param {RuntimeCondition} b second
224 * @param {RuntimeSpec} runtime full runtime
225 * @returns {RuntimeCondition} result
226 */
227exports.mergeRuntimeCondition = (a, b, runtime) => {
228 if (a === false) return b;
229 if (b === false) return a;
230 if (a === true || b === true) return true;
231 const merged = mergeRuntime(a, b);
232 if (merged === undefined) return undefined;
233 if (typeof merged === "string") {
234 if (typeof runtime === "string" && merged === runtime) return true;
235 return merged;
236 }
237 if (typeof runtime === "string" || runtime === undefined) return merged;
238 if (merged.size === runtime.size) return true;
239 return merged;
240};
241
242/**
243 * @param {RuntimeSpec | true} a first
244 * @param {RuntimeSpec | true} b second
245 * @param {RuntimeSpec} runtime full runtime
246 * @returns {RuntimeSpec | true} result
247 */
248exports.mergeRuntimeConditionNonFalse = (a, b, runtime) => {
249 if (a === true || b === true) return true;
250 const merged = mergeRuntime(a, b);
251 if (merged === undefined) return undefined;
252 if (typeof merged === "string") {
253 if (typeof runtime === "string" && merged === runtime) return true;
254 return merged;
255 }
256 if (typeof runtime === "string" || runtime === undefined) return merged;
257 if (merged.size === runtime.size) return true;
258 return merged;
259};
260
261/**
262 * @param {RuntimeSpec} a first (may be modified)
263 * @param {RuntimeSpec} b second
264 * @returns {RuntimeSpec} merged
265 */
266const mergeRuntimeOwned = (a, b) => {
267 if (b === undefined) {
268 return a;
269 } else if (a === b) {
270 return a;
271 } else if (a === undefined) {
272 if (typeof b === "string") {
273 return b;
274 } else {
275 return new SortableSet(b);
276 }
277 } else if (typeof a === "string") {
278 if (typeof b === "string") {
279 const set = new SortableSet();
280 set.add(a);
281 set.add(b);
282 return set;
283 } else {
284 const set = new SortableSet(b);
285 set.add(a);
286 return set;
287 }
288 } else {
289 if (typeof b === "string") {
290 a.add(b);
291 return a;
292 } else {
293 for (const item of b) a.add(item);
294 return a;
295 }
296 }
297};
298exports.mergeRuntimeOwned = mergeRuntimeOwned;
299
300/**
301 * @param {RuntimeSpec} a first
302 * @param {RuntimeSpec} b second
303 * @returns {RuntimeSpec} merged
304 */
305exports.intersectRuntime = (a, b) => {
306 if (a === undefined) {
307 return b;
308 } else if (b === undefined) {
309 return a;
310 } else if (a === b) {
311 return a;
312 } else if (typeof a === "string") {
313 if (typeof b === "string") {
314 return undefined;
315 } else if (b.has(a)) {
316 return a;
317 } else {
318 return undefined;
319 }
320 } else {
321 if (typeof b === "string") {
322 if (a.has(b)) return b;
323 return undefined;
324 } else {
325 const set = new SortableSet();
326 for (const item of b) {
327 if (a.has(item)) set.add(item);
328 }
329 if (set.size === 0) return undefined;
330 if (set.size === 1) for (const item of set) return item;
331 return set;
332 }
333 }
334};
335
336/**
337 * @param {RuntimeSpec} a first
338 * @param {RuntimeSpec} b second
339 * @returns {RuntimeSpec} result
340 */
341const subtractRuntime = (a, b) => {
342 if (a === undefined) {
343 return undefined;
344 } else if (b === undefined) {
345 return a;
346 } else if (a === b) {
347 return undefined;
348 } else if (typeof a === "string") {
349 if (typeof b === "string") {
350 return a;
351 } else if (b.has(a)) {
352 return undefined;
353 } else {
354 return a;
355 }
356 } else {
357 if (typeof b === "string") {
358 if (!a.has(b)) return a;
359 if (a.size === 2) {
360 for (const item of a) {
361 if (item !== b) return item;
362 }
363 }
364 const set = new SortableSet(a);
365 set.delete(b);
366 } else {
367 const set = new SortableSet();
368 for (const item of a) {
369 if (!b.has(item)) set.add(item);
370 }
371 if (set.size === 0) return undefined;
372 if (set.size === 1) for (const item of set) return item;
373 return set;
374 }
375 }
376};
377exports.subtractRuntime = subtractRuntime;
378
379/**
380 * @param {RuntimeCondition} a first
381 * @param {RuntimeCondition} b second
382 * @param {RuntimeSpec} runtime runtime
383 * @returns {RuntimeCondition} result
384 */
385exports.subtractRuntimeCondition = (a, b, runtime) => {
386 if (b === true) return false;
387 if (b === false) return a;
388 if (a === false) return false;
389 const result = subtractRuntime(a === true ? runtime : a, b);
390 return result === undefined ? false : result;
391};
392
393/**
394 * @param {RuntimeSpec} runtime runtime
395 * @param {function(RuntimeSpec): boolean} filter filter function
396 * @returns {boolean | RuntimeSpec} true/false if filter is constant for all runtimes, otherwise runtimes that are active
397 */
398exports.filterRuntime = (runtime, filter) => {
399 if (runtime === undefined) return filter(undefined);
400 if (typeof runtime === "string") return filter(runtime);
401 let some = false;
402 let every = true;
403 let result = undefined;
404 for (const r of runtime) {
405 const v = filter(r);
406 if (v) {
407 some = true;
408 result = mergeRuntimeOwned(result, r);
409 } else {
410 every = false;
411 }
412 }
413 if (!some) return false;
414 if (every) return true;
415 return result;
416};
417
418/**
419 * @template T
420 */
421class RuntimeSpecMap {
422 /**
423 * @param {RuntimeSpecMap<T>=} clone copy form this
424 */
425 constructor(clone) {
426 this._mode = clone ? clone._mode : 0; // 0 = empty, 1 = single entry, 2 = map
427 /** @type {RuntimeSpec} */
428 this._singleRuntime = clone ? clone._singleRuntime : undefined;
429 /** @type {T} */
430 this._singleValue = clone ? clone._singleValue : undefined;
431 /** @type {Map<string, T> | undefined} */
432 this._map = clone && clone._map ? new Map(clone._map) : undefined;
433 }
434
435 /**
436 * @param {RuntimeSpec} runtime the runtimes
437 * @returns {T} value
438 */
439 get(runtime) {
440 switch (this._mode) {
441 case 0:
442 return undefined;
443 case 1:
444 return runtimeEqual(this._singleRuntime, runtime)
445 ? this._singleValue
446 : undefined;
447 default:
448 return this._map.get(getRuntimeKey(runtime));
449 }
450 }
451
452 /**
453 * @param {RuntimeSpec} runtime the runtimes
454 * @returns {boolean} true, when the runtime is stored
455 */
456 has(runtime) {
457 switch (this._mode) {
458 case 0:
459 return false;
460 case 1:
461 return runtimeEqual(this._singleRuntime, runtime);
462 default:
463 return this._map.has(getRuntimeKey(runtime));
464 }
465 }
466
467 set(runtime, value) {
468 switch (this._mode) {
469 case 0:
470 this._mode = 1;
471 this._singleRuntime = runtime;
472 this._singleValue = value;
473 break;
474 case 1:
475 if (runtimeEqual(this._singleRuntime, runtime)) {
476 this._singleValue = value;
477 break;
478 }
479 this._mode = 2;
480 this._map = new Map();
481 this._map.set(getRuntimeKey(this._singleRuntime), this._singleValue);
482 this._singleRuntime = undefined;
483 this._singleValue = undefined;
484 /* falls through */
485 default:
486 this._map.set(getRuntimeKey(runtime), value);
487 }
488 }
489
490 provide(runtime, computer) {
491 switch (this._mode) {
492 case 0:
493 this._mode = 1;
494 this._singleRuntime = runtime;
495 return (this._singleValue = computer());
496 case 1: {
497 if (runtimeEqual(this._singleRuntime, runtime)) {
498 return this._singleValue;
499 }
500 this._mode = 2;
501 this._map = new Map();
502 this._map.set(getRuntimeKey(this._singleRuntime), this._singleValue);
503 this._singleRuntime = undefined;
504 this._singleValue = undefined;
505 const newValue = computer();
506 this._map.set(getRuntimeKey(runtime), newValue);
507 return newValue;
508 }
509 default: {
510 const key = getRuntimeKey(runtime);
511 const value = this._map.get(key);
512 if (value !== undefined) return value;
513 const newValue = computer();
514 this._map.set(key, newValue);
515 return newValue;
516 }
517 }
518 }
519
520 delete(runtime) {
521 switch (this._mode) {
522 case 0:
523 return;
524 case 1:
525 if (runtimeEqual(this._singleRuntime, runtime)) {
526 this._mode = 0;
527 this._singleRuntime = undefined;
528 this._singleValue = undefined;
529 }
530 return;
531 default:
532 this._map.delete(getRuntimeKey(runtime));
533 }
534 }
535
536 update(runtime, fn) {
537 switch (this._mode) {
538 case 0:
539 throw new Error("runtime passed to update must exist");
540 case 1: {
541 if (runtimeEqual(this._singleRuntime, runtime)) {
542 this._singleValue = fn(this._singleValue);
543 break;
544 }
545 const newValue = fn(undefined);
546 if (newValue !== undefined) {
547 this._mode = 2;
548 this._map = new Map();
549 this._map.set(getRuntimeKey(this._singleRuntime), this._singleValue);
550 this._singleRuntime = undefined;
551 this._singleValue = undefined;
552 this._map.set(getRuntimeKey(runtime), newValue);
553 }
554 break;
555 }
556 default: {
557 const key = getRuntimeKey(runtime);
558 const oldValue = this._map.get(key);
559 const newValue = fn(oldValue);
560 if (newValue !== oldValue) this._map.set(key, newValue);
561 }
562 }
563 }
564
565 keys() {
566 switch (this._mode) {
567 case 0:
568 return [];
569 case 1:
570 return [this._singleRuntime];
571 default:
572 return Array.from(this._map.keys(), keyToRuntime);
573 }
574 }
575
576 values() {
577 switch (this._mode) {
578 case 0:
579 return [][Symbol.iterator]();
580 case 1:
581 return [this._singleValue][Symbol.iterator]();
582 default:
583 return this._map.values();
584 }
585 }
586
587 get size() {
588 if (this._mode <= 1) return this._mode;
589 return this._map.size;
590 }
591}
592
593exports.RuntimeSpecMap = RuntimeSpecMap;
594
595class RuntimeSpecSet {
596 constructor(iterable) {
597 /** @type {Map<string, RuntimeSpec>} */
598 this._map = new Map();
599 if (iterable) {
600 for (const item of iterable) {
601 this.add(item);
602 }
603 }
604 }
605
606 add(runtime) {
607 this._map.set(getRuntimeKey(runtime), runtime);
608 }
609
610 has(runtime) {
611 return this._map.has(getRuntimeKey(runtime));
612 }
613
614 [Symbol.iterator]() {
615 return this._map.values();
616 }
617
618 get size() {
619 return this._map.size;
620 }
621}
622
623exports.RuntimeSpecSet = RuntimeSpecSet;