runtime.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  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. exports.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. /**
  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. 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 undefined;
  91. const items = key.split("\n");
  92. if (items.length === 1) return items[0];
  93. return new SortableSet(items);
  94. };
  95. 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. exports.runtimeToString = runtimeToString;
  115. /**
  116. * @param {RuntimeCondition} runtimeCondition runtime condition
  117. * @returns {string} readable version
  118. */
  119. 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. } else {
  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. };
  154. exports.runtimeEqual = runtimeEqual;
  155. /**
  156. * @param {RuntimeSpec} a first
  157. * @param {RuntimeSpec} b second
  158. * @returns {-1|0|1} compare
  159. */
  160. exports.compareRuntime = (a, b) => {
  161. if (a === b) {
  162. return 0;
  163. } else if (a === undefined) {
  164. return -1;
  165. } else if (b === undefined) {
  166. return 1;
  167. } else {
  168. const aKey = getRuntimeKey(a);
  169. const bKey = getRuntimeKey(b);
  170. if (aKey < bKey) return -1;
  171. if (aKey > bKey) return 1;
  172. return 0;
  173. }
  174. };
  175. /**
  176. * @param {RuntimeSpec} a first
  177. * @param {RuntimeSpec} b second
  178. * @returns {RuntimeSpec} merged
  179. */
  180. const mergeRuntime = (a, b) => {
  181. if (a === undefined) {
  182. return b;
  183. } else if (b === undefined) {
  184. return a;
  185. } else if (a === b) {
  186. return a;
  187. } else if (typeof a === "string") {
  188. if (typeof b === "string") {
  189. const set = new SortableSet();
  190. set.add(a);
  191. set.add(b);
  192. return set;
  193. } else if (b.has(a)) {
  194. return b;
  195. } else {
  196. const set = new SortableSet(b);
  197. set.add(a);
  198. return set;
  199. }
  200. } else {
  201. if (typeof b === "string") {
  202. if (a.has(b)) return a;
  203. const set = new SortableSet(a);
  204. set.add(b);
  205. return set;
  206. } else {
  207. const set = new SortableSet(a);
  208. for (const item of b) set.add(item);
  209. if (set.size === a.size) return a;
  210. return set;
  211. }
  212. }
  213. };
  214. exports.mergeRuntime = mergeRuntime;
  215. /**
  216. * @param {RuntimeCondition} a first
  217. * @param {RuntimeCondition} b second
  218. * @param {RuntimeSpec} runtime full runtime
  219. * @returns {RuntimeCondition} result
  220. */
  221. exports.mergeRuntimeCondition = (a, b, runtime) => {
  222. if (a === false) return b;
  223. if (b === false) return a;
  224. if (a === true || b === true) return true;
  225. const merged = mergeRuntime(a, b);
  226. if (merged === undefined) return undefined;
  227. if (typeof merged === "string") {
  228. if (typeof runtime === "string" && merged === runtime) return true;
  229. return merged;
  230. }
  231. if (typeof runtime === "string" || runtime === undefined) return merged;
  232. if (merged.size === runtime.size) return true;
  233. return merged;
  234. };
  235. /**
  236. * @param {RuntimeSpec | true} a first
  237. * @param {RuntimeSpec | true} b second
  238. * @param {RuntimeSpec} runtime full runtime
  239. * @returns {RuntimeSpec | true} result
  240. */
  241. exports.mergeRuntimeConditionNonFalse = (a, b, runtime) => {
  242. if (a === true || b === true) return true;
  243. const merged = mergeRuntime(a, b);
  244. if (merged === undefined) return undefined;
  245. if (typeof merged === "string") {
  246. if (typeof runtime === "string" && merged === runtime) return true;
  247. return merged;
  248. }
  249. if (typeof runtime === "string" || runtime === undefined) return merged;
  250. if (merged.size === runtime.size) return true;
  251. return merged;
  252. };
  253. /**
  254. * @param {RuntimeSpec} a first (may be modified)
  255. * @param {RuntimeSpec} b second
  256. * @returns {RuntimeSpec} merged
  257. */
  258. const mergeRuntimeOwned = (a, b) => {
  259. if (b === undefined) {
  260. return a;
  261. } else if (a === b) {
  262. return a;
  263. } else if (a === undefined) {
  264. if (typeof b === "string") {
  265. return b;
  266. } else {
  267. return new SortableSet(b);
  268. }
  269. } else if (typeof a === "string") {
  270. if (typeof b === "string") {
  271. const set = new SortableSet();
  272. set.add(a);
  273. set.add(b);
  274. return set;
  275. } else {
  276. const set = new SortableSet(b);
  277. set.add(a);
  278. return set;
  279. }
  280. } else {
  281. if (typeof b === "string") {
  282. a.add(b);
  283. return a;
  284. } else {
  285. for (const item of b) a.add(item);
  286. return a;
  287. }
  288. }
  289. };
  290. exports.mergeRuntimeOwned = mergeRuntimeOwned;
  291. /**
  292. * @param {RuntimeSpec} a first
  293. * @param {RuntimeSpec} b second
  294. * @returns {RuntimeSpec} merged
  295. */
  296. exports.intersectRuntime = (a, b) => {
  297. if (a === undefined) {
  298. return b;
  299. } else if (b === undefined) {
  300. return a;
  301. } else if (a === b) {
  302. return a;
  303. } else if (typeof a === "string") {
  304. if (typeof b === "string") {
  305. return undefined;
  306. } else if (b.has(a)) {
  307. return a;
  308. } else {
  309. return undefined;
  310. }
  311. } else {
  312. if (typeof b === "string") {
  313. if (a.has(b)) return b;
  314. return undefined;
  315. } else {
  316. const set = new SortableSet();
  317. for (const item of b) {
  318. if (a.has(item)) set.add(item);
  319. }
  320. if (set.size === 0) return undefined;
  321. if (set.size === 1) for (const item of set) return item;
  322. return set;
  323. }
  324. }
  325. };
  326. /**
  327. * @param {RuntimeSpec} a first
  328. * @param {RuntimeSpec} b second
  329. * @returns {RuntimeSpec} result
  330. */
  331. const subtractRuntime = (a, b) => {
  332. if (a === undefined) {
  333. return undefined;
  334. } else if (b === undefined) {
  335. return a;
  336. } else if (a === b) {
  337. return undefined;
  338. } else if (typeof a === "string") {
  339. if (typeof b === "string") {
  340. return a;
  341. } else if (b.has(a)) {
  342. return undefined;
  343. } else {
  344. return a;
  345. }
  346. } else {
  347. if (typeof b === "string") {
  348. if (!a.has(b)) return a;
  349. if (a.size === 2) {
  350. for (const item of a) {
  351. if (item !== b) return item;
  352. }
  353. }
  354. const set = new SortableSet(a);
  355. set.delete(b);
  356. } else {
  357. const set = new SortableSet();
  358. for (const item of a) {
  359. if (!b.has(item)) set.add(item);
  360. }
  361. if (set.size === 0) return undefined;
  362. if (set.size === 1) for (const item of set) return item;
  363. return set;
  364. }
  365. }
  366. };
  367. exports.subtractRuntime = subtractRuntime;
  368. /**
  369. * @param {RuntimeCondition} a first
  370. * @param {RuntimeCondition} b second
  371. * @param {RuntimeSpec} runtime runtime
  372. * @returns {RuntimeCondition} result
  373. */
  374. exports.subtractRuntimeCondition = (a, b, runtime) => {
  375. if (b === true) return false;
  376. if (b === false) return a;
  377. if (a === false) return false;
  378. const result = subtractRuntime(a === true ? runtime : a, b);
  379. return result === undefined ? false : result;
  380. };
  381. /**
  382. * @param {RuntimeSpec} runtime runtime
  383. * @param {function(RuntimeSpec): boolean} filter filter function
  384. * @returns {boolean | RuntimeSpec} true/false if filter is constant for all runtimes, otherwise runtimes that are active
  385. */
  386. exports.filterRuntime = (runtime, filter) => {
  387. if (runtime === undefined) return filter(undefined);
  388. if (typeof runtime === "string") return filter(runtime);
  389. let some = false;
  390. let every = true;
  391. let result = undefined;
  392. for (const r of runtime) {
  393. const v = filter(r);
  394. if (v) {
  395. some = true;
  396. result = mergeRuntimeOwned(result, r);
  397. } else {
  398. every = false;
  399. }
  400. }
  401. if (!some) return false;
  402. if (every) return true;
  403. return result;
  404. };
  405. /**
  406. * @template T
  407. * @typedef {Map<string, T>} RuntimeSpecMapInnerMap
  408. * */
  409. /**
  410. * @template T
  411. */
  412. class RuntimeSpecMap {
  413. /**
  414. * @param {RuntimeSpecMap<T>=} clone copy form this
  415. */
  416. constructor(clone) {
  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 {T | undefined} */
  421. this._singleValue = clone ? clone._singleValue : undefined;
  422. /** @type {RuntimeSpecMapInnerMap<T> | undefined} */
  423. this._map = clone && clone._map ? new Map(clone._map) : undefined;
  424. }
  425. /**
  426. * @param {RuntimeSpec} runtime the runtimes
  427. * @returns {T | undefined} value
  428. */
  429. get(runtime) {
  430. switch (this._mode) {
  431. case 0:
  432. return undefined;
  433. case 1:
  434. return runtimeEqual(this._singleRuntime, runtime)
  435. ? this._singleValue
  436. : undefined;
  437. default:
  438. return /** @type {RuntimeSpecMapInnerMap<T>} */ (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<T>} */ (this._map).has(
  455. getRuntimeKey(runtime)
  456. );
  457. }
  458. }
  459. /**
  460. * @param {RuntimeSpec} runtime the runtimes
  461. * @param {T} 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 {T} */ (this._singleValue)
  480. );
  481. this._singleRuntime = undefined;
  482. this._singleValue = undefined;
  483. /* falls through */
  484. default:
  485. /** @type {RuntimeSpecMapInnerMap<T>} */
  486. (this._map).set(getRuntimeKey(runtime), value);
  487. }
  488. }
  489. /**
  490. * @param {RuntimeSpec} runtime the runtimes
  491. * @param {() => TODO} computer function to compute the value
  492. * @returns {TODO} true, when the runtime was deleted
  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 {T} */ (this._singleValue);
  503. }
  504. this._mode = 2;
  505. this._map = new Map();
  506. this._map.set(
  507. getRuntimeKey(this._singleRuntime),
  508. /** @type {T} */ (this._singleValue)
  509. );
  510. this._singleRuntime = undefined;
  511. this._singleValue = undefined;
  512. const newValue = computer();
  513. this._map.set(getRuntimeKey(runtime), newValue);
  514. return newValue;
  515. }
  516. default: {
  517. const key = getRuntimeKey(runtime);
  518. const value = /** @type {Map<string, T>} */ (this._map).get(key);
  519. if (value !== undefined) return value;
  520. const newValue = computer();
  521. /** @type {Map<string, T>} */
  522. (this._map).set(key, newValue);
  523. return newValue;
  524. }
  525. }
  526. }
  527. /**
  528. * @param {RuntimeSpec} runtime the runtimes
  529. */
  530. delete(runtime) {
  531. switch (this._mode) {
  532. case 0:
  533. return;
  534. case 1:
  535. if (runtimeEqual(this._singleRuntime, runtime)) {
  536. this._mode = 0;
  537. this._singleRuntime = undefined;
  538. this._singleValue = undefined;
  539. }
  540. return;
  541. default:
  542. /** @type {RuntimeSpecMapInnerMap<T>} */
  543. (this._map).delete(getRuntimeKey(runtime));
  544. }
  545. }
  546. /**
  547. * @param {RuntimeSpec} runtime the runtimes
  548. * @param {function(T | undefined): T} fn function to update the value
  549. */
  550. update(runtime, fn) {
  551. switch (this._mode) {
  552. case 0:
  553. throw new Error("runtime passed to update must exist");
  554. case 1: {
  555. if (runtimeEqual(this._singleRuntime, runtime)) {
  556. this._singleValue = fn(this._singleValue);
  557. break;
  558. }
  559. const newValue = fn(undefined);
  560. if (newValue !== undefined) {
  561. this._mode = 2;
  562. this._map = new Map();
  563. this._map.set(
  564. getRuntimeKey(this._singleRuntime),
  565. /** @type {T} */ (this._singleValue)
  566. );
  567. this._singleRuntime = undefined;
  568. this._singleValue = undefined;
  569. this._map.set(getRuntimeKey(runtime), newValue);
  570. }
  571. break;
  572. }
  573. default: {
  574. const key = getRuntimeKey(runtime);
  575. const oldValue = /** @type {Map<string, T>} */ (this._map).get(key);
  576. const newValue = fn(oldValue);
  577. if (newValue !== oldValue)
  578. /** @type {RuntimeSpecMapInnerMap<T>} */
  579. (this._map).set(key, newValue);
  580. }
  581. }
  582. }
  583. keys() {
  584. switch (this._mode) {
  585. case 0:
  586. return [];
  587. case 1:
  588. return [this._singleRuntime];
  589. default:
  590. return Array.from(
  591. /** @type {RuntimeSpecMapInnerMap<T>} */
  592. (this._map).keys(),
  593. keyToRuntime
  594. );
  595. }
  596. }
  597. values() {
  598. switch (this._mode) {
  599. case 0:
  600. return [][Symbol.iterator]();
  601. case 1:
  602. return [/** @type {T} */ (this._singleValue)][Symbol.iterator]();
  603. default:
  604. return /** @type {Map<string, T>} */ (this._map).values();
  605. }
  606. }
  607. get size() {
  608. if (/** @type {number} */ (this._mode) <= 1) return this._mode;
  609. return /** @type {Map<string, T>} */ (this._map).size;
  610. }
  611. }
  612. exports.RuntimeSpecMap = RuntimeSpecMap;
  613. class RuntimeSpecSet {
  614. /**
  615. * @param {Iterable<RuntimeSpec>=} iterable iterable
  616. */
  617. constructor(iterable) {
  618. /** @type {Map<string, RuntimeSpec>} */
  619. this._map = new Map();
  620. if (iterable) {
  621. for (const item of iterable) {
  622. this.add(item);
  623. }
  624. }
  625. }
  626. /**
  627. * @param {RuntimeSpec} runtime runtime
  628. */
  629. add(runtime) {
  630. this._map.set(getRuntimeKey(runtime), runtime);
  631. }
  632. /**
  633. * @param {RuntimeSpec} runtime runtime
  634. * @returns {boolean} true, when the runtime exists
  635. */
  636. has(runtime) {
  637. return this._map.has(getRuntimeKey(runtime));
  638. }
  639. [Symbol.iterator]() {
  640. return this._map.values();
  641. }
  642. get size() {
  643. return this._map.size;
  644. }
  645. }
  646. exports.RuntimeSpecSet = RuntimeSpecSet;