runtime.js 16 KB

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