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