source: frontend/node_modules/webpack/lib/ExportsInfo.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 11 days ago

Fix frontend appearance

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