runtime.js 16 KB

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