ExportsInfo.js 60 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { ImportPhaseUtils } = require("./dependencies/ImportPhase");
  7. const { InlinedUsedName } = require("./optimize/InlineExports");
  8. const { equals } = require("./util/ArrayHelpers");
  9. const SortableSet = require("./util/SortableSet");
  10. const makeSerializable = require("./util/makeSerializable");
  11. const { forEachRuntime } = require("./util/runtime");
  12. /**
  13. * @import Dependency, {
  14. * ExportInfoName,
  15. * ExportsSpecExcludeExports
  16. * } from "./Dependency"
  17. */
  18. /** @import HarmonyImportDependency from "./dependencies/HarmonyImportDependency" */
  19. /** @import Module from "./Module" */
  20. /** @import ModuleGraph from "./ModuleGraph" */
  21. /** @import ModuleGraphConnection from "./ModuleGraphConnection" */
  22. /** @import { InlinedValue } from "./optimize/InlineExports" */
  23. /** @typedef {[RestoreProvidedDataExports[], ExportInfo["provided"], ExportInfo["canMangleProvide"], ExportInfo["terminalBinding"]]} RestoreProvidedDataTuple */
  24. /** @typedef {import("./serialization/ObjectMiddleware").ObjectDeserializerContext<RestoreProvidedDataTuple>} ObjectDeserializerContext */
  25. /** @typedef {import("./serialization/ObjectMiddleware").ObjectSerializerContext<RestoreProvidedDataTuple>} ObjectSerializerContext */
  26. /** @import Hash from "./util/Hash" */
  27. /** @import { RuntimeSpec } from "./util/runtime" */
  28. /** @typedef {typeof UsageState.OnlyPropertiesUsed | typeof UsageState.NoInfo | typeof UsageState.Unknown | typeof UsageState.Used} RuntimeUsageStateType */
  29. /** @typedef {typeof UsageState.Unused | RuntimeUsageStateType} UsageStateType */
  30. /** @typedef {Map<string, RuntimeUsageStateType>} UsedInRuntime */
  31. /** @typedef {{ module: Module, export: ExportInfoName[], deferred: boolean }} TargetItemWithoutConnection */
  32. /** @typedef {{ module: Module, connection: ModuleGraphConnection, export: ExportInfoName[] | undefined }} TargetItemWithConnection */
  33. /** @typedef {(target: TargetItemWithConnection) => boolean} ResolveTargetFilter */
  34. /** @typedef {(module: Module) => boolean} ValidTargetModuleFilter */
  35. /** @typedef {{ connection: ModuleGraphConnection, export: ExportInfoName[], priority: number }} TargetItem */
  36. /** @typedef {Map<Dependency | undefined, TargetItem>} Target */
  37. /** @typedef {string | InlinedUsedName | null} ExportInfoUsedName */
  38. /** @typedef {boolean | null} ExportInfoProvided */
  39. /** @typedef {Map<ExportInfoName, ExportInfo>} Exports */
  40. /** @typedef {string | string[] | InlinedUsedName | false} UsedName */
  41. /** @typedef {Set<ExportInfo>} AlreadyVisitedExportInfo */
  42. /**
  43. * Defines the restore provided data exports type used by this module.
  44. * @typedef {object} RestoreProvidedDataExports
  45. * @property {ExportInfoName} name
  46. * @property {ExportInfo["provided"]} provided
  47. * @property {ExportInfo["canMangleProvide"]} canMangleProvide
  48. * @property {ExportInfo["canInlineProvide"]=} canInlineProvide
  49. * @property {ExportInfo["terminalBinding"]} terminalBinding
  50. * @property {ExportInfo["pureProvide"]=} pureProvide
  51. * @property {RestoreProvidedData | undefined} exportsInfo
  52. */
  53. const UsageState = Object.freeze({
  54. Unused: /** @type {0} */ (0),
  55. OnlyPropertiesUsed: /** @type {1} */ (1),
  56. NoInfo: /** @type {2} */ (2),
  57. Unknown: /** @type {3} */ (3),
  58. Used: /** @type {4} */ (4)
  59. });
  60. const RETURNS_TRUE = () => true;
  61. // Shared empty visited-set for `findTarget`: `_findTarget` only reads
  62. // `alreadyVisited` (`.has`), never mutates it, so a fresh `new Set()` per call
  63. // is pure churn on the per-export reexport hot path. Must stay never-mutated.
  64. const EMPTY_VISITED = /** @type {Set<ExportInfo>} */ (new Set());
  65. const CIRCULAR = Symbol("circular target");
  66. // bit layout of ExportInfo._inlineFlags
  67. const INLINE_USE_TRUE = 1;
  68. const INLINE_USE_FALSE = 2;
  69. const INLINE_USE_MASK = 3;
  70. const PURE_PROVIDE_FLAG = 4;
  71. class RestoreProvidedData {
  72. /**
  73. * Creates an instance of RestoreProvidedData.
  74. * @param {RestoreProvidedDataExports[]} exports exports
  75. * @param {ExportInfo["provided"]} otherProvided other provided
  76. * @param {ExportInfo["canMangleProvide"]} otherCanMangleProvide other can mangle provide
  77. * @param {ExportInfo["terminalBinding"]} otherTerminalBinding other terminal binding
  78. */
  79. constructor(
  80. exports,
  81. otherProvided,
  82. otherCanMangleProvide,
  83. otherTerminalBinding
  84. ) {
  85. /** @type {RestoreProvidedDataExports[]} */
  86. this.exports = exports;
  87. /** @type {ExportInfoProvided | undefined} */
  88. this.otherProvided = otherProvided;
  89. /** @type {boolean | undefined} */
  90. this.otherCanMangleProvide = otherCanMangleProvide;
  91. /** @type {boolean} */
  92. this.otherTerminalBinding = otherTerminalBinding;
  93. }
  94. /**
  95. * Serializes this instance into the provided serializer context.
  96. * @param {ObjectSerializerContext} context context
  97. */
  98. serialize(context) {
  99. context
  100. .write(this.exports)
  101. .write(this.otherProvided)
  102. .write(this.otherCanMangleProvide)
  103. .write(this.otherTerminalBinding);
  104. }
  105. /**
  106. * Restores this instance from the provided deserializer context.
  107. * @param {ObjectDeserializerContext} context context
  108. * @returns {RestoreProvidedData} RestoreProvidedData
  109. */
  110. static deserialize(context) {
  111. const exports = context.read();
  112. const c1 = context.rest;
  113. const otherProvided = c1.read();
  114. const c2 = c1.rest;
  115. const otherCanMangleProvide = c2.read();
  116. const c3 = c2.rest;
  117. const otherTerminalBinding = c3.read();
  118. return new RestoreProvidedData(
  119. exports,
  120. otherProvided,
  121. otherCanMangleProvide,
  122. otherTerminalBinding
  123. );
  124. }
  125. }
  126. makeSerializable(
  127. RestoreProvidedData,
  128. "webpack/lib/ModuleGraph",
  129. "RestoreProvidedData"
  130. );
  131. class ExportsInfo {
  132. constructor() {
  133. /** @type {Exports} */
  134. this._exports = new Map();
  135. // `_otherExportsInfo` is a fallback entry for unlisted exports. Two roles:
  136. // 1. factory template — `getExportInfo` creates `new ExportInfo(name, this)`,
  137. // so created export info extends its properties.
  138. // 2. flags whether the whole exportsInfo can be statically analyzed.
  139. // Its `used` reachable values:
  140. // - NoInfo: no use analysis yet (`optimization#usedExports` off), or used without info
  141. // - Unused: analyzed, no unlisted export needed
  142. // - Unknown: used in unknown way
  143. // - Used/OnlyPropertiesUsed: never reached
  144. // Its `provided` reachable values:
  145. // - undefined: provision not determined yet
  146. // - false: determined, no unlisted export is provided
  147. // - null: only runtime knows (dynamic/unknown exports)
  148. // - true: never reached
  149. /** @type {ExportInfo} */
  150. this._otherExportsInfo = new ExportInfo(null);
  151. /** @type {ExportInfo} */
  152. this._sideEffectsOnlyInfo = new ExportInfo("*side effects only*");
  153. /** @type {boolean} */
  154. this._exportsAreOrdered = false;
  155. /** @type {ExportsInfo=} */
  156. this._redirectTo = undefined;
  157. // fast-path flag for hasInlinedUsedName; set by InlineExportsPlugin
  158. /** @type {boolean} */
  159. this._hasInlinedExports = false;
  160. }
  161. /**
  162. * Records that an owned export got an inlined used name.
  163. * @returns {void}
  164. */
  165. markInlinedExports() {
  166. this._hasInlinedExports = true;
  167. }
  168. /**
  169. * Gets owned exports.
  170. * @returns {Iterable<ExportInfo>} all owned exports in any order
  171. */
  172. get ownedExports() {
  173. return this._exports.values();
  174. }
  175. /**
  176. * Gets ordered owned exports.
  177. * @returns {Iterable<ExportInfo>} all owned exports in order
  178. */
  179. get orderedOwnedExports() {
  180. if (!this._exportsAreOrdered) {
  181. this._sortExports();
  182. }
  183. return this._exports.values();
  184. }
  185. /**
  186. * Returns all exports in any order.
  187. * @returns {Iterable<ExportInfo>} all exports in any order
  188. */
  189. get exports() {
  190. if (this._redirectTo !== undefined) {
  191. const map = new Map(this._redirectTo._exports);
  192. for (const [key, value] of this._exports) {
  193. map.set(key, value);
  194. }
  195. return map.values();
  196. }
  197. return this._exports.values();
  198. }
  199. /**
  200. * Gets ordered exports.
  201. * @returns {Iterable<ExportInfo>} all exports in order
  202. */
  203. get orderedExports() {
  204. if (!this._exportsAreOrdered) {
  205. this._sortExports();
  206. }
  207. if (this._redirectTo !== undefined) {
  208. /** @type {Exports} */
  209. const map = new Map(
  210. Array.from(this._redirectTo.orderedExports, (item) => [item.name, item])
  211. );
  212. for (const [key, value] of this._exports) {
  213. map.set(key, value);
  214. }
  215. // sorting should be pretty fast as map contains
  216. // a lot of presorted items
  217. this._sortExportsMap(map);
  218. return map.values();
  219. }
  220. return this._exports.values();
  221. }
  222. /**
  223. * Gets other exports info.
  224. * @returns {ExportInfo} the export info of unlisted exports
  225. */
  226. get otherExportsInfo() {
  227. if (this._redirectTo !== undefined) {
  228. return this._redirectTo.otherExportsInfo;
  229. }
  230. return this._otherExportsInfo;
  231. }
  232. /**
  233. * Processes the provided export.
  234. * @param {Exports} exports exports
  235. * @private
  236. */
  237. _sortExportsMap(exports) {
  238. if (exports.size > 1) {
  239. /** @type {ExportInfoName[]} */
  240. const namesInOrder = [];
  241. for (const entry of exports.values()) {
  242. namesInOrder.push(entry.name);
  243. }
  244. namesInOrder.sort();
  245. let i = 0;
  246. for (const entry of exports.values()) {
  247. const name = namesInOrder[i];
  248. if (entry.name !== name) break;
  249. i++;
  250. }
  251. for (; i < namesInOrder.length; i++) {
  252. const name = namesInOrder[i];
  253. const correctEntry = /** @type {ExportInfo} */ (exports.get(name));
  254. exports.delete(name);
  255. exports.set(name, correctEntry);
  256. }
  257. }
  258. }
  259. _sortExports() {
  260. this._sortExportsMap(this._exports);
  261. this._exportsAreOrdered = true;
  262. }
  263. /**
  264. * Sets redirect named to.
  265. * @param {ExportsInfo | undefined} exportsInfo exports info
  266. * @returns {boolean} result
  267. */
  268. setRedirectNamedTo(exportsInfo) {
  269. if (this._redirectTo === exportsInfo) return false;
  270. this._redirectTo = exportsInfo;
  271. return true;
  272. }
  273. setHasProvideInfo() {
  274. for (const exportInfo of this._exports.values()) {
  275. exportInfo.setHasProvideInfo();
  276. }
  277. if (this._redirectTo !== undefined) {
  278. this._redirectTo.setHasProvideInfo();
  279. } else {
  280. this._otherExportsInfo.setHasProvideInfo();
  281. }
  282. }
  283. /**
  284. * Resets provide-side info so FlagDependencyExports can recompute it on an
  285. * incremental rebuild against the persisted module graph (which otherwise
  286. * keeps exports removed by the rebuild, since the plugin only merges).
  287. * @returns {void}
  288. */
  289. _resetProvidedExports() {
  290. for (const exportInfo of this._exports.values()) {
  291. exportInfo._resetProvideInfo();
  292. }
  293. this._redirectTo = undefined;
  294. this._otherExportsInfo._resetProvideInfo();
  295. }
  296. /**
  297. * Resets the use-side info to the undetermined state so FlagDependencyUsage
  298. * can re-trace usage on an incremental rebuild. The persisted graph keeps a
  299. * module marked "used", which stops the trace re-queuing it, so exports newly
  300. * used by a changed importer would never be flagged.
  301. * @returns {void}
  302. */
  303. _resetUsedExports() {
  304. for (const exportInfo of this._exports.values()) {
  305. exportInfo._resetUseInfo();
  306. }
  307. this._sideEffectsOnlyInfo._resetUseInfo();
  308. // every InlinedUsedName was just cleared, so drop the fast-path flag
  309. this._hasInlinedExports = false;
  310. // never follow _redirectTo: it points into another module's exports info
  311. // (FlagDependencyExports targets), whose own reset/re-trace owns that state
  312. this._otherExportsInfo._resetUseInfo();
  313. }
  314. setHasUseInfo() {
  315. for (const exportInfo of this._exports.values()) {
  316. exportInfo.setHasUseInfo();
  317. }
  318. this._sideEffectsOnlyInfo.setHasUseInfo();
  319. if (this._redirectTo !== undefined) {
  320. this._redirectTo.setHasUseInfo();
  321. } else {
  322. this._otherExportsInfo.setHasUseInfo();
  323. }
  324. }
  325. /**
  326. * Gets own export info.
  327. * @param {ExportInfoName} name export name
  328. * @returns {ExportInfo} export info for this name
  329. */
  330. getOwnExportInfo(name) {
  331. const info = this._exports.get(name);
  332. if (info !== undefined) return info;
  333. const newInfo = new ExportInfo(name, this._otherExportsInfo);
  334. this._exports.set(name, newInfo);
  335. this._exportsAreOrdered = false;
  336. return newInfo;
  337. }
  338. /**
  339. * Returns export info for this name.
  340. * @param {ExportInfoName} name export name
  341. * @returns {ExportInfo} export info for this name
  342. */
  343. getExportInfo(name) {
  344. const info = this._exports.get(name);
  345. if (info !== undefined) return info;
  346. if (this._redirectTo !== undefined) {
  347. return this._redirectTo.getExportInfo(name);
  348. }
  349. const newInfo = new ExportInfo(name, this._otherExportsInfo);
  350. this._exports.set(name, newInfo);
  351. this._exportsAreOrdered = false;
  352. return newInfo;
  353. }
  354. /**
  355. * Gets read only export info.
  356. * @param {ExportInfoName} name export name
  357. * @returns {InstanceType<ExportInfo>} export info for this name
  358. */
  359. getReadOnlyExportInfo(name) {
  360. const info = this._exports.get(name);
  361. if (info !== undefined) return info;
  362. if (this._redirectTo !== undefined) {
  363. return this._redirectTo.getReadOnlyExportInfo(name);
  364. }
  365. return this._otherExportsInfo;
  366. }
  367. /**
  368. * Gets read only export info recursive.
  369. * @param {ExportInfoName[]} name export name
  370. * @returns {ExportInfo | undefined} export info for this name
  371. */
  372. getReadOnlyExportInfoRecursive(name) {
  373. /** @type {ExportsInfo} */
  374. let exportsInfo = this;
  375. const last = name.length - 1;
  376. for (let i = 0; ; i++) {
  377. const exportInfo = exportsInfo.getReadOnlyExportInfo(name[i]);
  378. if (i === last) return exportInfo;
  379. if (!exportInfo.exportsInfo) return;
  380. exportsInfo = exportInfo.exportsInfo;
  381. }
  382. }
  383. /**
  384. * Gets nested exports info.
  385. * @param {ExportInfoName[]=} name the export name
  386. * @returns {ExportsInfo | undefined} the nested exports info
  387. */
  388. getNestedExportsInfo(name) {
  389. if (Array.isArray(name) && name.length > 0) {
  390. /** @type {ExportsInfo} */
  391. let exportsInfo = this;
  392. for (let i = 0; i < name.length; i++) {
  393. const info = exportsInfo.getReadOnlyExportInfo(name[i]);
  394. if (!info.exportsInfo) return;
  395. exportsInfo = info.exportsInfo;
  396. }
  397. return exportsInfo;
  398. }
  399. return this;
  400. }
  401. /**
  402. * Sets unknown exports provided.
  403. * @param {boolean=} canMangle true, if exports can still be mangled (defaults to false)
  404. * @param {ExportsSpecExcludeExports=} excludeExports list of unaffected exports
  405. * @param {Dependency=} targetKey use this as key for the target
  406. * @param {ModuleGraphConnection=} targetModule set this module as target
  407. * @param {number=} priority priority
  408. * @returns {boolean} true, if this call changed something
  409. */
  410. setUnknownExportsProvided(
  411. canMangle,
  412. excludeExports,
  413. targetKey,
  414. targetModule,
  415. priority
  416. ) {
  417. let changed = false;
  418. if (excludeExports) {
  419. for (const name of excludeExports) {
  420. // Make sure these entries exist, so they can get different info
  421. this.getExportInfo(name);
  422. }
  423. }
  424. for (const exportInfo of this._exports.values()) {
  425. if (!canMangle && exportInfo.canMangleProvide !== false) {
  426. exportInfo.canMangleProvide = false;
  427. changed = true;
  428. }
  429. if (excludeExports && excludeExports.has(exportInfo.name)) continue;
  430. if (exportInfo.provided !== true && exportInfo.provided !== null) {
  431. exportInfo.provided = null;
  432. changed = true;
  433. }
  434. if (targetKey) {
  435. exportInfo.setTarget(
  436. targetKey,
  437. /** @type {ModuleGraphConnection} */
  438. (targetModule),
  439. [exportInfo.name],
  440. -1
  441. );
  442. }
  443. }
  444. if (this._redirectTo !== undefined) {
  445. if (
  446. this._redirectTo.setUnknownExportsProvided(
  447. canMangle,
  448. excludeExports,
  449. targetKey,
  450. targetModule,
  451. priority
  452. )
  453. ) {
  454. changed = true;
  455. }
  456. } else {
  457. if (
  458. this._otherExportsInfo.provided !== true &&
  459. this._otherExportsInfo.provided !== null
  460. ) {
  461. this._otherExportsInfo.provided = null;
  462. changed = true;
  463. }
  464. if (!canMangle && this._otherExportsInfo.canMangleProvide !== false) {
  465. this._otherExportsInfo.canMangleProvide = false;
  466. changed = true;
  467. }
  468. if (targetKey) {
  469. this._otherExportsInfo.setTarget(
  470. targetKey,
  471. /** @type {ModuleGraphConnection} */ (targetModule),
  472. undefined,
  473. priority
  474. );
  475. }
  476. }
  477. return changed;
  478. }
  479. /**
  480. * Sets used in unknown way.
  481. * @param {RuntimeSpec} runtime the runtime
  482. * @returns {boolean} true, when something changed
  483. */
  484. setUsedInUnknownWay(runtime) {
  485. let changed = false;
  486. for (const exportInfo of this._exports.values()) {
  487. if (exportInfo.setUsedInUnknownWay(runtime)) {
  488. changed = true;
  489. }
  490. }
  491. if (this._redirectTo !== undefined) {
  492. if (this._redirectTo.setUsedInUnknownWay(runtime)) {
  493. changed = true;
  494. }
  495. } else if (this._otherExportsInfo.setUsedInUnknownWay(runtime)) {
  496. changed = true;
  497. }
  498. return changed;
  499. }
  500. /**
  501. * Sets used without info.
  502. * @param {RuntimeSpec} runtime the runtime
  503. * @returns {boolean} true, when something changed
  504. */
  505. setUsedWithoutInfo(runtime) {
  506. let changed = false;
  507. for (const exportInfo of this._exports.values()) {
  508. if (exportInfo.setUsedWithoutInfo(runtime)) {
  509. changed = true;
  510. }
  511. }
  512. if (this._redirectTo !== undefined) {
  513. if (this._redirectTo.setUsedWithoutInfo(runtime)) {
  514. changed = true;
  515. }
  516. } else if (this._otherExportsInfo.setUsedWithoutInfo(runtime)) {
  517. changed = true;
  518. }
  519. return changed;
  520. }
  521. /**
  522. * Sets all known exports used.
  523. * @param {RuntimeSpec} runtime the runtime
  524. * @returns {boolean} true, when something changed
  525. */
  526. setAllKnownExportsUsed(runtime) {
  527. let changed = false;
  528. for (const exportInfo of this._exports.values()) {
  529. if (!exportInfo.provided) continue;
  530. if (exportInfo.setUsed(UsageState.Used, runtime)) {
  531. changed = true;
  532. }
  533. }
  534. return changed;
  535. }
  536. /**
  537. * Sets used for side effects only.
  538. * @param {RuntimeSpec} runtime the runtime
  539. * @returns {boolean} true, when something changed
  540. */
  541. setUsedForSideEffectsOnly(runtime) {
  542. return this._sideEffectsOnlyInfo.setUsedConditionally(
  543. (used) => used === UsageState.Unused,
  544. UsageState.Used,
  545. runtime
  546. );
  547. }
  548. /**
  549. * Checks whether this exports info is used.
  550. * @param {RuntimeSpec} runtime the runtime
  551. * @returns {boolean} true, when the module exports are used in any way
  552. */
  553. isUsed(runtime) {
  554. if (this._redirectTo !== undefined) {
  555. if (this._redirectTo.isUsed(runtime)) {
  556. return true;
  557. }
  558. } else if (this._otherExportsInfo.getUsed(runtime) !== UsageState.Unused) {
  559. return true;
  560. }
  561. for (const exportInfo of this._exports.values()) {
  562. if (exportInfo.getUsed(runtime) !== UsageState.Unused) {
  563. return true;
  564. }
  565. }
  566. return false;
  567. }
  568. /**
  569. * Checks whether this exports info is module used.
  570. * @param {RuntimeSpec} runtime the runtime
  571. * @returns {boolean} true, when the module is used in any way
  572. */
  573. isModuleUsed(runtime) {
  574. if (this.isUsed(runtime)) return true;
  575. if (this._sideEffectsOnlyInfo.getUsed(runtime) !== UsageState.Unused) {
  576. return true;
  577. }
  578. return false;
  579. }
  580. /**
  581. * Returns set of used exports, or true (when namespace object is used), or false (when unused), or null (when unknown).
  582. * @param {RuntimeSpec} runtime the runtime
  583. * @returns {SortableSet<ExportInfoName> | boolean | null} set of used exports, or true (when namespace object is used), or false (when unused), or null (when unknown)
  584. */
  585. getUsedExports(runtime) {
  586. switch (this._otherExportsInfo.getUsed(runtime)) {
  587. case UsageState.NoInfo:
  588. return null;
  589. case UsageState.Unknown:
  590. case UsageState.OnlyPropertiesUsed:
  591. case UsageState.Used:
  592. return true;
  593. }
  594. /** @type {ExportInfoName[]} */
  595. const array = [];
  596. if (!this._exportsAreOrdered) this._sortExports();
  597. for (const exportInfo of this._exports.values()) {
  598. switch (exportInfo.getUsed(runtime)) {
  599. case UsageState.NoInfo:
  600. return null;
  601. case UsageState.Unknown:
  602. return true;
  603. case UsageState.OnlyPropertiesUsed:
  604. case UsageState.Used:
  605. array.push(exportInfo.name);
  606. }
  607. }
  608. if (this._redirectTo !== undefined) {
  609. const inner = this._redirectTo.getUsedExports(runtime);
  610. if (inner === null) return null;
  611. if (inner === true) return true;
  612. if (inner !== false) {
  613. for (const item of inner) {
  614. array.push(item);
  615. }
  616. }
  617. }
  618. if (array.length === 0) {
  619. switch (this._sideEffectsOnlyInfo.getUsed(runtime)) {
  620. case UsageState.NoInfo:
  621. return null;
  622. case UsageState.Unused:
  623. return false;
  624. }
  625. }
  626. return /** @type {SortableSet<ExportInfoName>} */ (new SortableSet(array));
  627. }
  628. /**
  629. * Gets provided exports.
  630. * @returns {null | true | ExportInfoName[]} list of exports when known
  631. */
  632. getProvidedExports() {
  633. switch (this._otherExportsInfo.provided) {
  634. case undefined:
  635. return null;
  636. case null:
  637. return true;
  638. case true:
  639. return true;
  640. }
  641. /** @type {ExportInfoName[]} */
  642. const array = [];
  643. if (!this._exportsAreOrdered) this._sortExports();
  644. for (const exportInfo of this._exports.values()) {
  645. switch (exportInfo.provided) {
  646. case undefined:
  647. return null;
  648. case null:
  649. return true;
  650. case true:
  651. array.push(exportInfo.name);
  652. }
  653. }
  654. if (this._redirectTo !== undefined) {
  655. const inner = this._redirectTo.getProvidedExports();
  656. if (inner === null) return null;
  657. if (inner === true) return true;
  658. for (const item of inner) {
  659. if (!array.includes(item)) {
  660. array.push(item);
  661. }
  662. }
  663. }
  664. return array;
  665. }
  666. /**
  667. * Gets relevant exports.
  668. * @param {RuntimeSpec} runtime the runtime
  669. * @returns {ExportInfo[]} exports that are relevant (not unused and potential provided)
  670. */
  671. getRelevantExports(runtime) {
  672. /** @type {ExportInfo[]} */
  673. const list = [];
  674. for (const exportInfo of this._exports.values()) {
  675. const used = exportInfo.getUsed(runtime);
  676. if (used === UsageState.Unused) continue;
  677. if (exportInfo.provided === false) continue;
  678. list.push(exportInfo);
  679. }
  680. if (this._redirectTo !== undefined) {
  681. for (const exportInfo of this._redirectTo.getRelevantExports(runtime)) {
  682. if (!this._exports.has(exportInfo.name)) list.push(exportInfo);
  683. }
  684. }
  685. if (
  686. this._otherExportsInfo.provided !== false &&
  687. this._otherExportsInfo.getUsed(runtime) !== UsageState.Unused
  688. ) {
  689. list.push(this._otherExportsInfo);
  690. }
  691. return list;
  692. }
  693. /**
  694. * Checks whether this exports info is export provided.
  695. * @param {ExportInfoName | ExportInfoName[]} name the name of the export
  696. * @returns {boolean | undefined | null} if the export is provided
  697. */
  698. isExportProvided(name) {
  699. if (Array.isArray(name)) {
  700. /** @type {ExportsInfo} */
  701. let exportsInfo = this;
  702. const last = name.length - 1;
  703. for (let i = 0; ; i++) {
  704. const info = exportsInfo.getReadOnlyExportInfo(name[i]);
  705. if (info.exportsInfo && i < last) {
  706. exportsInfo = info.exportsInfo;
  707. continue;
  708. }
  709. return info.provided ? i === last || undefined : info.provided;
  710. }
  711. }
  712. const info = this.getReadOnlyExportInfo(name);
  713. return info.provided;
  714. }
  715. /**
  716. * Returns key representing the usage.
  717. * @param {RuntimeSpec} runtime runtime
  718. * @returns {string} key representing the usage
  719. */
  720. getUsageKey(runtime) {
  721. /** @type {(string | number)[]} */
  722. const key = [];
  723. if (this._redirectTo !== undefined) {
  724. key.push(this._redirectTo.getUsageKey(runtime));
  725. } else {
  726. key.push(this._otherExportsInfo.getUsed(runtime));
  727. }
  728. key.push(this._sideEffectsOnlyInfo.getUsed(runtime));
  729. for (const exportInfo of this.orderedOwnedExports) {
  730. key.push(exportInfo.getUsed(runtime));
  731. }
  732. return key.join("|");
  733. }
  734. /**
  735. * Checks whether this exports info is equally used.
  736. * @param {RuntimeSpec} runtimeA first runtime
  737. * @param {RuntimeSpec} runtimeB second runtime
  738. * @returns {boolean} true, when equally used
  739. */
  740. isEquallyUsed(runtimeA, runtimeB) {
  741. if (this._redirectTo !== undefined) {
  742. if (!this._redirectTo.isEquallyUsed(runtimeA, runtimeB)) return false;
  743. } else if (
  744. this._otherExportsInfo.getUsed(runtimeA) !==
  745. this._otherExportsInfo.getUsed(runtimeB)
  746. ) {
  747. return false;
  748. }
  749. if (
  750. this._sideEffectsOnlyInfo.getUsed(runtimeA) !==
  751. this._sideEffectsOnlyInfo.getUsed(runtimeB)
  752. ) {
  753. return false;
  754. }
  755. for (const exportInfo of this.ownedExports) {
  756. if (exportInfo.getUsed(runtimeA) !== exportInfo.getUsed(runtimeB)) {
  757. return false;
  758. }
  759. }
  760. return true;
  761. }
  762. /**
  763. * Returns usage status.
  764. * @param {ExportInfoName | ExportInfoName[]} name export name
  765. * @param {RuntimeSpec} runtime check usage for this runtime only
  766. * @returns {UsageStateType} usage status
  767. */
  768. getUsed(name, runtime) {
  769. if (Array.isArray(name)) {
  770. if (name.length === 0) return this.otherExportsInfo.getUsed(runtime);
  771. // Walk the nested path iteratively to avoid a `slice` per level
  772. /** @type {ExportsInfo} */
  773. let exportsInfo = this;
  774. const last = name.length - 1;
  775. for (let i = 0; ; i++) {
  776. const info = exportsInfo.getReadOnlyExportInfo(name[i]);
  777. if (i === last || !info.exportsInfo) return info.getUsed(runtime);
  778. exportsInfo = info.exportsInfo;
  779. }
  780. }
  781. const info = this.getReadOnlyExportInfo(name);
  782. return info.getUsed(runtime);
  783. }
  784. /**
  785. * Returns the used name.
  786. * @param {ExportInfoName | ExportInfoName[]} name the export name
  787. * @param {RuntimeSpec} runtime check usage for this runtime only
  788. * @returns {UsedName} the used name
  789. */
  790. getUsedName(name, runtime) {
  791. if (Array.isArray(name)) {
  792. if (name.length === 0) {
  793. if (!this.isUsed(runtime)) return false;
  794. return name;
  795. }
  796. // Walk the nested path once, collecting used names into a single array
  797. // instead of slicing/spreading at every level.
  798. /** @type {ExportsInfo} */
  799. let exportsInfo = this;
  800. /** @type {ExportInfoName[] | undefined} */
  801. let result;
  802. const last = name.length - 1;
  803. for (let i = 0; ; i++) {
  804. const info = exportsInfo.getReadOnlyExportInfo(name[i]);
  805. const x = info.getUsedName(name[i], runtime);
  806. if (x === false) return false;
  807. if (x instanceof InlinedUsedName) {
  808. // The inlined value replaces the prefix; trailing ids become its suffix
  809. if (i === last) return x;
  810. return new InlinedUsedName(x.value, [
  811. ...x.suffix,
  812. ...name.slice(i + 1)
  813. ]);
  814. }
  815. if (i === last) {
  816. if (result === undefined) return x === name[i] ? name : [x];
  817. result.push(x);
  818. return result;
  819. }
  820. if (result === undefined) result = [];
  821. result.push(x);
  822. // Descend only while the parent is reached purely via property access
  823. if (
  824. info.exportsInfo &&
  825. info.getUsed(runtime) === UsageState.OnlyPropertiesUsed
  826. ) {
  827. exportsInfo = info.exportsInfo;
  828. continue;
  829. }
  830. // Cannot descend further: keep the remaining ids unchanged
  831. for (let j = i + 1; j <= last; j++) result.push(name[j]);
  832. return result;
  833. }
  834. }
  835. const info = this.getReadOnlyExportInfo(name);
  836. return info.getUsedName(name, runtime);
  837. }
  838. /**
  839. * Checks whether `getUsedName(name, runtime)` would return an `InlinedUsedName`,
  840. * without allocating intermediate used-name arrays (hot path for connection conditions).
  841. * @param {ExportInfoName[]} name the export name path
  842. * @param {RuntimeSpec} runtime check usage for this runtime only
  843. * @returns {boolean} true when the used name resolves to an inlined value
  844. */
  845. hasInlinedUsedName(name, runtime) {
  846. if (name.length === 0) return false;
  847. /** @type {ExportsInfo} */
  848. let exportsInfo = this;
  849. const last = name.length - 1;
  850. for (let i = 0; ; i++) {
  851. // follow redirects for the fast-path flag; InlinedUsedName can only
  852. // exist on a level whose (redirected) exportsInfo is marked
  853. let hasInlined = false;
  854. /** @type {ExportsInfo | undefined} */
  855. let e = exportsInfo;
  856. while (e !== undefined) {
  857. if (e._hasInlinedExports) {
  858. hasInlined = true;
  859. break;
  860. }
  861. e = e._redirectTo;
  862. }
  863. if (!hasInlined && i === last) return false;
  864. const info = exportsInfo.getReadOnlyExportInfo(name[i]);
  865. if (hasInlined) {
  866. const x = info.getUsedName(name[i], runtime);
  867. if (x === false) return false;
  868. if (x instanceof InlinedUsedName) return true;
  869. if (i === last) return false;
  870. }
  871. if (
  872. info.exportsInfo &&
  873. info.getUsed(runtime) === UsageState.OnlyPropertiesUsed
  874. ) {
  875. exportsInfo = info.exportsInfo;
  876. continue;
  877. }
  878. return false;
  879. }
  880. }
  881. /**
  882. * Updates the hash with the data contributed by this instance.
  883. * @param {Hash} hash the hash
  884. * @param {RuntimeSpec} runtime the runtime
  885. * @returns {void}
  886. */
  887. updateHash(hash, runtime) {
  888. this._updateHash(hash, runtime, new Set());
  889. }
  890. /**
  891. * Updates hash using the provided hash.
  892. * @param {Hash} hash the hash
  893. * @param {RuntimeSpec} runtime the runtime
  894. * @param {Set<ExportsInfo>} alreadyVisitedExportsInfo for circular references
  895. * @returns {void}
  896. */
  897. _updateHash(hash, runtime, alreadyVisitedExportsInfo) {
  898. // add/delete keeps path semantics without copying the set per level
  899. alreadyVisitedExportsInfo.add(this);
  900. for (const exportInfo of this.orderedExports) {
  901. if (exportInfo.hasInfo(this._otherExportsInfo, runtime)) {
  902. exportInfo._updateHash(hash, runtime, alreadyVisitedExportsInfo);
  903. }
  904. }
  905. this._sideEffectsOnlyInfo._updateHash(
  906. hash,
  907. runtime,
  908. alreadyVisitedExportsInfo
  909. );
  910. this._otherExportsInfo._updateHash(
  911. hash,
  912. runtime,
  913. alreadyVisitedExportsInfo
  914. );
  915. if (this._redirectTo !== undefined) {
  916. this._redirectTo._updateHash(hash, runtime, alreadyVisitedExportsInfo);
  917. }
  918. alreadyVisitedExportsInfo.delete(this);
  919. }
  920. /**
  921. * Gets restore provided data.
  922. * @returns {RestoreProvidedData} restore provided data
  923. */
  924. getRestoreProvidedData() {
  925. const otherProvided = this._otherExportsInfo.provided;
  926. const otherCanMangleProvide = this._otherExportsInfo.canMangleProvide;
  927. const otherTerminalBinding = this._otherExportsInfo.terminalBinding;
  928. /** @type {RestoreProvidedDataExports[]} */
  929. const exports = [];
  930. for (const exportInfo of this.orderedExports) {
  931. const canInlineProvide = exportInfo.canInlineProvide;
  932. const pureProvide = exportInfo.pureProvide;
  933. // inline-exports data is only stored when present, so builds without the
  934. // feature don't pay for it in the persistent cache
  935. const hasInlineInfo =
  936. canInlineProvide !== undefined || pureProvide !== undefined;
  937. if (
  938. exportInfo.provided !== otherProvided ||
  939. exportInfo.canMangleProvide !== otherCanMangleProvide ||
  940. exportInfo.terminalBinding !== otherTerminalBinding ||
  941. hasInlineInfo ||
  942. exportInfo.exportsInfoOwned
  943. ) {
  944. const exportsInfo = exportInfo.exportsInfoOwned
  945. ? /** @type {NonNullable<ExportInfo["exportsInfo"]>} */
  946. (exportInfo.exportsInfo).getRestoreProvidedData()
  947. : undefined;
  948. exports.push(
  949. hasInlineInfo
  950. ? {
  951. name: exportInfo.name,
  952. provided: exportInfo.provided,
  953. canMangleProvide: exportInfo.canMangleProvide,
  954. canInlineProvide,
  955. terminalBinding: exportInfo.terminalBinding,
  956. pureProvide,
  957. exportsInfo
  958. }
  959. : {
  960. name: exportInfo.name,
  961. provided: exportInfo.provided,
  962. canMangleProvide: exportInfo.canMangleProvide,
  963. terminalBinding: exportInfo.terminalBinding,
  964. exportsInfo
  965. }
  966. );
  967. }
  968. }
  969. return new RestoreProvidedData(
  970. exports,
  971. otherProvided,
  972. otherCanMangleProvide,
  973. otherTerminalBinding
  974. );
  975. }
  976. /**
  977. * Processes the provided data.
  978. * @param {RestoreProvidedData} data data
  979. */
  980. restoreProvided({
  981. otherProvided,
  982. otherCanMangleProvide,
  983. otherTerminalBinding,
  984. exports
  985. }) {
  986. let wasEmpty = true;
  987. for (const exportInfo of this._exports.values()) {
  988. wasEmpty = false;
  989. exportInfo.provided = otherProvided;
  990. exportInfo.canMangleProvide = otherCanMangleProvide;
  991. exportInfo.terminalBinding = otherTerminalBinding;
  992. }
  993. this._otherExportsInfo.provided = otherProvided;
  994. this._otherExportsInfo.canMangleProvide = otherCanMangleProvide;
  995. this._otherExportsInfo.terminalBinding = otherTerminalBinding;
  996. for (const exp of exports) {
  997. const exportInfo = this.getExportInfo(exp.name);
  998. exportInfo.provided = exp.provided;
  999. exportInfo.canMangleProvide = exp.canMangleProvide;
  1000. exportInfo.canInlineProvide = exp.canInlineProvide;
  1001. exportInfo.terminalBinding = exp.terminalBinding;
  1002. exportInfo.pureProvide = exp.pureProvide;
  1003. if (exp.exportsInfo) {
  1004. const exportsInfo = exportInfo.createNestedExportsInfo();
  1005. exportsInfo.restoreProvided(exp.exportsInfo);
  1006. }
  1007. }
  1008. if (wasEmpty) this._exportsAreOrdered = true;
  1009. }
  1010. }
  1011. class ExportInfo {
  1012. /**
  1013. * Creates an instance of ExportInfo.
  1014. * @param {ExportInfoName | null} name the original name of the export
  1015. * @param {ExportInfo=} initFrom init values from this ExportInfo
  1016. */
  1017. constructor(name, initFrom) {
  1018. /** @type {ExportInfoName} */
  1019. this.name = /** @type {ExportInfoName} */ (name);
  1020. /**
  1021. * @private
  1022. * @type {ExportInfoUsedName}
  1023. */
  1024. this._usedName = initFrom ? initFrom._usedName : null;
  1025. /**
  1026. * @private
  1027. * @type {UsageStateType | undefined}
  1028. */
  1029. this._globalUsed = initFrom ? initFrom._globalUsed : undefined;
  1030. /**
  1031. * @private
  1032. * @type {UsedInRuntime | undefined}
  1033. */
  1034. this._usedInRuntime =
  1035. initFrom && initFrom._usedInRuntime
  1036. ? new Map(initFrom._usedInRuntime)
  1037. : undefined;
  1038. /**
  1039. * @private
  1040. * @type {boolean}
  1041. */
  1042. this._hasUseInRuntimeInfo = initFrom
  1043. ? initFrom._hasUseInRuntimeInfo
  1044. : false;
  1045. /**
  1046. * true: it is provided
  1047. * false: it is not provided
  1048. * null: only the runtime knows if it is provided
  1049. * undefined: it was not determined if it is provided
  1050. * @type {ExportInfoProvided | undefined}
  1051. */
  1052. this.provided = initFrom ? initFrom.provided : undefined;
  1053. /**
  1054. * is the export a terminal binding that should be checked for export star conflicts
  1055. * @type {boolean}
  1056. */
  1057. this.terminalBinding = initFrom ? initFrom.terminalBinding : false;
  1058. /**
  1059. * true: it can be mangled
  1060. * false: is can not be mangled
  1061. * undefined: it was not determined if it can be mangled
  1062. * @type {boolean | undefined}
  1063. */
  1064. this.canMangleProvide = initFrom ? initFrom.canMangleProvide : undefined;
  1065. // only specific export info can be inlined,
  1066. // so _otherExportsInfo.canInlineProvide is always undefined
  1067. /**
  1068. * value slot for `canInlineProvide`
  1069. * @private
  1070. * @type {InlinedValue | undefined}
  1071. */
  1072. this._inlinedValue = undefined;
  1073. /**
  1074. * true: it can be mangled
  1075. * false: is can not be mangled
  1076. * undefined: it was not determined if it can be mangled
  1077. * @type {boolean | undefined}
  1078. */
  1079. this.canMangleUse = initFrom ? initFrom.canMangleUse : undefined;
  1080. /**
  1081. * bitfield backing `canInlineUse` and `pureProvide`
  1082. * @private
  1083. * @type {number}
  1084. */
  1085. this._inlineFlags = 0;
  1086. /** @type {boolean} */
  1087. this.exportsInfoOwned = false;
  1088. /** @type {ExportsInfo | undefined} */
  1089. this.exportsInfo = undefined;
  1090. /** @type {Target | undefined} */
  1091. this._target = undefined;
  1092. if (initFrom && initFrom._target) {
  1093. this._target = /** @type {Target} */ (new Map());
  1094. for (const [key, value] of initFrom._target) {
  1095. this._target.set(key, {
  1096. connection: value.connection,
  1097. export: value.export || [name],
  1098. priority: value.priority
  1099. });
  1100. }
  1101. }
  1102. /** @type {Target | undefined} */
  1103. this._maxTarget = undefined;
  1104. }
  1105. /**
  1106. * defined: the export binds to a small primitive constant and may be inlined
  1107. * undefined: not an inlined constant export
  1108. * @returns {InlinedValue | undefined} inlined value
  1109. */
  1110. get canInlineProvide() {
  1111. return this._inlinedValue;
  1112. }
  1113. set canInlineProvide(value) {
  1114. this._inlinedValue = value;
  1115. }
  1116. /**
  1117. * true: at least one consumer accepts inlining, none rejected
  1118. * false: at least one consumer rejected inlining
  1119. * undefined: collecting; no consumer has decided yet
  1120. * @returns {boolean | undefined} can inline use
  1121. */
  1122. get canInlineUse() {
  1123. switch (this._inlineFlags & INLINE_USE_MASK) {
  1124. case INLINE_USE_TRUE:
  1125. return true;
  1126. case INLINE_USE_FALSE:
  1127. return false;
  1128. default:
  1129. return undefined;
  1130. }
  1131. }
  1132. set canInlineUse(value) {
  1133. this._inlineFlags =
  1134. (this._inlineFlags & ~INLINE_USE_MASK) |
  1135. (value === undefined ? 0 : value ? INLINE_USE_TRUE : INLINE_USE_FALSE);
  1136. }
  1137. /**
  1138. * Only specific export info can be pure, so other_export_info.pure is always undefined.
  1139. * true: calling the export has no observable side effects
  1140. * undefined: it was not determined whether the export is pure
  1141. * @returns {boolean | undefined} pure provide
  1142. */
  1143. get pureProvide() {
  1144. return (this._inlineFlags & PURE_PROVIDE_FLAG) !== 0 ? true : undefined;
  1145. }
  1146. set pureProvide(value) {
  1147. if (value) this._inlineFlags |= PURE_PROVIDE_FLAG;
  1148. else this._inlineFlags &= ~PURE_PROVIDE_FLAG;
  1149. }
  1150. get canMangle() {
  1151. switch (this.canMangleProvide) {
  1152. case undefined:
  1153. return this.canMangleUse === false ? false : undefined;
  1154. case false:
  1155. return false;
  1156. case true:
  1157. switch (this.canMangleUse) {
  1158. case undefined:
  1159. return undefined;
  1160. case false:
  1161. return false;
  1162. case true:
  1163. return true;
  1164. }
  1165. }
  1166. throw new Error(
  1167. `Unexpected flags for canMangle ${this.canMangleProvide} ${this.canMangleUse}`
  1168. );
  1169. }
  1170. /**
  1171. * @returns {InlinedValue | undefined} the inlined value when both provide and use sides agree, otherwise undefined
  1172. */
  1173. canInline() {
  1174. return this.canInlineProvide !== undefined && this.canInlineUse === true
  1175. ? this.canInlineProvide
  1176. : undefined;
  1177. }
  1178. /**
  1179. * Sets used in unknown way.
  1180. * @param {RuntimeSpec} runtime only apply to this runtime
  1181. * @returns {boolean} true, when something changed
  1182. */
  1183. setUsedInUnknownWay(runtime) {
  1184. let changed = false;
  1185. if (
  1186. this.setUsedConditionally(
  1187. (used) => used < UsageState.Unknown,
  1188. UsageState.Unknown,
  1189. runtime
  1190. )
  1191. ) {
  1192. changed = true;
  1193. }
  1194. if (this.canMangleUse !== false) {
  1195. this.canMangleUse = false;
  1196. changed = true;
  1197. }
  1198. if (this.canInlineUse !== false) {
  1199. this.canInlineUse = false;
  1200. changed = true;
  1201. }
  1202. return changed;
  1203. }
  1204. /**
  1205. * Sets used without info.
  1206. * @param {RuntimeSpec} runtime only apply to this runtime
  1207. * @returns {boolean} true, when something changed
  1208. */
  1209. setUsedWithoutInfo(runtime) {
  1210. let changed = false;
  1211. if (this.setUsed(UsageState.NoInfo, runtime)) {
  1212. changed = true;
  1213. }
  1214. if (this.canMangleUse !== false) {
  1215. this.canMangleUse = false;
  1216. changed = true;
  1217. }
  1218. if (this.canInlineUse !== false) {
  1219. this.canInlineUse = false;
  1220. changed = true;
  1221. }
  1222. return changed;
  1223. }
  1224. setHasProvideInfo() {
  1225. if (this.provided === undefined) {
  1226. this.provided = false;
  1227. }
  1228. if (this.canMangleProvide === undefined) {
  1229. this.canMangleProvide = true;
  1230. }
  1231. // mirror setHasUseInfo: after a provide reset the owned nested info is
  1232. // undefined again, and FlagDependencyExports only upgrades from false/null
  1233. if (this.exportsInfoOwned) {
  1234. /** @type {ExportsInfo} */
  1235. (this.exportsInfo).setHasProvideInfo();
  1236. }
  1237. }
  1238. /**
  1239. * Resets provide-side info to the undetermined state (use info untouched).
  1240. * @returns {void}
  1241. */
  1242. _resetProvideInfo() {
  1243. this.provided = undefined;
  1244. this.terminalBinding = false;
  1245. this.canMangleProvide = undefined;
  1246. this._inlinedValue = undefined;
  1247. if (this.exportsInfoOwned) {
  1248. // reset the owned nested info in place so its use state survives
  1249. /** @type {ExportsInfo} */
  1250. (this.exportsInfo)._resetProvidedExports();
  1251. } else {
  1252. this.exportsInfo = undefined;
  1253. }
  1254. this._target = undefined;
  1255. // also drop the memoized max target so it cannot serve stale entries
  1256. this._maxTarget = undefined;
  1257. // clear the provide-side pure bit; FlagDependencyExports only ever sets it
  1258. // true, so an export that turned impure would otherwise stay marked pure
  1259. this._inlineFlags &= ~PURE_PROVIDE_FLAG;
  1260. }
  1261. /**
  1262. * Resets the use-side info to the undetermined state (provide info untouched,
  1263. * incl. the pure-provide bit) so FlagDependencyUsage can re-trace it.
  1264. * @returns {void}
  1265. */
  1266. _resetUseInfo() {
  1267. this._globalUsed = undefined;
  1268. this._usedInRuntime = undefined;
  1269. this._hasUseInRuntimeInfo = false;
  1270. this.canMangleUse = undefined;
  1271. this._usedName = null;
  1272. this._inlineFlags &= PURE_PROVIDE_FLAG;
  1273. // mirror setHasUseInfo: recurse into an owned nested exportsInfo, where
  1274. // FlagDependencyUsage also writes usage
  1275. if (this.exportsInfoOwned) {
  1276. /** @type {ExportsInfo} */
  1277. (this.exportsInfo)._resetUsedExports();
  1278. }
  1279. }
  1280. setHasUseInfo() {
  1281. if (!this._hasUseInRuntimeInfo) {
  1282. this._hasUseInRuntimeInfo = true;
  1283. }
  1284. if (this.canMangleUse === undefined) {
  1285. this.canMangleUse = true;
  1286. }
  1287. if (this.exportsInfoOwned) {
  1288. /** @type {ExportsInfo} */
  1289. (this.exportsInfo).setHasUseInfo();
  1290. }
  1291. }
  1292. /**
  1293. * Sets used conditionally.
  1294. * @param {(condition: UsageStateType) => boolean} condition compare with old value
  1295. * @param {UsageStateType} newValue set when condition is true
  1296. * @param {RuntimeSpec} runtime only apply to this runtime
  1297. * @returns {boolean} true when something has changed
  1298. */
  1299. setUsedConditionally(condition, newValue, runtime) {
  1300. if (runtime === undefined) {
  1301. if (this._globalUsed === undefined) {
  1302. this._globalUsed = newValue;
  1303. return true;
  1304. }
  1305. if (this._globalUsed !== newValue && condition(this._globalUsed)) {
  1306. this._globalUsed = newValue;
  1307. return true;
  1308. }
  1309. } else if (this._usedInRuntime === undefined) {
  1310. if (newValue !== UsageState.Unused && condition(UsageState.Unused)) {
  1311. this._usedInRuntime = new Map();
  1312. forEachRuntime(runtime, (runtime) =>
  1313. /** @type {UsedInRuntime} */
  1314. (this._usedInRuntime).set(/** @type {string} */ (runtime), newValue)
  1315. );
  1316. return true;
  1317. }
  1318. } else {
  1319. let changed = false;
  1320. forEachRuntime(runtime, (runtime_) => {
  1321. const runtime = /** @type {string} */ (runtime_);
  1322. const usedInRuntime =
  1323. /** @type {UsedInRuntime} */
  1324. (this._usedInRuntime);
  1325. let oldValue =
  1326. /** @type {UsageStateType} */
  1327. (usedInRuntime.get(runtime));
  1328. if (oldValue === undefined) oldValue = UsageState.Unused;
  1329. if (newValue !== oldValue && condition(oldValue)) {
  1330. if (newValue === UsageState.Unused) {
  1331. usedInRuntime.delete(runtime);
  1332. } else {
  1333. usedInRuntime.set(runtime, newValue);
  1334. }
  1335. changed = true;
  1336. }
  1337. });
  1338. if (changed) {
  1339. if (this._usedInRuntime.size === 0) this._usedInRuntime = undefined;
  1340. return true;
  1341. }
  1342. }
  1343. return false;
  1344. }
  1345. /**
  1346. * Updates used using the provided new value.
  1347. * @param {UsageStateType} newValue new value of the used state
  1348. * @param {RuntimeSpec} runtime only apply to this runtime
  1349. * @returns {boolean} true when something has changed
  1350. */
  1351. setUsed(newValue, runtime) {
  1352. if (runtime === undefined) {
  1353. if (this._globalUsed !== newValue) {
  1354. this._globalUsed = newValue;
  1355. return true;
  1356. }
  1357. } else if (this._usedInRuntime === undefined) {
  1358. if (newValue !== UsageState.Unused) {
  1359. this._usedInRuntime = new Map();
  1360. forEachRuntime(runtime, (runtime) =>
  1361. /** @type {UsedInRuntime} */
  1362. (this._usedInRuntime).set(/** @type {string} */ (runtime), newValue)
  1363. );
  1364. return true;
  1365. }
  1366. } else {
  1367. let changed = false;
  1368. forEachRuntime(runtime, (_runtime) => {
  1369. const runtime = /** @type {string} */ (_runtime);
  1370. const usedInRuntime =
  1371. /** @type {UsedInRuntime} */
  1372. (this._usedInRuntime);
  1373. let oldValue =
  1374. /** @type {UsageStateType} */
  1375. (usedInRuntime.get(runtime));
  1376. if (oldValue === undefined) oldValue = UsageState.Unused;
  1377. if (newValue !== oldValue) {
  1378. if (newValue === UsageState.Unused) {
  1379. usedInRuntime.delete(runtime);
  1380. } else {
  1381. usedInRuntime.set(runtime, newValue);
  1382. }
  1383. changed = true;
  1384. }
  1385. });
  1386. if (changed) {
  1387. if (this._usedInRuntime.size === 0) this._usedInRuntime = undefined;
  1388. return true;
  1389. }
  1390. }
  1391. return false;
  1392. }
  1393. /**
  1394. * Returns true, if something has changed.
  1395. * @param {Dependency} key the key
  1396. * @returns {boolean} true, if something has changed
  1397. */
  1398. unsetTarget(key) {
  1399. if (!this._target) return false;
  1400. if (this._target.delete(key)) {
  1401. this._maxTarget = undefined;
  1402. return true;
  1403. }
  1404. return false;
  1405. }
  1406. /**
  1407. * Updates target using the provided key.
  1408. * @param {Dependency} key the key
  1409. * @param {ModuleGraphConnection} connection the target module if a single one
  1410. * @param {ExportInfoName[] | null=} exportName the exported name
  1411. * @param {number=} priority priority
  1412. * @returns {boolean} true, if something has changed
  1413. */
  1414. setTarget(key, connection, exportName, priority = 0) {
  1415. // `equals` compares element-wise, so the incoming `exportName` can be used
  1416. // directly for the change-check; only copy it when actually stored, so the
  1417. // common no-op (unchanged target) path allocates nothing.
  1418. if (!this._target) {
  1419. this._target = /** @type {Target} */ (new Map());
  1420. this._target.set(key, {
  1421. connection,
  1422. export: /** @type {ExportInfoName[]} */ (
  1423. exportName ? [...exportName] : exportName
  1424. ),
  1425. priority
  1426. });
  1427. return true;
  1428. }
  1429. const oldTarget = this._target.get(key);
  1430. if (!oldTarget) {
  1431. if (oldTarget === null && !connection) return false;
  1432. this._target.set(key, {
  1433. connection,
  1434. export: /** @type {ExportInfoName[]} */ (
  1435. exportName ? [...exportName] : exportName
  1436. ),
  1437. priority
  1438. });
  1439. this._maxTarget = undefined;
  1440. return true;
  1441. }
  1442. if (
  1443. oldTarget.connection !== connection ||
  1444. oldTarget.priority !== priority ||
  1445. (exportName
  1446. ? !oldTarget.export || !equals(oldTarget.export, exportName)
  1447. : oldTarget.export)
  1448. ) {
  1449. oldTarget.connection = connection;
  1450. oldTarget.export = /** @type {ExportInfoName[]} */ (
  1451. exportName ? [...exportName] : exportName
  1452. );
  1453. oldTarget.priority = priority;
  1454. this._maxTarget = undefined;
  1455. return true;
  1456. }
  1457. return false;
  1458. }
  1459. /**
  1460. * Returns usage state.
  1461. * @param {RuntimeSpec} runtime for this runtime
  1462. * @returns {UsageStateType} usage state
  1463. */
  1464. getUsed(runtime) {
  1465. if (!this._hasUseInRuntimeInfo) return UsageState.NoInfo;
  1466. if (this._globalUsed !== undefined) return this._globalUsed;
  1467. if (this._usedInRuntime === undefined) {
  1468. return UsageState.Unused;
  1469. } else if (typeof runtime === "string") {
  1470. const value = this._usedInRuntime.get(runtime);
  1471. return value === undefined ? UsageState.Unused : value;
  1472. } else if (runtime === undefined) {
  1473. /** @type {UsageStateType} */
  1474. let max = UsageState.Unused;
  1475. for (const value of this._usedInRuntime.values()) {
  1476. if (value === UsageState.Used) {
  1477. return UsageState.Used;
  1478. }
  1479. if (max < value) max = value;
  1480. }
  1481. return max;
  1482. }
  1483. /** @type {UsageStateType} */
  1484. let max = UsageState.Unused;
  1485. for (const item of runtime) {
  1486. const value = this._usedInRuntime.get(item);
  1487. if (value !== undefined) {
  1488. if (value === UsageState.Used) {
  1489. return UsageState.Used;
  1490. }
  1491. if (max < value) max = value;
  1492. }
  1493. }
  1494. return max;
  1495. }
  1496. /**
  1497. * Returns used name. May return InlinedUsedName when the export is inlined to a primitive.
  1498. * @param {string | undefined} fallbackName fallback name for used exports with no name
  1499. * @param {RuntimeSpec} runtime check usage for this runtime only
  1500. * @returns {string | InlinedUsedName | false} used name
  1501. */
  1502. getUsedName(fallbackName, runtime) {
  1503. if (this._hasUseInRuntimeInfo) {
  1504. if (this._globalUsed !== undefined) {
  1505. if (this._globalUsed === UsageState.Unused) return false;
  1506. } else {
  1507. if (this._usedInRuntime === undefined) return false;
  1508. if (typeof runtime === "string") {
  1509. if (!this._usedInRuntime.has(runtime)) {
  1510. return false;
  1511. }
  1512. } else if (runtime !== undefined) {
  1513. // Unused unless at least one runtime in the set is used; loop avoids
  1514. // allocating an array from the runtime set on this hot path.
  1515. let anyUsed = false;
  1516. for (const r of runtime) {
  1517. if (this._usedInRuntime.has(r)) {
  1518. anyUsed = true;
  1519. break;
  1520. }
  1521. }
  1522. if (!anyUsed) return false;
  1523. }
  1524. }
  1525. }
  1526. if (this._usedName !== null) return this._usedName;
  1527. return /** @type {string | false} */ (this.name || fallbackName);
  1528. }
  1529. /**
  1530. * Checks whether this export info has used name.
  1531. * @returns {boolean} true, when a mangled name of this export is set
  1532. */
  1533. hasUsedName() {
  1534. return this._usedName !== null;
  1535. }
  1536. /**
  1537. * Updates used name using the provided name.
  1538. * @param {string | InlinedUsedName} name the new name
  1539. * @returns {void}
  1540. */
  1541. setUsedName(name) {
  1542. this._usedName = name;
  1543. }
  1544. /**
  1545. * Gets terminal binding.
  1546. * @param {ModuleGraph} moduleGraph the module graph
  1547. * @param {ResolveTargetFilter} resolveTargetFilter filter function to further resolve target
  1548. * @returns {ExportInfo | ExportsInfo | undefined} the terminal binding export(s) info if known
  1549. */
  1550. getTerminalBinding(moduleGraph, resolveTargetFilter = RETURNS_TRUE) {
  1551. if (this.terminalBinding) return this;
  1552. const target = this.getTarget(moduleGraph, resolveTargetFilter);
  1553. if (!target) return;
  1554. const exportsInfo = moduleGraph.getExportsInfo(target.module);
  1555. if (!target.export) return exportsInfo;
  1556. return exportsInfo.getReadOnlyExportInfoRecursive(target.export);
  1557. }
  1558. isReexport() {
  1559. return !this.terminalBinding && this._target && this._target.size > 0;
  1560. }
  1561. _getMaxTarget() {
  1562. if (this._maxTarget !== undefined) return this._maxTarget;
  1563. if (/** @type {Target} */ (this._target).size <= 1) {
  1564. return (this._maxTarget = this._target);
  1565. }
  1566. let maxPriority = -Infinity;
  1567. let minPriority = Infinity;
  1568. for (const { priority } of /** @type {Target} */ (this._target).values()) {
  1569. if (maxPriority < priority) maxPriority = priority;
  1570. if (minPriority > priority) minPriority = priority;
  1571. }
  1572. // This should be very common
  1573. if (maxPriority === minPriority) return (this._maxTarget = this._target);
  1574. // This is an edge case
  1575. /** @type {Target} */
  1576. const map = new Map();
  1577. for (const [key, value] of /** @type {Target} */ (this._target)) {
  1578. if (maxPriority === value.priority) {
  1579. map.set(key, value);
  1580. }
  1581. }
  1582. this._maxTarget = map;
  1583. return map;
  1584. }
  1585. /**
  1586. * Returns the target, undefined when there is no target, false when no target is valid.
  1587. * @param {ModuleGraph} moduleGraph the module graph
  1588. * @param {ValidTargetModuleFilter} validTargetModuleFilter a valid target module
  1589. * @returns {TargetItemWithoutConnection | null | undefined | false} the target, undefined when there is no target, false when no target is valid
  1590. */
  1591. findTarget(moduleGraph, validTargetModuleFilter) {
  1592. return this._findTarget(
  1593. moduleGraph,
  1594. validTargetModuleFilter,
  1595. EMPTY_VISITED
  1596. );
  1597. }
  1598. /**
  1599. * Returns the target, undefined when there is no target, false when no target is valid.
  1600. * @param {ModuleGraph} moduleGraph the module graph
  1601. * @param {ValidTargetModuleFilter} validTargetModuleFilter a valid target module
  1602. * @param {AlreadyVisitedExportInfo} alreadyVisited set of already visited export info to avoid circular references
  1603. * @returns {TargetItemWithoutConnection | null | undefined | false} the target, undefined when there is no target, false when no target is valid
  1604. */
  1605. _findTarget(moduleGraph, validTargetModuleFilter, alreadyVisited) {
  1606. if (!this._target || this._target.size === 0) return;
  1607. const rawTarget =
  1608. /** @type {Target} */
  1609. (this._getMaxTarget()).values().next().value;
  1610. if (!rawTarget) return;
  1611. /** @type {TargetItemWithoutConnection} */
  1612. let target = {
  1613. module: rawTarget.connection.module,
  1614. export: rawTarget.export,
  1615. deferred: Boolean(
  1616. rawTarget.connection.dependency &&
  1617. ImportPhaseUtils.isDefer(
  1618. /** @type {HarmonyImportDependency} */ (
  1619. rawTarget.connection.dependency
  1620. ).phase
  1621. )
  1622. )
  1623. };
  1624. for (;;) {
  1625. if (validTargetModuleFilter(target.module)) return target;
  1626. const exportsInfo = moduleGraph.getExportsInfo(target.module);
  1627. const exportInfo = exportsInfo.getExportInfo(target.export[0]);
  1628. if (alreadyVisited.has(exportInfo)) return null;
  1629. const newTarget = exportInfo._findTarget(
  1630. moduleGraph,
  1631. validTargetModuleFilter,
  1632. alreadyVisited
  1633. );
  1634. if (!newTarget) return false;
  1635. if (target.export.length === 1) {
  1636. target = newTarget;
  1637. } else {
  1638. target = {
  1639. module: newTarget.module,
  1640. export: newTarget.export
  1641. ? [...newTarget.export, ...target.export.slice(1)]
  1642. : target.export.slice(1),
  1643. deferred: newTarget.deferred
  1644. };
  1645. }
  1646. }
  1647. }
  1648. /**
  1649. * Returns the target.
  1650. * @param {ModuleGraph} moduleGraph the module graph
  1651. * @param {ResolveTargetFilter} resolveTargetFilter filter function to further resolve target
  1652. * @returns {TargetItemWithConnection | undefined} the target
  1653. */
  1654. getTarget(moduleGraph, resolveTargetFilter = RETURNS_TRUE) {
  1655. const result = this._getTarget(moduleGraph, resolveTargetFilter, undefined);
  1656. if (result === CIRCULAR) return;
  1657. return result;
  1658. }
  1659. /**
  1660. * Returns the target.
  1661. * @param {ModuleGraph} moduleGraph the module graph
  1662. * @param {ResolveTargetFilter} resolveTargetFilter filter function to further resolve target
  1663. * @param {AlreadyVisitedExportInfo | undefined} alreadyVisited set of already visited export info to avoid circular references
  1664. * @returns {TargetItemWithConnection | CIRCULAR | undefined} the target
  1665. */
  1666. _getTarget(moduleGraph, resolveTargetFilter, alreadyVisited) {
  1667. /**
  1668. * Returns resolved target.
  1669. * @param {TargetItem | undefined | null} inputTarget unresolved target
  1670. * @param {AlreadyVisitedExportInfo} alreadyVisited set of already visited export info to avoid circular references
  1671. * @returns {TargetItemWithConnection | CIRCULAR | null} resolved target
  1672. */
  1673. const resolveTarget = (inputTarget, alreadyVisited) => {
  1674. if (!inputTarget) return null;
  1675. if (!inputTarget.export) {
  1676. return {
  1677. module: inputTarget.connection.module,
  1678. connection: inputTarget.connection,
  1679. export: undefined
  1680. };
  1681. }
  1682. /** @type {TargetItemWithConnection} */
  1683. let target = {
  1684. module: inputTarget.connection.module,
  1685. connection: inputTarget.connection,
  1686. export: inputTarget.export
  1687. };
  1688. if (!resolveTargetFilter(target)) return target;
  1689. let alreadyVisitedOwned = false;
  1690. for (;;) {
  1691. const exportsInfo = moduleGraph.getExportsInfo(target.module);
  1692. const exportInfo = exportsInfo.getExportInfo(
  1693. /** @type {NonNullable<TargetItemWithConnection["export"]>} */
  1694. (target.export)[0]
  1695. );
  1696. if (!exportInfo) return target;
  1697. if (alreadyVisited.has(exportInfo)) return CIRCULAR;
  1698. const newTarget = exportInfo._getTarget(
  1699. moduleGraph,
  1700. resolveTargetFilter,
  1701. alreadyVisited
  1702. );
  1703. if (newTarget === CIRCULAR) return CIRCULAR;
  1704. if (!newTarget) return target;
  1705. if (
  1706. /** @type {NonNullable<TargetItemWithConnection["export"]>} */
  1707. (target.export).length === 1
  1708. ) {
  1709. target = newTarget;
  1710. if (!target.export) return target;
  1711. } else {
  1712. target = {
  1713. module: newTarget.module,
  1714. connection: newTarget.connection,
  1715. export: newTarget.export
  1716. ? [
  1717. ...newTarget.export,
  1718. .../** @type {NonNullable<TargetItemWithConnection["export"]>} */
  1719. (target.export).slice(1)
  1720. ]
  1721. : /** @type {NonNullable<TargetItemWithConnection["export"]>} */
  1722. (target.export).slice(1)
  1723. };
  1724. }
  1725. if (!resolveTargetFilter(target)) return target;
  1726. if (!alreadyVisitedOwned) {
  1727. alreadyVisited = new Set(alreadyVisited);
  1728. alreadyVisitedOwned = true;
  1729. }
  1730. alreadyVisited.add(exportInfo);
  1731. }
  1732. };
  1733. if (!this._target || this._target.size === 0) return;
  1734. if (alreadyVisited && alreadyVisited.has(this)) return CIRCULAR;
  1735. const newAlreadyVisited = new Set(alreadyVisited);
  1736. newAlreadyVisited.add(this);
  1737. const values = /** @type {Target} */ (this._getMaxTarget()).values();
  1738. const target = resolveTarget(values.next().value, newAlreadyVisited);
  1739. if (target === CIRCULAR) return CIRCULAR;
  1740. if (target === null) return;
  1741. let result = values.next();
  1742. while (!result.done) {
  1743. const t = resolveTarget(result.value, newAlreadyVisited);
  1744. if (t === CIRCULAR) return CIRCULAR;
  1745. if (t === null) return;
  1746. if (t.module !== target.module) return;
  1747. if (!t.export !== !target.export) return;
  1748. if (
  1749. target.export &&
  1750. !equals(/** @type {ArrayLike<string>} */ (t.export), target.export)
  1751. ) {
  1752. return;
  1753. }
  1754. result = values.next();
  1755. }
  1756. return target;
  1757. }
  1758. /**
  1759. * Move the target forward as long resolveTargetFilter is fulfilled
  1760. * @param {ModuleGraph} moduleGraph the module graph
  1761. * @param {ResolveTargetFilter} resolveTargetFilter filter function to further resolve target
  1762. * @param {((target: TargetItemWithConnection) => ModuleGraphConnection | undefined)=} updateOriginalConnection updates the original connection instead of using the target connection
  1763. * @returns {TargetItemWithConnection | undefined} the resolved target when moved
  1764. */
  1765. moveTarget(moduleGraph, resolveTargetFilter, updateOriginalConnection) {
  1766. const target = this._getTarget(moduleGraph, resolveTargetFilter, undefined);
  1767. if (target === CIRCULAR) return;
  1768. if (!target) return;
  1769. const originalTarget =
  1770. /** @type {TargetItem} */
  1771. (
  1772. /** @type {Target} */
  1773. (this._getMaxTarget()).values().next().value
  1774. );
  1775. if (
  1776. originalTarget.connection === target.connection &&
  1777. originalTarget.export === target.export
  1778. ) {
  1779. return;
  1780. }
  1781. /** @type {Target} */
  1782. (this._target).clear();
  1783. /** @type {Target} */
  1784. (this._target).set(undefined, {
  1785. connection: updateOriginalConnection
  1786. ? updateOriginalConnection(target) || target.connection
  1787. : target.connection,
  1788. export: /** @type {NonNullable<TargetItemWithConnection["export"]>} */ (
  1789. target.export
  1790. ),
  1791. priority: 0
  1792. });
  1793. return target;
  1794. }
  1795. /**
  1796. * Creates a nested exports info.
  1797. * @returns {ExportsInfo} an exports info
  1798. */
  1799. createNestedExportsInfo() {
  1800. if (this.exportsInfoOwned) {
  1801. return /** @type {ExportsInfo} */ (this.exportsInfo);
  1802. }
  1803. this.exportsInfoOwned = true;
  1804. const oldExportsInfo = this.exportsInfo;
  1805. this.exportsInfo = new ExportsInfo();
  1806. this.exportsInfo.setHasProvideInfo();
  1807. if (oldExportsInfo) {
  1808. this.exportsInfo.setRedirectNamedTo(oldExportsInfo);
  1809. }
  1810. return this.exportsInfo;
  1811. }
  1812. getNestedExportsInfo() {
  1813. return this.exportsInfo;
  1814. }
  1815. /**
  1816. * Checks whether this export info contains the base info.
  1817. * @param {ExportInfo} baseInfo base info
  1818. * @param {RuntimeSpec} runtime runtime
  1819. * @returns {boolean} true when has info, otherwise false
  1820. */
  1821. hasInfo(baseInfo, runtime) {
  1822. return (
  1823. (this._usedName && this._usedName !== this.name) ||
  1824. this.provided ||
  1825. this.terminalBinding ||
  1826. this.getUsed(runtime) !== baseInfo.getUsed(runtime)
  1827. );
  1828. }
  1829. /**
  1830. * Updates the hash with the data contributed by this instance.
  1831. * @param {Hash} hash the hash
  1832. * @param {RuntimeSpec} runtime the runtime
  1833. * @returns {void}
  1834. */
  1835. updateHash(hash, runtime) {
  1836. this._updateHash(hash, runtime, new Set());
  1837. }
  1838. /**
  1839. * Updates hash using the provided hash.
  1840. * @param {Hash} hash the hash
  1841. * @param {RuntimeSpec} runtime the runtime
  1842. * @param {Set<ExportsInfo>} alreadyVisitedExportsInfo for circular references
  1843. */
  1844. _updateHash(hash, runtime, alreadyVisitedExportsInfo) {
  1845. const usedNameHash =
  1846. this._usedName instanceof InlinedUsedName
  1847. ? `inlined:${this._usedName.render("")}`
  1848. : this._usedName || this.name;
  1849. hash.update(
  1850. `${usedNameHash}${this.getUsed(runtime)}${this.provided}${
  1851. this.terminalBinding
  1852. }${this.pureProvide ? "P" : ""}`
  1853. );
  1854. if (this.exportsInfo && !alreadyVisitedExportsInfo.has(this.exportsInfo)) {
  1855. this.exportsInfo._updateHash(hash, runtime, alreadyVisitedExportsInfo);
  1856. }
  1857. }
  1858. getUsedInfo() {
  1859. if (this._globalUsed !== undefined) {
  1860. switch (this._globalUsed) {
  1861. case UsageState.Unused:
  1862. return "unused";
  1863. case UsageState.NoInfo:
  1864. return "no usage info";
  1865. case UsageState.Unknown:
  1866. return "maybe used (runtime-defined)";
  1867. case UsageState.Used:
  1868. return "used";
  1869. case UsageState.OnlyPropertiesUsed:
  1870. return "only properties used";
  1871. }
  1872. } else if (this._usedInRuntime !== undefined) {
  1873. /** @type {Map<RuntimeUsageStateType, string[]>} */
  1874. const map = new Map();
  1875. for (const [runtime, used] of this._usedInRuntime) {
  1876. const list = map.get(used);
  1877. if (list !== undefined) list.push(runtime);
  1878. else map.set(used, [runtime]);
  1879. }
  1880. // eslint-disable-next-line array-callback-return
  1881. const specificInfo = Array.from(map, ([used, runtimes]) => {
  1882. switch (used) {
  1883. case UsageState.NoInfo:
  1884. return `no usage info in ${runtimes.join(", ")}`;
  1885. case UsageState.Unknown:
  1886. return `maybe used in ${runtimes.join(", ")} (runtime-defined)`;
  1887. case UsageState.Used:
  1888. return `used in ${runtimes.join(", ")}`;
  1889. case UsageState.OnlyPropertiesUsed:
  1890. return `only properties used in ${runtimes.join(", ")}`;
  1891. }
  1892. });
  1893. if (specificInfo.length > 0) {
  1894. return specificInfo.join("; ");
  1895. }
  1896. }
  1897. return this._hasUseInRuntimeInfo ? "unused" : "no usage info";
  1898. }
  1899. getProvidedInfo() {
  1900. switch (this.provided) {
  1901. case undefined:
  1902. return "no provided info";
  1903. case null:
  1904. return "maybe provided (runtime-defined)";
  1905. case true:
  1906. return "provided";
  1907. case false:
  1908. return "not provided";
  1909. }
  1910. }
  1911. getRenameInfo() {
  1912. if (this._usedName !== null && this._usedName !== this.name) {
  1913. return `renamed to ${JSON.stringify(this._usedName).slice(1, -1)}`;
  1914. }
  1915. switch (this.canMangleProvide) {
  1916. case undefined:
  1917. switch (this.canMangleUse) {
  1918. case undefined:
  1919. return "missing provision and use info prevents renaming";
  1920. case false:
  1921. return "usage prevents renaming (no provision info)";
  1922. case true:
  1923. return "missing provision info prevents renaming";
  1924. }
  1925. break;
  1926. case true:
  1927. switch (this.canMangleUse) {
  1928. case undefined:
  1929. return "missing usage info prevents renaming";
  1930. case false:
  1931. return "usage prevents renaming";
  1932. case true:
  1933. return "could be renamed";
  1934. }
  1935. break;
  1936. case false:
  1937. switch (this.canMangleUse) {
  1938. case undefined:
  1939. return "provision prevents renaming (no use info)";
  1940. case false:
  1941. return "usage and provision prevents renaming";
  1942. case true:
  1943. return "provision prevents renaming";
  1944. }
  1945. break;
  1946. }
  1947. throw new Error(
  1948. `Unexpected flags for getRenameInfo ${this.canMangleProvide} ${this.canMangleUse}`
  1949. );
  1950. }
  1951. }
  1952. ExportsInfo.ExportInfo = ExportInfo;
  1953. ExportsInfo.RestoreProvidedData = RestoreProvidedData;
  1954. ExportsInfo.UsageState = UsageState;
  1955. module.exports = ExportsInfo;