runtime.js 17 KB

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