runtime.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688
  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 {RuntimeCondition} a first
  218. * @param {RuntimeCondition} b second
  219. * @param {RuntimeSpec} runtime full runtime
  220. * @returns {RuntimeCondition} result
  221. */
  222. exports.mergeRuntimeCondition = (a, b, runtime) => {
  223. if (a === false) return b;
  224. if (b === false) return a;
  225. if (a === true || b === true) return true;
  226. const merged = mergeRuntime(a, b);
  227. if (merged === undefined) return undefined;
  228. if (typeof merged === "string") {
  229. if (typeof runtime === "string" && merged === runtime) return true;
  230. return merged;
  231. }
  232. if (typeof runtime === "string" || runtime === undefined) return merged;
  233. if (merged.size === runtime.size) return true;
  234. return merged;
  235. };
  236. /**
  237. * @param {RuntimeSpec | true} a first
  238. * @param {RuntimeSpec | true} b second
  239. * @param {RuntimeSpec} runtime full runtime
  240. * @returns {RuntimeSpec | true} result
  241. */
  242. exports.mergeRuntimeConditionNonFalse = (a, b, runtime) => {
  243. if (a === true || b === true) return true;
  244. const merged = mergeRuntime(a, b);
  245. if (merged === undefined) return undefined;
  246. if (typeof merged === "string") {
  247. if (typeof runtime === "string" && merged === runtime) return true;
  248. return merged;
  249. }
  250. if (typeof runtime === "string" || runtime === undefined) return merged;
  251. if (merged.size === runtime.size) return true;
  252. return merged;
  253. };
  254. /**
  255. * @param {RuntimeSpec} a first (may be modified)
  256. * @param {RuntimeSpec} b second
  257. * @returns {RuntimeSpec} merged
  258. */
  259. const mergeRuntimeOwned = (a, b) => {
  260. if (b === undefined) {
  261. return a;
  262. } else if (a === b) {
  263. return a;
  264. } else if (a === undefined) {
  265. if (typeof b === "string") {
  266. return b;
  267. } else {
  268. return new SortableSet(b);
  269. }
  270. } else if (typeof a === "string") {
  271. if (typeof b === "string") {
  272. const set = new SortableSet();
  273. set.add(a);
  274. set.add(b);
  275. return set;
  276. } else {
  277. const set = new SortableSet(b);
  278. set.add(a);
  279. return set;
  280. }
  281. } else {
  282. if (typeof b === "string") {
  283. a.add(b);
  284. return a;
  285. } else {
  286. for (const item of b) a.add(item);
  287. return a;
  288. }
  289. }
  290. };
  291. exports.mergeRuntimeOwned = mergeRuntimeOwned;
  292. /**
  293. * @param {RuntimeSpec} a first
  294. * @param {RuntimeSpec} b second
  295. * @returns {RuntimeSpec} merged
  296. */
  297. exports.intersectRuntime = (a, b) => {
  298. if (a === undefined) {
  299. return b;
  300. } else if (b === undefined) {
  301. return a;
  302. } else if (a === b) {
  303. return a;
  304. } else if (typeof a === "string") {
  305. if (typeof b === "string") {
  306. return undefined;
  307. } else if (b.has(a)) {
  308. return a;
  309. } else {
  310. return undefined;
  311. }
  312. } else {
  313. if (typeof b === "string") {
  314. if (a.has(b)) return b;
  315. return undefined;
  316. } else {
  317. const set = new SortableSet();
  318. for (const item of b) {
  319. if (a.has(item)) set.add(item);
  320. }
  321. if (set.size === 0) return undefined;
  322. if (set.size === 1) for (const item of set) return item;
  323. return set;
  324. }
  325. }
  326. };
  327. /**
  328. * @param {RuntimeSpec} a first
  329. * @param {RuntimeSpec} b second
  330. * @returns {RuntimeSpec} result
  331. */
  332. const subtractRuntime = (a, b) => {
  333. if (a === undefined) {
  334. return undefined;
  335. } else if (b === undefined) {
  336. return a;
  337. } else if (a === b) {
  338. return undefined;
  339. } else if (typeof a === "string") {
  340. if (typeof b === "string") {
  341. return a;
  342. } else if (b.has(a)) {
  343. return undefined;
  344. } else {
  345. return a;
  346. }
  347. } else {
  348. if (typeof b === "string") {
  349. if (!a.has(b)) return a;
  350. if (a.size === 2) {
  351. for (const item of a) {
  352. if (item !== b) return item;
  353. }
  354. }
  355. const set = new SortableSet(a);
  356. set.delete(b);
  357. return set;
  358. } else {
  359. const set = new SortableSet();
  360. for (const item of a) {
  361. if (!b.has(item)) set.add(item);
  362. }
  363. if (set.size === 0) return undefined;
  364. if (set.size === 1) for (const item of set) return item;
  365. return set;
  366. }
  367. }
  368. };
  369. exports.subtractRuntime = subtractRuntime;
  370. /**
  371. * @param {RuntimeCondition} a first
  372. * @param {RuntimeCondition} b second
  373. * @param {RuntimeSpec} runtime runtime
  374. * @returns {RuntimeCondition} result
  375. */
  376. exports.subtractRuntimeCondition = (a, b, runtime) => {
  377. if (b === true) return false;
  378. if (b === false) return a;
  379. if (a === false) return false;
  380. const result = subtractRuntime(a === true ? runtime : a, b);
  381. return result === undefined ? false : result;
  382. };
  383. /**
  384. * @param {RuntimeSpec} runtime runtime
  385. * @param {function(RuntimeSpec): boolean} filter filter function
  386. * @returns {boolean | RuntimeSpec} true/false if filter is constant for all runtimes, otherwise runtimes that are active
  387. */
  388. exports.filterRuntime = (runtime, filter) => {
  389. if (runtime === undefined) return filter(undefined);
  390. if (typeof runtime === "string") return filter(runtime);
  391. let some = false;
  392. let every = true;
  393. let result = undefined;
  394. for (const r of runtime) {
  395. const v = filter(r);
  396. if (v) {
  397. some = true;
  398. result = mergeRuntimeOwned(result, r);
  399. } else {
  400. every = false;
  401. }
  402. }
  403. if (!some) return false;
  404. if (every) return true;
  405. return result;
  406. };
  407. /**
  408. * @template T
  409. * @typedef {Map<string, T>} RuntimeSpecMapInnerMap
  410. */
  411. /**
  412. * @template T
  413. */
  414. class RuntimeSpecMap {
  415. /**
  416. * @param {RuntimeSpecMap<T>=} clone copy form this
  417. */
  418. constructor(clone) {
  419. this._mode = clone ? clone._mode : 0; // 0 = empty, 1 = single entry, 2 = map
  420. /** @type {RuntimeSpec} */
  421. this._singleRuntime = clone ? clone._singleRuntime : undefined;
  422. /** @type {T | undefined} */
  423. this._singleValue = clone ? clone._singleValue : undefined;
  424. /** @type {RuntimeSpecMapInnerMap<T> | undefined} */
  425. this._map = clone && clone._map ? new Map(clone._map) : undefined;
  426. }
  427. /**
  428. * @param {RuntimeSpec} runtime the runtimes
  429. * @returns {T | undefined} value
  430. */
  431. get(runtime) {
  432. switch (this._mode) {
  433. case 0:
  434. return undefined;
  435. case 1:
  436. return runtimeEqual(this._singleRuntime, runtime)
  437. ? this._singleValue
  438. : undefined;
  439. default:
  440. return /** @type {RuntimeSpecMapInnerMap<T>} */ (this._map).get(
  441. getRuntimeKey(runtime)
  442. );
  443. }
  444. }
  445. /**
  446. * @param {RuntimeSpec} runtime the runtimes
  447. * @returns {boolean} true, when the runtime is stored
  448. */
  449. has(runtime) {
  450. switch (this._mode) {
  451. case 0:
  452. return false;
  453. case 1:
  454. return runtimeEqual(this._singleRuntime, runtime);
  455. default:
  456. return /** @type {RuntimeSpecMapInnerMap<T>} */ (this._map).has(
  457. getRuntimeKey(runtime)
  458. );
  459. }
  460. }
  461. /**
  462. * @param {RuntimeSpec} runtime the runtimes
  463. * @param {T} value the value
  464. */
  465. set(runtime, value) {
  466. switch (this._mode) {
  467. case 0:
  468. this._mode = 1;
  469. this._singleRuntime = runtime;
  470. this._singleValue = value;
  471. break;
  472. case 1:
  473. if (runtimeEqual(this._singleRuntime, runtime)) {
  474. this._singleValue = value;
  475. break;
  476. }
  477. this._mode = 2;
  478. this._map = new Map();
  479. this._map.set(
  480. getRuntimeKey(this._singleRuntime),
  481. /** @type {T} */ (this._singleValue)
  482. );
  483. this._singleRuntime = undefined;
  484. this._singleValue = undefined;
  485. /* falls through */
  486. default:
  487. /** @type {RuntimeSpecMapInnerMap<T>} */
  488. (this._map).set(getRuntimeKey(runtime), value);
  489. }
  490. }
  491. /**
  492. * @param {RuntimeSpec} runtime the runtimes
  493. * @param {() => TODO} computer function to compute the value
  494. * @returns {TODO} true, when the runtime was deleted
  495. */
  496. provide(runtime, computer) {
  497. switch (this._mode) {
  498. case 0:
  499. this._mode = 1;
  500. this._singleRuntime = runtime;
  501. return (this._singleValue = computer());
  502. case 1: {
  503. if (runtimeEqual(this._singleRuntime, runtime)) {
  504. return /** @type {T} */ (this._singleValue);
  505. }
  506. this._mode = 2;
  507. this._map = new Map();
  508. this._map.set(
  509. getRuntimeKey(this._singleRuntime),
  510. /** @type {T} */ (this._singleValue)
  511. );
  512. this._singleRuntime = undefined;
  513. this._singleValue = undefined;
  514. const newValue = computer();
  515. this._map.set(getRuntimeKey(runtime), newValue);
  516. return newValue;
  517. }
  518. default: {
  519. const key = getRuntimeKey(runtime);
  520. const value = /** @type {Map<string, T>} */ (this._map).get(key);
  521. if (value !== undefined) return value;
  522. const newValue = computer();
  523. /** @type {Map<string, T>} */
  524. (this._map).set(key, newValue);
  525. return newValue;
  526. }
  527. }
  528. }
  529. /**
  530. * @param {RuntimeSpec} runtime the runtimes
  531. */
  532. delete(runtime) {
  533. switch (this._mode) {
  534. case 0:
  535. return;
  536. case 1:
  537. if (runtimeEqual(this._singleRuntime, runtime)) {
  538. this._mode = 0;
  539. this._singleRuntime = undefined;
  540. this._singleValue = undefined;
  541. }
  542. return;
  543. default:
  544. /** @type {RuntimeSpecMapInnerMap<T>} */
  545. (this._map).delete(getRuntimeKey(runtime));
  546. }
  547. }
  548. /**
  549. * @param {RuntimeSpec} runtime the runtimes
  550. * @param {function(T | undefined): T} fn function to update the value
  551. */
  552. update(runtime, fn) {
  553. switch (this._mode) {
  554. case 0:
  555. throw new Error("runtime passed to update must exist");
  556. case 1: {
  557. if (runtimeEqual(this._singleRuntime, runtime)) {
  558. this._singleValue = fn(this._singleValue);
  559. break;
  560. }
  561. const newValue = fn(undefined);
  562. if (newValue !== undefined) {
  563. this._mode = 2;
  564. this._map = new Map();
  565. this._map.set(
  566. getRuntimeKey(this._singleRuntime),
  567. /** @type {T} */ (this._singleValue)
  568. );
  569. this._singleRuntime = undefined;
  570. this._singleValue = undefined;
  571. this._map.set(getRuntimeKey(runtime), newValue);
  572. }
  573. break;
  574. }
  575. default: {
  576. const key = getRuntimeKey(runtime);
  577. const oldValue = /** @type {Map<string, T>} */ (this._map).get(key);
  578. const newValue = fn(oldValue);
  579. if (newValue !== oldValue)
  580. /** @type {RuntimeSpecMapInnerMap<T>} */
  581. (this._map).set(key, newValue);
  582. }
  583. }
  584. }
  585. keys() {
  586. switch (this._mode) {
  587. case 0:
  588. return [];
  589. case 1:
  590. return [this._singleRuntime];
  591. default:
  592. return Array.from(
  593. /** @type {RuntimeSpecMapInnerMap<T>} */
  594. (this._map).keys(),
  595. keyToRuntime
  596. );
  597. }
  598. }
  599. values() {
  600. switch (this._mode) {
  601. case 0:
  602. return [][Symbol.iterator]();
  603. case 1:
  604. return [/** @type {T} */ (this._singleValue)][Symbol.iterator]();
  605. default:
  606. return /** @type {Map<string, T>} */ (this._map).values();
  607. }
  608. }
  609. get size() {
  610. if (/** @type {number} */ (this._mode) <= 1) return this._mode;
  611. return /** @type {Map<string, T>} */ (this._map).size;
  612. }
  613. }
  614. exports.RuntimeSpecMap = RuntimeSpecMap;
  615. class RuntimeSpecSet {
  616. /**
  617. * @param {Iterable<RuntimeSpec>=} iterable iterable
  618. */
  619. constructor(iterable) {
  620. /** @type {Map<string, RuntimeSpec>} */
  621. this._map = new Map();
  622. if (iterable) {
  623. for (const item of iterable) {
  624. this.add(item);
  625. }
  626. }
  627. }
  628. /**
  629. * @param {RuntimeSpec} runtime runtime
  630. */
  631. add(runtime) {
  632. this._map.set(getRuntimeKey(runtime), runtime);
  633. }
  634. /**
  635. * @param {RuntimeSpec} runtime runtime
  636. * @returns {boolean} true, when the runtime exists
  637. */
  638. has(runtime) {
  639. return this._map.has(getRuntimeKey(runtime));
  640. }
  641. [Symbol.iterator]() {
  642. return this._map.values();
  643. }
  644. get size() {
  645. return this._map.size;
  646. }
  647. }
  648. exports.RuntimeSpecSet = RuntimeSpecSet;