runtime.js 14 KB

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