ExportsInfo.js 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { equals } = require("./util/ArrayHelpers");
  7. const SortableSet = require("./util/SortableSet");
  8. const makeSerializable = require("./util/makeSerializable");
  9. const { forEachRuntime } = require("./util/runtime");
  10. /** @typedef {import("./Dependency").RuntimeSpec} RuntimeSpec */
  11. /** @typedef {import("./Module")} Module */
  12. /** @typedef {import("./ModuleGraph")} ModuleGraph */
  13. /** @typedef {import("./ModuleGraphConnection")} ModuleGraphConnection */
  14. /** @typedef {import("./serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */
  15. /** @typedef {import("./serialization/ObjectMiddleware").ObjectSerializerContext} ObjectSerializerContext */
  16. /** @typedef {import("./util/Hash")} Hash */
  17. /** @typedef {typeof UsageState.OnlyPropertiesUsed | typeof UsageState.NoInfo | typeof UsageState.Unknown | typeof UsageState.Used} RuntimeUsageStateType */
  18. /** @typedef {typeof UsageState.Unused | RuntimeUsageStateType} UsageStateType */
  19. const UsageState = Object.freeze({
  20. Unused: /** @type {0} */ (0),
  21. OnlyPropertiesUsed: /** @type {1} */ (1),
  22. NoInfo: /** @type {2} */ (2),
  23. Unknown: /** @type {3} */ (3),
  24. Used: /** @type {4} */ (4)
  25. });
  26. const RETURNS_TRUE = () => true;
  27. const CIRCULAR = Symbol("circular target");
  28. class RestoreProvidedData {
  29. constructor(
  30. exports,
  31. otherProvided,
  32. otherCanMangleProvide,
  33. otherTerminalBinding
  34. ) {
  35. this.exports = exports;
  36. this.otherProvided = otherProvided;
  37. this.otherCanMangleProvide = otherCanMangleProvide;
  38. this.otherTerminalBinding = otherTerminalBinding;
  39. }
  40. /**
  41. * @param {ObjectSerializerContext} context context
  42. */
  43. serialize({ write }) {
  44. write(this.exports);
  45. write(this.otherProvided);
  46. write(this.otherCanMangleProvide);
  47. write(this.otherTerminalBinding);
  48. }
  49. /**
  50. * @param {ObjectDeserializerContext} context context
  51. * @returns {RestoreProvidedData} RestoreProvidedData
  52. */
  53. static deserialize({ read }) {
  54. return new RestoreProvidedData(read(), read(), read(), read());
  55. }
  56. }
  57. makeSerializable(
  58. RestoreProvidedData,
  59. "webpack/lib/ModuleGraph",
  60. "RestoreProvidedData"
  61. );
  62. /** @typedef {Map<string, ExportInfo>} Exports */
  63. /** @typedef {string | string[] | false} UsedName */
  64. class ExportsInfo {
  65. constructor() {
  66. /** @type {Exports} */
  67. this._exports = new Map();
  68. this._otherExportsInfo = new ExportInfo(null);
  69. this._sideEffectsOnlyInfo = new ExportInfo("*side effects only*");
  70. this._exportsAreOrdered = false;
  71. /** @type {ExportsInfo=} */
  72. this._redirectTo = undefined;
  73. }
  74. /**
  75. * @returns {Iterable<ExportInfo>} all owned exports in any order
  76. */
  77. get ownedExports() {
  78. return this._exports.values();
  79. }
  80. /**
  81. * @returns {Iterable<ExportInfo>} all owned exports in order
  82. */
  83. get orderedOwnedExports() {
  84. if (!this._exportsAreOrdered) {
  85. this._sortExports();
  86. }
  87. return this._exports.values();
  88. }
  89. /**
  90. * @returns {Iterable<ExportInfo>} all exports in any order
  91. */
  92. get exports() {
  93. if (this._redirectTo !== undefined) {
  94. const map = new Map(this._redirectTo._exports);
  95. for (const [key, value] of this._exports) {
  96. map.set(key, value);
  97. }
  98. return map.values();
  99. }
  100. return this._exports.values();
  101. }
  102. /**
  103. * @returns {Iterable<ExportInfo>} all exports in order
  104. */
  105. get orderedExports() {
  106. if (!this._exportsAreOrdered) {
  107. this._sortExports();
  108. }
  109. if (this._redirectTo !== undefined) {
  110. const map = new Map(
  111. Array.from(this._redirectTo.orderedExports, item => [item.name, item])
  112. );
  113. for (const [key, value] of this._exports) {
  114. map.set(key, value);
  115. }
  116. // sorting should be pretty fast as map contains
  117. // a lot of presorted items
  118. this._sortExportsMap(map);
  119. return map.values();
  120. }
  121. return this._exports.values();
  122. }
  123. /**
  124. * @returns {ExportInfo} the export info of unlisted exports
  125. */
  126. get otherExportsInfo() {
  127. if (this._redirectTo !== undefined)
  128. return this._redirectTo.otherExportsInfo;
  129. return this._otherExportsInfo;
  130. }
  131. /**
  132. * @param {Exports} exports exports
  133. * @private
  134. */
  135. _sortExportsMap(exports) {
  136. if (exports.size > 1) {
  137. const namesInOrder = [];
  138. for (const entry of exports.values()) {
  139. namesInOrder.push(entry.name);
  140. }
  141. namesInOrder.sort();
  142. let i = 0;
  143. for (const entry of exports.values()) {
  144. const name = namesInOrder[i];
  145. if (entry.name !== name) break;
  146. i++;
  147. }
  148. for (; i < namesInOrder.length; i++) {
  149. const name = namesInOrder[i];
  150. const correctEntry = /** @type {ExportInfo} */ (exports.get(name));
  151. exports.delete(name);
  152. exports.set(name, correctEntry);
  153. }
  154. }
  155. }
  156. _sortExports() {
  157. this._sortExportsMap(this._exports);
  158. this._exportsAreOrdered = true;
  159. }
  160. /**
  161. * @param {ExportsInfo | undefined} exportsInfo exports info
  162. * @returns {boolean} result
  163. */
  164. setRedirectNamedTo(exportsInfo) {
  165. if (this._redirectTo === exportsInfo) return false;
  166. this._redirectTo = exportsInfo;
  167. return true;
  168. }
  169. setHasProvideInfo() {
  170. for (const exportInfo of this._exports.values()) {
  171. if (exportInfo.provided === undefined) {
  172. exportInfo.provided = false;
  173. }
  174. if (exportInfo.canMangleProvide === undefined) {
  175. exportInfo.canMangleProvide = true;
  176. }
  177. }
  178. if (this._redirectTo !== undefined) {
  179. this._redirectTo.setHasProvideInfo();
  180. } else {
  181. if (this._otherExportsInfo.provided === undefined) {
  182. this._otherExportsInfo.provided = false;
  183. }
  184. if (this._otherExportsInfo.canMangleProvide === undefined) {
  185. this._otherExportsInfo.canMangleProvide = true;
  186. }
  187. }
  188. }
  189. setHasUseInfo() {
  190. for (const exportInfo of this._exports.values()) {
  191. exportInfo.setHasUseInfo();
  192. }
  193. this._sideEffectsOnlyInfo.setHasUseInfo();
  194. if (this._redirectTo !== undefined) {
  195. this._redirectTo.setHasUseInfo();
  196. } else {
  197. this._otherExportsInfo.setHasUseInfo();
  198. }
  199. }
  200. /**
  201. * @param {string} name export name
  202. * @returns {ExportInfo} export info for this name
  203. */
  204. getOwnExportInfo(name) {
  205. const info = this._exports.get(name);
  206. if (info !== undefined) return info;
  207. const newInfo = new ExportInfo(name, this._otherExportsInfo);
  208. this._exports.set(name, newInfo);
  209. this._exportsAreOrdered = false;
  210. return newInfo;
  211. }
  212. /**
  213. * @param {string} name export name
  214. * @returns {ExportInfo} export info for this name
  215. */
  216. getExportInfo(name) {
  217. const info = this._exports.get(name);
  218. if (info !== undefined) return info;
  219. if (this._redirectTo !== undefined)
  220. return this._redirectTo.getExportInfo(name);
  221. const newInfo = new ExportInfo(name, this._otherExportsInfo);
  222. this._exports.set(name, newInfo);
  223. this._exportsAreOrdered = false;
  224. return newInfo;
  225. }
  226. /**
  227. * @param {string} name export name
  228. * @returns {ExportInfo} export info for this name
  229. */
  230. getReadOnlyExportInfo(name) {
  231. const info = this._exports.get(name);
  232. if (info !== undefined) return info;
  233. if (this._redirectTo !== undefined)
  234. return this._redirectTo.getReadOnlyExportInfo(name);
  235. return this._otherExportsInfo;
  236. }
  237. /**
  238. * @param {string[]} name export name
  239. * @returns {ExportInfo | undefined} export info for this name
  240. */
  241. getReadOnlyExportInfoRecursive(name) {
  242. const exportInfo = this.getReadOnlyExportInfo(name[0]);
  243. if (name.length === 1) return exportInfo;
  244. if (!exportInfo.exportsInfo) return;
  245. return exportInfo.exportsInfo.getReadOnlyExportInfoRecursive(name.slice(1));
  246. }
  247. /**
  248. * @param {string[]=} name the export name
  249. * @returns {ExportsInfo | undefined} the nested exports info
  250. */
  251. getNestedExportsInfo(name) {
  252. if (Array.isArray(name) && name.length > 0) {
  253. const info = this.getReadOnlyExportInfo(name[0]);
  254. if (!info.exportsInfo) return;
  255. return info.exportsInfo.getNestedExportsInfo(name.slice(1));
  256. }
  257. return this;
  258. }
  259. /**
  260. * @param {boolean=} canMangle true, if exports can still be mangled (defaults to false)
  261. * @param {Set<string>=} excludeExports list of unaffected exports
  262. * @param {any=} targetKey use this as key for the target
  263. * @param {ModuleGraphConnection=} targetModule set this module as target
  264. * @param {number=} priority priority
  265. * @returns {boolean} true, if this call changed something
  266. */
  267. setUnknownExportsProvided(
  268. canMangle,
  269. excludeExports,
  270. targetKey,
  271. targetModule,
  272. priority
  273. ) {
  274. let changed = false;
  275. if (excludeExports) {
  276. for (const name of excludeExports) {
  277. // Make sure these entries exist, so they can get different info
  278. this.getExportInfo(name);
  279. }
  280. }
  281. for (const exportInfo of this._exports.values()) {
  282. if (!canMangle && exportInfo.canMangleProvide !== false) {
  283. exportInfo.canMangleProvide = false;
  284. changed = true;
  285. }
  286. if (excludeExports && excludeExports.has(exportInfo.name)) continue;
  287. if (exportInfo.provided !== true && exportInfo.provided !== null) {
  288. exportInfo.provided = null;
  289. changed = true;
  290. }
  291. if (targetKey) {
  292. exportInfo.setTarget(
  293. targetKey,
  294. /** @type {ModuleGraphConnection} */ (targetModule),
  295. [exportInfo.name],
  296. -1
  297. );
  298. }
  299. }
  300. if (this._redirectTo !== undefined) {
  301. if (
  302. this._redirectTo.setUnknownExportsProvided(
  303. canMangle,
  304. excludeExports,
  305. targetKey,
  306. targetModule,
  307. priority
  308. )
  309. ) {
  310. changed = true;
  311. }
  312. } else {
  313. if (
  314. this._otherExportsInfo.provided !== true &&
  315. this._otherExportsInfo.provided !== null
  316. ) {
  317. this._otherExportsInfo.provided = null;
  318. changed = true;
  319. }
  320. if (!canMangle && this._otherExportsInfo.canMangleProvide !== false) {
  321. this._otherExportsInfo.canMangleProvide = false;
  322. changed = true;
  323. }
  324. if (targetKey) {
  325. this._otherExportsInfo.setTarget(
  326. targetKey,
  327. /** @type {ModuleGraphConnection} */ (targetModule),
  328. undefined,
  329. priority
  330. );
  331. }
  332. }
  333. return changed;
  334. }
  335. /**
  336. * @param {RuntimeSpec} runtime the runtime
  337. * @returns {boolean} true, when something changed
  338. */
  339. setUsedInUnknownWay(runtime) {
  340. let changed = false;
  341. for (const exportInfo of this._exports.values()) {
  342. if (exportInfo.setUsedInUnknownWay(runtime)) {
  343. changed = true;
  344. }
  345. }
  346. if (this._redirectTo !== undefined) {
  347. if (this._redirectTo.setUsedInUnknownWay(runtime)) {
  348. changed = true;
  349. }
  350. } else {
  351. if (
  352. this._otherExportsInfo.setUsedConditionally(
  353. used => used < UsageState.Unknown,
  354. UsageState.Unknown,
  355. runtime
  356. )
  357. ) {
  358. changed = true;
  359. }
  360. if (this._otherExportsInfo.canMangleUse !== false) {
  361. this._otherExportsInfo.canMangleUse = false;
  362. changed = true;
  363. }
  364. }
  365. return changed;
  366. }
  367. /**
  368. * @param {RuntimeSpec} runtime the runtime
  369. * @returns {boolean} true, when something changed
  370. */
  371. setUsedWithoutInfo(runtime) {
  372. let changed = false;
  373. for (const exportInfo of this._exports.values()) {
  374. if (exportInfo.setUsedWithoutInfo(runtime)) {
  375. changed = true;
  376. }
  377. }
  378. if (this._redirectTo !== undefined) {
  379. if (this._redirectTo.setUsedWithoutInfo(runtime)) {
  380. changed = true;
  381. }
  382. } else {
  383. if (this._otherExportsInfo.setUsed(UsageState.NoInfo, runtime)) {
  384. changed = true;
  385. }
  386. if (this._otherExportsInfo.canMangleUse !== false) {
  387. this._otherExportsInfo.canMangleUse = false;
  388. changed = true;
  389. }
  390. }
  391. return changed;
  392. }
  393. /**
  394. * @param {RuntimeSpec} runtime the runtime
  395. * @returns {boolean} true, when something changed
  396. */
  397. setAllKnownExportsUsed(runtime) {
  398. let changed = false;
  399. for (const exportInfo of this._exports.values()) {
  400. if (!exportInfo.provided) continue;
  401. if (exportInfo.setUsed(UsageState.Used, runtime)) {
  402. changed = true;
  403. }
  404. }
  405. return changed;
  406. }
  407. /**
  408. * @param {RuntimeSpec} runtime the runtime
  409. * @returns {boolean} true, when something changed
  410. */
  411. setUsedForSideEffectsOnly(runtime) {
  412. return this._sideEffectsOnlyInfo.setUsedConditionally(
  413. used => used === UsageState.Unused,
  414. UsageState.Used,
  415. runtime
  416. );
  417. }
  418. /**
  419. * @param {RuntimeSpec} runtime the runtime
  420. * @returns {boolean} true, when the module exports are used in any way
  421. */
  422. isUsed(runtime) {
  423. if (this._redirectTo !== undefined) {
  424. if (this._redirectTo.isUsed(runtime)) {
  425. return true;
  426. }
  427. } else if (this._otherExportsInfo.getUsed(runtime) !== UsageState.Unused) {
  428. return true;
  429. }
  430. for (const exportInfo of this._exports.values()) {
  431. if (exportInfo.getUsed(runtime) !== UsageState.Unused) {
  432. return true;
  433. }
  434. }
  435. return false;
  436. }
  437. /**
  438. * @param {RuntimeSpec} runtime the runtime
  439. * @returns {boolean} true, when the module is used in any way
  440. */
  441. isModuleUsed(runtime) {
  442. if (this.isUsed(runtime)) return true;
  443. if (this._sideEffectsOnlyInfo.getUsed(runtime) !== UsageState.Unused)
  444. return true;
  445. return false;
  446. }
  447. /**
  448. * @param {RuntimeSpec} runtime the runtime
  449. * @returns {SortableSet<string> | boolean | null} set of used exports, or true (when namespace object is used), or false (when unused), or null (when unknown)
  450. */
  451. getUsedExports(runtime) {
  452. // eslint-disable-next-line no-constant-binary-expression
  453. if (!this._redirectTo !== undefined) {
  454. switch (this._otherExportsInfo.getUsed(runtime)) {
  455. case UsageState.NoInfo:
  456. return null;
  457. case UsageState.Unknown:
  458. case UsageState.OnlyPropertiesUsed:
  459. case UsageState.Used:
  460. return true;
  461. }
  462. }
  463. const array = [];
  464. if (!this._exportsAreOrdered) this._sortExports();
  465. for (const exportInfo of this._exports.values()) {
  466. switch (exportInfo.getUsed(runtime)) {
  467. case UsageState.NoInfo:
  468. return null;
  469. case UsageState.Unknown:
  470. return true;
  471. case UsageState.OnlyPropertiesUsed:
  472. case UsageState.Used:
  473. array.push(exportInfo.name);
  474. }
  475. }
  476. if (this._redirectTo !== undefined) {
  477. const inner = this._redirectTo.getUsedExports(runtime);
  478. if (inner === null) return null;
  479. if (inner === true) return true;
  480. if (inner !== false) {
  481. for (const item of inner) {
  482. array.push(item);
  483. }
  484. }
  485. }
  486. if (array.length === 0) {
  487. switch (this._sideEffectsOnlyInfo.getUsed(runtime)) {
  488. case UsageState.NoInfo:
  489. return null;
  490. case UsageState.Unused:
  491. return false;
  492. }
  493. }
  494. return /** @type {SortableSet<string>} */ (new SortableSet(array));
  495. }
  496. /**
  497. * @returns {null | true | string[]} list of exports when known
  498. */
  499. getProvidedExports() {
  500. // eslint-disable-next-line no-constant-binary-expression
  501. if (!this._redirectTo !== undefined) {
  502. switch (this._otherExportsInfo.provided) {
  503. case undefined:
  504. return null;
  505. case null:
  506. return true;
  507. case true:
  508. return true;
  509. }
  510. }
  511. const array = [];
  512. if (!this._exportsAreOrdered) this._sortExports();
  513. for (const exportInfo of this._exports.values()) {
  514. switch (exportInfo.provided) {
  515. case undefined:
  516. return null;
  517. case null:
  518. return true;
  519. case true:
  520. array.push(exportInfo.name);
  521. }
  522. }
  523. if (this._redirectTo !== undefined) {
  524. const inner = this._redirectTo.getProvidedExports();
  525. if (inner === null) return null;
  526. if (inner === true) return true;
  527. for (const item of inner) {
  528. if (!array.includes(item)) {
  529. array.push(item);
  530. }
  531. }
  532. }
  533. return array;
  534. }
  535. /**
  536. * @param {RuntimeSpec} runtime the runtime
  537. * @returns {ExportInfo[]} exports that are relevant (not unused and potential provided)
  538. */
  539. getRelevantExports(runtime) {
  540. const list = [];
  541. for (const exportInfo of this._exports.values()) {
  542. const used = exportInfo.getUsed(runtime);
  543. if (used === UsageState.Unused) continue;
  544. if (exportInfo.provided === false) continue;
  545. list.push(exportInfo);
  546. }
  547. if (this._redirectTo !== undefined) {
  548. for (const exportInfo of this._redirectTo.getRelevantExports(runtime)) {
  549. if (!this._exports.has(exportInfo.name)) list.push(exportInfo);
  550. }
  551. }
  552. if (
  553. this._otherExportsInfo.provided !== false &&
  554. this._otherExportsInfo.getUsed(runtime) !== UsageState.Unused
  555. ) {
  556. list.push(this._otherExportsInfo);
  557. }
  558. return list;
  559. }
  560. /**
  561. * @param {string | string[]} name the name of the export
  562. * @returns {boolean | undefined | null} if the export is provided
  563. */
  564. isExportProvided(name) {
  565. if (Array.isArray(name)) {
  566. const info = this.getReadOnlyExportInfo(name[0]);
  567. if (info.exportsInfo && name.length > 1) {
  568. return info.exportsInfo.isExportProvided(name.slice(1));
  569. }
  570. return info.provided ? name.length === 1 || undefined : info.provided;
  571. }
  572. const info = this.getReadOnlyExportInfo(name);
  573. return info.provided;
  574. }
  575. /**
  576. * @param {RuntimeSpec} runtime runtime
  577. * @returns {string} key representing the usage
  578. */
  579. getUsageKey(runtime) {
  580. const key = [];
  581. if (this._redirectTo !== undefined) {
  582. key.push(this._redirectTo.getUsageKey(runtime));
  583. } else {
  584. key.push(this._otherExportsInfo.getUsed(runtime));
  585. }
  586. key.push(this._sideEffectsOnlyInfo.getUsed(runtime));
  587. for (const exportInfo of this.orderedOwnedExports) {
  588. key.push(exportInfo.getUsed(runtime));
  589. }
  590. return key.join("|");
  591. }
  592. /**
  593. * @param {RuntimeSpec} runtimeA first runtime
  594. * @param {RuntimeSpec} runtimeB second runtime
  595. * @returns {boolean} true, when equally used
  596. */
  597. isEquallyUsed(runtimeA, runtimeB) {
  598. if (this._redirectTo !== undefined) {
  599. if (!this._redirectTo.isEquallyUsed(runtimeA, runtimeB)) return false;
  600. } else if (
  601. this._otherExportsInfo.getUsed(runtimeA) !==
  602. this._otherExportsInfo.getUsed(runtimeB)
  603. ) {
  604. return false;
  605. }
  606. if (
  607. this._sideEffectsOnlyInfo.getUsed(runtimeA) !==
  608. this._sideEffectsOnlyInfo.getUsed(runtimeB)
  609. ) {
  610. return false;
  611. }
  612. for (const exportInfo of this.ownedExports) {
  613. if (exportInfo.getUsed(runtimeA) !== exportInfo.getUsed(runtimeB))
  614. return false;
  615. }
  616. return true;
  617. }
  618. /**
  619. * @param {string | string[]} name export name
  620. * @param {RuntimeSpec} runtime check usage for this runtime only
  621. * @returns {UsageStateType} usage status
  622. */
  623. getUsed(name, runtime) {
  624. if (Array.isArray(name)) {
  625. if (name.length === 0) return this.otherExportsInfo.getUsed(runtime);
  626. const info = this.getReadOnlyExportInfo(name[0]);
  627. if (info.exportsInfo && name.length > 1) {
  628. return info.exportsInfo.getUsed(name.slice(1), runtime);
  629. }
  630. return info.getUsed(runtime);
  631. }
  632. const info = this.getReadOnlyExportInfo(name);
  633. return info.getUsed(runtime);
  634. }
  635. /**
  636. * @param {string | string[]} name the export name
  637. * @param {RuntimeSpec} runtime check usage for this runtime only
  638. * @returns {UsedName} the used name
  639. */
  640. getUsedName(name, runtime) {
  641. if (Array.isArray(name)) {
  642. // TODO improve this
  643. if (name.length === 0) {
  644. if (!this.isUsed(runtime)) return false;
  645. return name;
  646. }
  647. const info = this.getReadOnlyExportInfo(name[0]);
  648. const x = info.getUsedName(name[0], runtime);
  649. if (x === false) return false;
  650. const arr =
  651. /** @type {string[]} */
  652. (x === name[0] && name.length === 1 ? name : [x]);
  653. if (name.length === 1) {
  654. return arr;
  655. }
  656. if (
  657. info.exportsInfo &&
  658. info.getUsed(runtime) === UsageState.OnlyPropertiesUsed
  659. ) {
  660. const nested = info.exportsInfo.getUsedName(name.slice(1), runtime);
  661. if (!nested) return false;
  662. return arr.concat(nested);
  663. }
  664. return arr.concat(name.slice(1));
  665. }
  666. const info = this.getReadOnlyExportInfo(name);
  667. const usedName = info.getUsedName(name, runtime);
  668. return usedName;
  669. }
  670. /**
  671. * @param {Hash} hash the hash
  672. * @param {RuntimeSpec} runtime the runtime
  673. * @returns {void}
  674. */
  675. updateHash(hash, runtime) {
  676. this._updateHash(hash, runtime, new Set());
  677. }
  678. /**
  679. * @param {Hash} hash the hash
  680. * @param {RuntimeSpec} runtime the runtime
  681. * @param {Set<ExportsInfo>} alreadyVisitedExportsInfo for circular references
  682. * @returns {void}
  683. */
  684. _updateHash(hash, runtime, alreadyVisitedExportsInfo) {
  685. const set = new Set(alreadyVisitedExportsInfo);
  686. set.add(this);
  687. for (const exportInfo of this.orderedExports) {
  688. if (exportInfo.hasInfo(this._otherExportsInfo, runtime)) {
  689. exportInfo._updateHash(hash, runtime, set);
  690. }
  691. }
  692. this._sideEffectsOnlyInfo._updateHash(hash, runtime, set);
  693. this._otherExportsInfo._updateHash(hash, runtime, set);
  694. if (this._redirectTo !== undefined) {
  695. this._redirectTo._updateHash(hash, runtime, set);
  696. }
  697. }
  698. /**
  699. * @returns {RestoreProvidedData} restore provided data
  700. */
  701. getRestoreProvidedData() {
  702. const otherProvided = this._otherExportsInfo.provided;
  703. const otherCanMangleProvide = this._otherExportsInfo.canMangleProvide;
  704. const otherTerminalBinding = this._otherExportsInfo.terminalBinding;
  705. const exports = [];
  706. for (const exportInfo of this.orderedExports) {
  707. if (
  708. exportInfo.provided !== otherProvided ||
  709. exportInfo.canMangleProvide !== otherCanMangleProvide ||
  710. exportInfo.terminalBinding !== otherTerminalBinding ||
  711. exportInfo.exportsInfoOwned
  712. ) {
  713. exports.push({
  714. name: exportInfo.name,
  715. provided: exportInfo.provided,
  716. canMangleProvide: exportInfo.canMangleProvide,
  717. terminalBinding: exportInfo.terminalBinding,
  718. exportsInfo: exportInfo.exportsInfoOwned
  719. ? /** @type {NonNullable<ExportInfo["exportsInfo"]>} */
  720. (exportInfo.exportsInfo).getRestoreProvidedData()
  721. : undefined
  722. });
  723. }
  724. }
  725. return new RestoreProvidedData(
  726. exports,
  727. otherProvided,
  728. otherCanMangleProvide,
  729. otherTerminalBinding
  730. );
  731. }
  732. /**
  733. * @param {{ otherProvided: any, otherCanMangleProvide: any, otherTerminalBinding: any, exports: any }} data data
  734. */
  735. restoreProvided({
  736. otherProvided,
  737. otherCanMangleProvide,
  738. otherTerminalBinding,
  739. exports
  740. }) {
  741. let wasEmpty = true;
  742. for (const exportInfo of this._exports.values()) {
  743. wasEmpty = false;
  744. exportInfo.provided = otherProvided;
  745. exportInfo.canMangleProvide = otherCanMangleProvide;
  746. exportInfo.terminalBinding = otherTerminalBinding;
  747. }
  748. this._otherExportsInfo.provided = otherProvided;
  749. this._otherExportsInfo.canMangleProvide = otherCanMangleProvide;
  750. this._otherExportsInfo.terminalBinding = otherTerminalBinding;
  751. for (const exp of exports) {
  752. const exportInfo = this.getExportInfo(exp.name);
  753. exportInfo.provided = exp.provided;
  754. exportInfo.canMangleProvide = exp.canMangleProvide;
  755. exportInfo.terminalBinding = exp.terminalBinding;
  756. if (exp.exportsInfo) {
  757. const exportsInfo = exportInfo.createNestedExportsInfo();
  758. exportsInfo.restoreProvided(exp.exportsInfo);
  759. }
  760. }
  761. if (wasEmpty) this._exportsAreOrdered = true;
  762. }
  763. }
  764. /** @typedef {{ module: Module, export: string[] }} TargetItemWithoutConnection */
  765. /** @typedef {{ module: Module, connection: ModuleGraphConnection, export: string[] | undefined }} TargetItem */
  766. /** @typedef {Map<any, { connection: ModuleGraphConnection | null, export: string[], priority: number }>} Target */
  767. class ExportInfo {
  768. /**
  769. * @param {string} name the original name of the export
  770. * @param {ExportInfo=} initFrom init values from this ExportInfo
  771. */
  772. constructor(name, initFrom) {
  773. /** @type {string} */
  774. this.name = name;
  775. /**
  776. * @private
  777. * @type {string | null}
  778. */
  779. this._usedName = initFrom ? initFrom._usedName : null;
  780. /**
  781. * @private
  782. * @type {UsageStateType | undefined}
  783. */
  784. this._globalUsed = initFrom ? initFrom._globalUsed : undefined;
  785. /**
  786. * @private
  787. * @type {Map<string, RuntimeUsageStateType>}
  788. */
  789. this._usedInRuntime =
  790. initFrom && initFrom._usedInRuntime
  791. ? new Map(initFrom._usedInRuntime)
  792. : undefined;
  793. /**
  794. * @private
  795. * @type {boolean}
  796. */
  797. this._hasUseInRuntimeInfo = initFrom
  798. ? initFrom._hasUseInRuntimeInfo
  799. : false;
  800. /**
  801. * true: it is provided
  802. * false: it is not provided
  803. * null: only the runtime knows if it is provided
  804. * undefined: it was not determined if it is provided
  805. * @type {boolean | null | undefined}
  806. */
  807. this.provided = initFrom ? initFrom.provided : undefined;
  808. /**
  809. * is the export a terminal binding that should be checked for export star conflicts
  810. * @type {boolean}
  811. */
  812. this.terminalBinding = initFrom ? initFrom.terminalBinding : false;
  813. /**
  814. * true: it can be mangled
  815. * false: is can not be mangled
  816. * undefined: it was not determined if it can be mangled
  817. * @type {boolean | undefined}
  818. */
  819. this.canMangleProvide = initFrom ? initFrom.canMangleProvide : undefined;
  820. /**
  821. * true: it can be mangled
  822. * false: is can not be mangled
  823. * undefined: it was not determined if it can be mangled
  824. * @type {boolean | undefined}
  825. */
  826. this.canMangleUse = initFrom ? initFrom.canMangleUse : undefined;
  827. /** @type {boolean} */
  828. this.exportsInfoOwned = false;
  829. /** @type {ExportsInfo | undefined} */
  830. this.exportsInfo = undefined;
  831. /** @type {Target | undefined} */
  832. this._target = undefined;
  833. if (initFrom && initFrom._target) {
  834. this._target = new Map();
  835. for (const [key, value] of initFrom._target) {
  836. this._target.set(key, {
  837. connection: value.connection,
  838. export: value.export || [name],
  839. priority: value.priority
  840. });
  841. }
  842. }
  843. /** @type {Target | undefined} */
  844. this._maxTarget = undefined;
  845. }
  846. // TODO webpack 5 remove
  847. /**
  848. * @private
  849. * @param {*} v v
  850. */
  851. set used(v) {
  852. throw new Error("REMOVED");
  853. }
  854. // TODO webpack 5 remove
  855. /** @private */
  856. get used() {
  857. throw new Error("REMOVED");
  858. }
  859. // TODO webpack 5 remove
  860. /**
  861. * @private
  862. * @param {*} v v
  863. */
  864. set usedName(v) {
  865. throw new Error("REMOVED");
  866. }
  867. // TODO webpack 5 remove
  868. /** @private */
  869. get usedName() {
  870. throw new Error("REMOVED");
  871. }
  872. get canMangle() {
  873. switch (this.canMangleProvide) {
  874. case undefined:
  875. return this.canMangleUse === false ? false : undefined;
  876. case false:
  877. return false;
  878. case true:
  879. switch (this.canMangleUse) {
  880. case undefined:
  881. return undefined;
  882. case false:
  883. return false;
  884. case true:
  885. return true;
  886. }
  887. }
  888. throw new Error(
  889. `Unexpected flags for canMangle ${this.canMangleProvide} ${this.canMangleUse}`
  890. );
  891. }
  892. /**
  893. * @param {RuntimeSpec} runtime only apply to this runtime
  894. * @returns {boolean} true, when something changed
  895. */
  896. setUsedInUnknownWay(runtime) {
  897. let changed = false;
  898. if (
  899. this.setUsedConditionally(
  900. used => used < UsageState.Unknown,
  901. UsageState.Unknown,
  902. runtime
  903. )
  904. ) {
  905. changed = true;
  906. }
  907. if (this.canMangleUse !== false) {
  908. this.canMangleUse = false;
  909. changed = true;
  910. }
  911. return changed;
  912. }
  913. /**
  914. * @param {RuntimeSpec} runtime only apply to this runtime
  915. * @returns {boolean} true, when something changed
  916. */
  917. setUsedWithoutInfo(runtime) {
  918. let changed = false;
  919. if (this.setUsed(UsageState.NoInfo, runtime)) {
  920. changed = true;
  921. }
  922. if (this.canMangleUse !== false) {
  923. this.canMangleUse = false;
  924. changed = true;
  925. }
  926. return changed;
  927. }
  928. setHasUseInfo() {
  929. if (!this._hasUseInRuntimeInfo) {
  930. this._hasUseInRuntimeInfo = true;
  931. }
  932. if (this.canMangleUse === undefined) {
  933. this.canMangleUse = true;
  934. }
  935. if (this.exportsInfoOwned) {
  936. /** @type {ExportsInfo} */
  937. (this.exportsInfo).setHasUseInfo();
  938. }
  939. }
  940. /**
  941. * @param {function(UsageStateType): boolean} condition compare with old value
  942. * @param {UsageStateType} newValue set when condition is true
  943. * @param {RuntimeSpec} runtime only apply to this runtime
  944. * @returns {boolean} true when something has changed
  945. */
  946. setUsedConditionally(condition, newValue, runtime) {
  947. if (runtime === undefined) {
  948. if (this._globalUsed === undefined) {
  949. this._globalUsed = newValue;
  950. return true;
  951. }
  952. if (this._globalUsed !== newValue && condition(this._globalUsed)) {
  953. this._globalUsed = newValue;
  954. return true;
  955. }
  956. } else if (this._usedInRuntime === undefined) {
  957. if (newValue !== UsageState.Unused && condition(UsageState.Unused)) {
  958. this._usedInRuntime = new Map();
  959. forEachRuntime(runtime, runtime =>
  960. this._usedInRuntime.set(/** @type {string} */ (runtime), newValue)
  961. );
  962. return true;
  963. }
  964. } else {
  965. let changed = false;
  966. forEachRuntime(runtime, _runtime => {
  967. const runtime = /** @type {string} */ (_runtime);
  968. let oldValue =
  969. /** @type {UsageStateType} */
  970. (this._usedInRuntime.get(runtime));
  971. if (oldValue === undefined) oldValue = UsageState.Unused;
  972. if (newValue !== oldValue && condition(oldValue)) {
  973. if (newValue === UsageState.Unused) {
  974. this._usedInRuntime.delete(runtime);
  975. } else {
  976. this._usedInRuntime.set(runtime, newValue);
  977. }
  978. changed = true;
  979. }
  980. });
  981. if (changed) {
  982. if (this._usedInRuntime.size === 0) this._usedInRuntime = undefined;
  983. return true;
  984. }
  985. }
  986. return false;
  987. }
  988. /**
  989. * @param {UsageStateType} newValue new value of the used state
  990. * @param {RuntimeSpec} runtime only apply to this runtime
  991. * @returns {boolean} true when something has changed
  992. */
  993. setUsed(newValue, runtime) {
  994. if (runtime === undefined) {
  995. if (this._globalUsed !== newValue) {
  996. this._globalUsed = newValue;
  997. return true;
  998. }
  999. } else if (this._usedInRuntime === undefined) {
  1000. if (newValue !== UsageState.Unused) {
  1001. this._usedInRuntime = new Map();
  1002. forEachRuntime(runtime, runtime =>
  1003. this._usedInRuntime.set(/** @type {string} */ (runtime), newValue)
  1004. );
  1005. return true;
  1006. }
  1007. } else {
  1008. let changed = false;
  1009. forEachRuntime(runtime, _runtime => {
  1010. const runtime = /** @type {string} */ (_runtime);
  1011. let oldValue =
  1012. /** @type {UsageStateType} */
  1013. (this._usedInRuntime.get(runtime));
  1014. if (oldValue === undefined) oldValue = UsageState.Unused;
  1015. if (newValue !== oldValue) {
  1016. if (newValue === UsageState.Unused) {
  1017. this._usedInRuntime.delete(runtime);
  1018. } else {
  1019. this._usedInRuntime.set(runtime, newValue);
  1020. }
  1021. changed = true;
  1022. }
  1023. });
  1024. if (changed) {
  1025. if (this._usedInRuntime.size === 0) this._usedInRuntime = undefined;
  1026. return true;
  1027. }
  1028. }
  1029. return false;
  1030. }
  1031. /**
  1032. * @param {any} key the key
  1033. * @returns {boolean} true, if something has changed
  1034. */
  1035. unsetTarget(key) {
  1036. if (!this._target) return false;
  1037. if (this._target.delete(key)) {
  1038. this._maxTarget = undefined;
  1039. return true;
  1040. }
  1041. return false;
  1042. }
  1043. /**
  1044. * @param {any} key the key
  1045. * @param {ModuleGraphConnection} connection the target module if a single one
  1046. * @param {(string[] | null)=} exportName the exported name
  1047. * @param {number=} priority priority
  1048. * @returns {boolean} true, if something has changed
  1049. */
  1050. setTarget(key, connection, exportName, priority = 0) {
  1051. if (exportName) exportName = [...exportName];
  1052. if (!this._target) {
  1053. this._target = new Map();
  1054. this._target.set(key, {
  1055. connection,
  1056. export: /** @type {string[]} */ (exportName),
  1057. priority
  1058. });
  1059. return true;
  1060. }
  1061. const oldTarget = this._target.get(key);
  1062. if (!oldTarget) {
  1063. if (oldTarget === null && !connection) return false;
  1064. this._target.set(key, {
  1065. connection,
  1066. export: /** @type {string[]} */ (exportName),
  1067. priority
  1068. });
  1069. this._maxTarget = undefined;
  1070. return true;
  1071. }
  1072. if (
  1073. oldTarget.connection !== connection ||
  1074. oldTarget.priority !== priority ||
  1075. (exportName
  1076. ? !oldTarget.export || !equals(oldTarget.export, exportName)
  1077. : oldTarget.export)
  1078. ) {
  1079. oldTarget.connection = connection;
  1080. oldTarget.export = /** @type {string[]} */ (exportName);
  1081. oldTarget.priority = priority;
  1082. this._maxTarget = undefined;
  1083. return true;
  1084. }
  1085. return false;
  1086. }
  1087. /**
  1088. * @param {RuntimeSpec} runtime for this runtime
  1089. * @returns {UsageStateType} usage state
  1090. */
  1091. getUsed(runtime) {
  1092. if (!this._hasUseInRuntimeInfo) return UsageState.NoInfo;
  1093. if (this._globalUsed !== undefined) return this._globalUsed;
  1094. if (this._usedInRuntime === undefined) {
  1095. return UsageState.Unused;
  1096. } else if (typeof runtime === "string") {
  1097. const value = this._usedInRuntime.get(runtime);
  1098. return value === undefined ? UsageState.Unused : value;
  1099. } else if (runtime === undefined) {
  1100. /** @type {UsageStateType} */
  1101. let max = UsageState.Unused;
  1102. for (const value of this._usedInRuntime.values()) {
  1103. if (value === UsageState.Used) {
  1104. return UsageState.Used;
  1105. }
  1106. if (max < value) max = value;
  1107. }
  1108. return max;
  1109. }
  1110. /** @type {UsageStateType} */
  1111. let max = UsageState.Unused;
  1112. for (const item of runtime) {
  1113. const value = this._usedInRuntime.get(item);
  1114. if (value !== undefined) {
  1115. if (value === UsageState.Used) {
  1116. return UsageState.Used;
  1117. }
  1118. if (max < value) max = value;
  1119. }
  1120. }
  1121. return max;
  1122. }
  1123. /**
  1124. * get used name
  1125. * @param {string | undefined} fallbackName fallback name for used exports with no name
  1126. * @param {RuntimeSpec} runtime check usage for this runtime only
  1127. * @returns {string | false} used name
  1128. */
  1129. getUsedName(fallbackName, runtime) {
  1130. if (this._hasUseInRuntimeInfo) {
  1131. if (this._globalUsed !== undefined) {
  1132. if (this._globalUsed === UsageState.Unused) return false;
  1133. } else {
  1134. if (this._usedInRuntime === undefined) return false;
  1135. if (typeof runtime === "string") {
  1136. if (!this._usedInRuntime.has(runtime)) {
  1137. return false;
  1138. }
  1139. } else if (
  1140. runtime !== undefined &&
  1141. Array.from(runtime).every(
  1142. runtime => !this._usedInRuntime.has(runtime)
  1143. )
  1144. ) {
  1145. return false;
  1146. }
  1147. }
  1148. }
  1149. if (this._usedName !== null) return this._usedName;
  1150. return /** @type {string | false} */ (this.name || fallbackName);
  1151. }
  1152. /**
  1153. * @returns {boolean} true, when a mangled name of this export is set
  1154. */
  1155. hasUsedName() {
  1156. return this._usedName !== null;
  1157. }
  1158. /**
  1159. * Sets the mangled name of this export
  1160. * @param {string} name the new name
  1161. * @returns {void}
  1162. */
  1163. setUsedName(name) {
  1164. this._usedName = name;
  1165. }
  1166. /**
  1167. * @param {ModuleGraph} moduleGraph the module graph
  1168. * @param {function(TargetItem): boolean} resolveTargetFilter filter function to further resolve target
  1169. * @returns {ExportInfo | ExportsInfo | undefined} the terminal binding export(s) info if known
  1170. */
  1171. getTerminalBinding(moduleGraph, resolveTargetFilter = RETURNS_TRUE) {
  1172. if (this.terminalBinding) return this;
  1173. const target = this.getTarget(moduleGraph, resolveTargetFilter);
  1174. if (!target) return;
  1175. const exportsInfo = moduleGraph.getExportsInfo(target.module);
  1176. if (!target.export) return exportsInfo;
  1177. return exportsInfo.getReadOnlyExportInfoRecursive(target.export);
  1178. }
  1179. isReexport() {
  1180. return !this.terminalBinding && this._target && this._target.size > 0;
  1181. }
  1182. _getMaxTarget() {
  1183. if (this._maxTarget !== undefined) return this._maxTarget;
  1184. if (/** @type {Target} */ (this._target).size <= 1)
  1185. return (this._maxTarget = this._target);
  1186. let maxPriority = -Infinity;
  1187. let minPriority = Infinity;
  1188. for (const { priority } of /** @type {Target} */ (this._target).values()) {
  1189. if (maxPriority < priority) maxPriority = priority;
  1190. if (minPriority > priority) minPriority = priority;
  1191. }
  1192. // This should be very common
  1193. if (maxPriority === minPriority) return (this._maxTarget = this._target);
  1194. // This is an edge case
  1195. const map = new Map();
  1196. for (const [key, value] of /** @type {Target} */ (this._target)) {
  1197. if (maxPriority === value.priority) {
  1198. map.set(key, value);
  1199. }
  1200. }
  1201. this._maxTarget = map;
  1202. return map;
  1203. }
  1204. /**
  1205. * @param {ModuleGraph} moduleGraph the module graph
  1206. * @param {function(Module): boolean} validTargetModuleFilter a valid target module
  1207. * @returns {TargetItemWithoutConnection | null | undefined | false} the target, undefined when there is no target, false when no target is valid
  1208. */
  1209. findTarget(moduleGraph, validTargetModuleFilter) {
  1210. return this._findTarget(moduleGraph, validTargetModuleFilter, new Set());
  1211. }
  1212. /**
  1213. * @param {ModuleGraph} moduleGraph the module graph
  1214. * @param {function(Module): boolean} validTargetModuleFilter a valid target module
  1215. * @param {Set<ExportInfo>} alreadyVisited set of already visited export info to avoid circular references
  1216. * @returns {TargetItemWithoutConnection | null | undefined | false} the target, undefined when there is no target, false when no target is valid
  1217. */
  1218. _findTarget(moduleGraph, validTargetModuleFilter, alreadyVisited) {
  1219. if (!this._target || this._target.size === 0) return;
  1220. const rawTarget =
  1221. /** @type {Target} */
  1222. (this._getMaxTarget()).values().next().value;
  1223. if (!rawTarget) return;
  1224. /** @type {TargetItemWithoutConnection} */
  1225. let target = {
  1226. module: rawTarget.connection.module,
  1227. export: rawTarget.export
  1228. };
  1229. for (;;) {
  1230. if (validTargetModuleFilter(target.module)) return target;
  1231. const exportsInfo = moduleGraph.getExportsInfo(target.module);
  1232. const exportInfo = exportsInfo.getExportInfo(target.export[0]);
  1233. if (alreadyVisited.has(exportInfo)) return null;
  1234. const newTarget = exportInfo._findTarget(
  1235. moduleGraph,
  1236. validTargetModuleFilter,
  1237. alreadyVisited
  1238. );
  1239. if (!newTarget) return false;
  1240. if (target.export.length === 1) {
  1241. target = newTarget;
  1242. } else {
  1243. target = {
  1244. module: newTarget.module,
  1245. export: newTarget.export
  1246. ? newTarget.export.concat(target.export.slice(1))
  1247. : target.export.slice(1)
  1248. };
  1249. }
  1250. }
  1251. }
  1252. /**
  1253. * @param {ModuleGraph} moduleGraph the module graph
  1254. * @param {function(TargetItem): boolean} resolveTargetFilter filter function to further resolve target
  1255. * @returns {TargetItem | undefined} the target
  1256. */
  1257. getTarget(moduleGraph, resolveTargetFilter = RETURNS_TRUE) {
  1258. const result = this._getTarget(moduleGraph, resolveTargetFilter, undefined);
  1259. if (result === CIRCULAR) return;
  1260. return result;
  1261. }
  1262. /**
  1263. * @param {ModuleGraph} moduleGraph the module graph
  1264. * @param {function(TargetItem): boolean} resolveTargetFilter filter function to further resolve target
  1265. * @param {Set<ExportInfo> | undefined} alreadyVisited set of already visited export info to avoid circular references
  1266. * @returns {TargetItem | CIRCULAR | undefined} the target
  1267. */
  1268. _getTarget(moduleGraph, resolveTargetFilter, alreadyVisited) {
  1269. /**
  1270. * @param {TargetItem | null} inputTarget unresolved target
  1271. * @param {Set<ExportInfo>} alreadyVisited set of already visited export info to avoid circular references
  1272. * @returns {TargetItem | CIRCULAR | null} resolved target
  1273. */
  1274. const resolveTarget = (inputTarget, alreadyVisited) => {
  1275. if (!inputTarget) return null;
  1276. if (!inputTarget.export) {
  1277. return {
  1278. module: inputTarget.connection.module,
  1279. connection: inputTarget.connection,
  1280. export: undefined
  1281. };
  1282. }
  1283. /** @type {TargetItem} */
  1284. let target = {
  1285. module: inputTarget.connection.module,
  1286. connection: inputTarget.connection,
  1287. export: inputTarget.export
  1288. };
  1289. if (!resolveTargetFilter(target)) return target;
  1290. let alreadyVisitedOwned = false;
  1291. for (;;) {
  1292. const exportsInfo = moduleGraph.getExportsInfo(target.module);
  1293. const exportInfo = exportsInfo.getExportInfo(
  1294. /** @type {NonNullable<TargetItem["export"]>} */
  1295. (target.export)[0]
  1296. );
  1297. if (!exportInfo) return target;
  1298. if (alreadyVisited.has(exportInfo)) return CIRCULAR;
  1299. const newTarget = exportInfo._getTarget(
  1300. moduleGraph,
  1301. resolveTargetFilter,
  1302. alreadyVisited
  1303. );
  1304. if (newTarget === CIRCULAR) return CIRCULAR;
  1305. if (!newTarget) return target;
  1306. if (
  1307. /** @type {NonNullable<TargetItem["export"]>} */
  1308. (target.export).length === 1
  1309. ) {
  1310. target = newTarget;
  1311. if (!target.export) return target;
  1312. } else {
  1313. target = {
  1314. module: newTarget.module,
  1315. connection: newTarget.connection,
  1316. export: newTarget.export
  1317. ? newTarget.export.concat(
  1318. /** @type {NonNullable<TargetItem["export"]>} */
  1319. (target.export).slice(1)
  1320. )
  1321. : /** @type {NonNullable<TargetItem["export"]>} */
  1322. (target.export).slice(1)
  1323. };
  1324. }
  1325. if (!resolveTargetFilter(target)) return target;
  1326. if (!alreadyVisitedOwned) {
  1327. alreadyVisited = new Set(alreadyVisited);
  1328. alreadyVisitedOwned = true;
  1329. }
  1330. alreadyVisited.add(exportInfo);
  1331. }
  1332. };
  1333. if (!this._target || this._target.size === 0) return;
  1334. if (alreadyVisited && alreadyVisited.has(this)) return CIRCULAR;
  1335. const newAlreadyVisited = new Set(alreadyVisited);
  1336. newAlreadyVisited.add(this);
  1337. const values = /** @type {Target} */ (this._getMaxTarget()).values();
  1338. const target = resolveTarget(values.next().value, newAlreadyVisited);
  1339. if (target === CIRCULAR) return CIRCULAR;
  1340. if (target === null) return;
  1341. let result = values.next();
  1342. while (!result.done) {
  1343. const t = resolveTarget(result.value, newAlreadyVisited);
  1344. if (t === CIRCULAR) return CIRCULAR;
  1345. if (t === null) return;
  1346. if (t.module !== target.module) return;
  1347. if (!t.export !== !target.export) return;
  1348. if (
  1349. target.export &&
  1350. !equals(/** @type {ArrayLike<string>} */ (t.export), target.export)
  1351. )
  1352. return;
  1353. result = values.next();
  1354. }
  1355. return target;
  1356. }
  1357. /**
  1358. * Move the target forward as long resolveTargetFilter is fulfilled
  1359. * @param {ModuleGraph} moduleGraph the module graph
  1360. * @param {function(TargetItem): boolean} resolveTargetFilter filter function to further resolve target
  1361. * @param {function(TargetItem): ModuleGraphConnection=} updateOriginalConnection updates the original connection instead of using the target connection
  1362. * @returns {TargetItem | undefined} the resolved target when moved
  1363. */
  1364. moveTarget(moduleGraph, resolveTargetFilter, updateOriginalConnection) {
  1365. const target = this._getTarget(moduleGraph, resolveTargetFilter, undefined);
  1366. if (target === CIRCULAR) return;
  1367. if (!target) return;
  1368. const originalTarget =
  1369. /** @type {Target} */
  1370. (this._getMaxTarget()).values().next().value;
  1371. if (
  1372. originalTarget.connection === target.connection &&
  1373. originalTarget.export === target.export
  1374. ) {
  1375. return;
  1376. }
  1377. /** @type {Target} */
  1378. (this._target).clear();
  1379. /** @type {Target} */
  1380. (this._target).set(undefined, {
  1381. connection: updateOriginalConnection
  1382. ? updateOriginalConnection(target)
  1383. : target.connection,
  1384. export: /** @type {NonNullable<TargetItem["export"]>} */ (target.export),
  1385. priority: 0
  1386. });
  1387. return target;
  1388. }
  1389. /**
  1390. * @returns {ExportsInfo} an exports info
  1391. */
  1392. createNestedExportsInfo() {
  1393. if (this.exportsInfoOwned)
  1394. return /** @type {ExportsInfo} */ (this.exportsInfo);
  1395. this.exportsInfoOwned = true;
  1396. const oldExportsInfo = this.exportsInfo;
  1397. this.exportsInfo = new ExportsInfo();
  1398. this.exportsInfo.setHasProvideInfo();
  1399. if (oldExportsInfo) {
  1400. this.exportsInfo.setRedirectNamedTo(oldExportsInfo);
  1401. }
  1402. return this.exportsInfo;
  1403. }
  1404. getNestedExportsInfo() {
  1405. return this.exportsInfo;
  1406. }
  1407. /**
  1408. * @param {ExportInfo} baseInfo base info
  1409. * @param {RuntimeSpec} runtime runtime
  1410. * @returns {boolean} true when has info, otherwise false
  1411. */
  1412. hasInfo(baseInfo, runtime) {
  1413. return (
  1414. (this._usedName && this._usedName !== this.name) ||
  1415. this.provided ||
  1416. this.terminalBinding ||
  1417. this.getUsed(runtime) !== baseInfo.getUsed(runtime)
  1418. );
  1419. }
  1420. /**
  1421. * @param {Hash} hash the hash
  1422. * @param {RuntimeSpec} runtime the runtime
  1423. * @returns {void}
  1424. */
  1425. updateHash(hash, runtime) {
  1426. this._updateHash(hash, runtime, new Set());
  1427. }
  1428. /**
  1429. * @param {Hash} hash the hash
  1430. * @param {RuntimeSpec} runtime the runtime
  1431. * @param {Set<ExportsInfo>} alreadyVisitedExportsInfo for circular references
  1432. */
  1433. _updateHash(hash, runtime, alreadyVisitedExportsInfo) {
  1434. hash.update(
  1435. `${this._usedName || this.name}${this.getUsed(runtime)}${this.provided}${
  1436. this.terminalBinding
  1437. }`
  1438. );
  1439. if (this.exportsInfo && !alreadyVisitedExportsInfo.has(this.exportsInfo)) {
  1440. this.exportsInfo._updateHash(hash, runtime, alreadyVisitedExportsInfo);
  1441. }
  1442. }
  1443. getUsedInfo() {
  1444. if (this._globalUsed !== undefined) {
  1445. switch (this._globalUsed) {
  1446. case UsageState.Unused:
  1447. return "unused";
  1448. case UsageState.NoInfo:
  1449. return "no usage info";
  1450. case UsageState.Unknown:
  1451. return "maybe used (runtime-defined)";
  1452. case UsageState.Used:
  1453. return "used";
  1454. case UsageState.OnlyPropertiesUsed:
  1455. return "only properties used";
  1456. }
  1457. } else if (this._usedInRuntime !== undefined) {
  1458. /** @type {Map<RuntimeUsageStateType, string[]>} */
  1459. const map = new Map();
  1460. for (const [runtime, used] of this._usedInRuntime) {
  1461. const list = map.get(used);
  1462. if (list !== undefined) list.push(runtime);
  1463. else map.set(used, [runtime]);
  1464. }
  1465. // eslint-disable-next-line array-callback-return
  1466. const specificInfo = Array.from(map, ([used, runtimes]) => {
  1467. switch (used) {
  1468. case UsageState.NoInfo:
  1469. return `no usage info in ${runtimes.join(", ")}`;
  1470. case UsageState.Unknown:
  1471. return `maybe used in ${runtimes.join(", ")} (runtime-defined)`;
  1472. case UsageState.Used:
  1473. return `used in ${runtimes.join(", ")}`;
  1474. case UsageState.OnlyPropertiesUsed:
  1475. return `only properties used in ${runtimes.join(", ")}`;
  1476. }
  1477. });
  1478. if (specificInfo.length > 0) {
  1479. return specificInfo.join("; ");
  1480. }
  1481. }
  1482. return this._hasUseInRuntimeInfo ? "unused" : "no usage info";
  1483. }
  1484. getProvidedInfo() {
  1485. switch (this.provided) {
  1486. case undefined:
  1487. return "no provided info";
  1488. case null:
  1489. return "maybe provided (runtime-defined)";
  1490. case true:
  1491. return "provided";
  1492. case false:
  1493. return "not provided";
  1494. }
  1495. }
  1496. getRenameInfo() {
  1497. if (this._usedName !== null && this._usedName !== this.name) {
  1498. return `renamed to ${JSON.stringify(this._usedName).slice(1, -1)}`;
  1499. }
  1500. switch (this.canMangleProvide) {
  1501. case undefined:
  1502. switch (this.canMangleUse) {
  1503. case undefined:
  1504. return "missing provision and use info prevents renaming";
  1505. case false:
  1506. return "usage prevents renaming (no provision info)";
  1507. case true:
  1508. return "missing provision info prevents renaming";
  1509. }
  1510. break;
  1511. case true:
  1512. switch (this.canMangleUse) {
  1513. case undefined:
  1514. return "missing usage info prevents renaming";
  1515. case false:
  1516. return "usage prevents renaming";
  1517. case true:
  1518. return "could be renamed";
  1519. }
  1520. break;
  1521. case false:
  1522. switch (this.canMangleUse) {
  1523. case undefined:
  1524. return "provision prevents renaming (no use info)";
  1525. case false:
  1526. return "usage and provision prevents renaming";
  1527. case true:
  1528. return "provision prevents renaming";
  1529. }
  1530. break;
  1531. }
  1532. throw new Error(
  1533. `Unexpected flags for getRenameInfo ${this.canMangleProvide} ${this.canMangleUse}`
  1534. );
  1535. }
  1536. }
  1537. module.exports = ExportsInfo;
  1538. module.exports.ExportInfo = ExportInfo;
  1539. module.exports.UsageState = UsageState;