webpack 5.108.2 → 5.108.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/Compilation.js +1 -4
- package/lib/FlagDependencyExportsPlugin.js +55 -8
- package/lib/LazyBarrel.js +30 -3
- package/lib/NormalModule.js +8 -12
- package/lib/config/defaults.js +3 -3
- package/lib/css/CssGenerator.js +33 -42
- package/lib/css/CssModulesPlugin.js +79 -53
- package/lib/css/CssParser.js +699 -449
- package/lib/css/syntax.js +1146 -360
- package/lib/dependencies/CssIcssExportDependency.js +28 -16
- package/lib/dependencies/HarmonyAcceptDependency.js +8 -16
- package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +23 -96
- package/lib/dependencies/HarmonyImportGuard.js +192 -20
- package/lib/dependencies/ImportParserPlugin.js +7 -9
- package/lib/html/HtmlGenerator.js +53 -76
- package/lib/html/HtmlModule.js +1 -0
- package/lib/html/HtmlModulesPlugin.js +24 -13
- package/lib/html/HtmlParser.js +240 -124
- package/lib/html/syntax.js +1362 -683
- package/lib/javascript/JavascriptParser.js +10 -0
- package/lib/url/URLParserPlugin.js +33 -7
- package/lib/util/LocConverter.js +9 -8
- package/lib/util/SourceProcessor.js +41 -28
- package/package.json +3 -3
- package/types.d.ts +79 -221
|
@@ -245,7 +245,10 @@ const computeInterpolatedIdentifier = (
|
|
|
245
245
|
* @property {boolean} interpolate whether the value needs interpolation
|
|
246
246
|
* @property {ExportMode} exportMode export mode
|
|
247
247
|
* @property {ExportType} exportType export type
|
|
248
|
-
* @property {
|
|
248
|
+
* @property {number} locStartLine per-export source start line (1-based; flat numbers instead of a nested `DependencyLocation` — entries are per-export hot allocations)
|
|
249
|
+
* @property {number} locStartColumn per-export source start column
|
|
250
|
+
* @property {number} locEndLine per-export source end line
|
|
251
|
+
* @property {number} locEndColumn per-export source end column
|
|
249
252
|
* @property {string[]=} _conventionNames memoized convention names for `name`
|
|
250
253
|
* @property {string[]=} _valueConventionNames memoized convention names for `value`
|
|
251
254
|
*/
|
|
@@ -412,8 +415,14 @@ class CssIcssExportDependency extends NullDependency {
|
|
|
412
415
|
);
|
|
413
416
|
error.module = module;
|
|
414
417
|
// Per-entry loc so Compilation attributes the warning precisely
|
|
415
|
-
// (it can't use the consolidated dependency's single loc).
|
|
416
|
-
|
|
418
|
+
// (it can't use the consolidated dependency's single loc). Built
|
|
419
|
+
// on demand — entries carry flat numbers, not location objects.
|
|
420
|
+
if (entry.locStartLine > 0) {
|
|
421
|
+
error.loc = {
|
|
422
|
+
start: { line: entry.locStartLine, column: entry.locStartColumn },
|
|
423
|
+
end: { line: entry.locEndLine, column: entry.locEndColumn }
|
|
424
|
+
};
|
|
425
|
+
}
|
|
417
426
|
if (warnings === null) warnings = [];
|
|
418
427
|
warnings.push(error);
|
|
419
428
|
}
|
|
@@ -464,7 +473,10 @@ class CssIcssExportDependency extends NullDependency {
|
|
|
464
473
|
write(entry.interpolate);
|
|
465
474
|
write(entry.exportMode);
|
|
466
475
|
write(entry.exportType);
|
|
467
|
-
write(entry.
|
|
476
|
+
write(entry.locStartLine);
|
|
477
|
+
write(entry.locStartColumn);
|
|
478
|
+
write(entry.locEndLine);
|
|
479
|
+
write(entry.locEndColumn);
|
|
468
480
|
}
|
|
469
481
|
super.serialize(context);
|
|
470
482
|
}
|
|
@@ -486,7 +498,10 @@ class CssIcssExportDependency extends NullDependency {
|
|
|
486
498
|
interpolate: /** @type {boolean} */ (read()),
|
|
487
499
|
exportMode: /** @type {ExportMode} */ (read()),
|
|
488
500
|
exportType: /** @type {ExportType} */ (read()),
|
|
489
|
-
|
|
501
|
+
locStartLine: /** @type {number} */ (read()),
|
|
502
|
+
locStartColumn: /** @type {number} */ (read()),
|
|
503
|
+
locEndLine: /** @type {number} */ (read()),
|
|
504
|
+
locEndColumn: /** @type {number} */ (read())
|
|
490
505
|
});
|
|
491
506
|
}
|
|
492
507
|
this.entries = entries;
|
|
@@ -807,23 +822,20 @@ CssIcssExportDependency.Template = class CssIcssExportDependencyTemplate extends
|
|
|
807
822
|
templateContext.runtimeTemplate.compilation.compiler.root
|
|
808
823
|
);
|
|
809
824
|
|
|
810
|
-
const
|
|
811
|
-
|
|
812
|
-
/** @type {{ start?: { line: number, column: number } }} */ (
|
|
813
|
-
entry.loc
|
|
814
|
-
).start;
|
|
825
|
+
const depLocLine = entry.locStartLine;
|
|
826
|
+
const depLocColumn = entry.locStartColumn;
|
|
815
827
|
for (const used of allNames) {
|
|
816
828
|
if (entry.exportMode === EXPORT_MODE.ONCE) {
|
|
817
829
|
if (cssData.exports.has(used)) continue;
|
|
818
830
|
cssData.exports.set(used, unescaped);
|
|
819
831
|
if (
|
|
820
|
-
|
|
832
|
+
depLocLine > 0 &&
|
|
821
833
|
cssData.exportLocs &&
|
|
822
834
|
!cssData.exportLocs.has(used)
|
|
823
835
|
) {
|
|
824
836
|
cssData.exportLocs.set(used, {
|
|
825
|
-
line:
|
|
826
|
-
column:
|
|
837
|
+
line: depLocLine,
|
|
838
|
+
column: depLocColumn
|
|
827
839
|
});
|
|
828
840
|
}
|
|
829
841
|
} else {
|
|
@@ -836,13 +848,13 @@ CssIcssExportDependency.Template = class CssIcssExportDependencyTemplate extends
|
|
|
836
848
|
`${originalValue ? `${originalValue}${unescaped ? " " : ""}` : ""}${unescaped}`
|
|
837
849
|
);
|
|
838
850
|
if (
|
|
839
|
-
|
|
851
|
+
depLocLine > 0 &&
|
|
840
852
|
cssData.exportLocs &&
|
|
841
853
|
!cssData.exportLocs.has(used)
|
|
842
854
|
) {
|
|
843
855
|
cssData.exportLocs.set(used, {
|
|
844
|
-
line:
|
|
845
|
-
column:
|
|
856
|
+
line: depLocLine,
|
|
857
|
+
column: depLocColumn
|
|
846
858
|
});
|
|
847
859
|
}
|
|
848
860
|
}
|
|
@@ -19,8 +19,6 @@ const NullDependency = require("./NullDependency");
|
|
|
19
19
|
/** @typedef {import("./HarmonyAcceptImportDependency")} HarmonyAcceptImportDependency */
|
|
20
20
|
/** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext<[Range, HarmonyAcceptImportDependency[], boolean]>} ObjectDeserializerContext */
|
|
21
21
|
/** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext<[Range, HarmonyAcceptImportDependency[], boolean]>} ObjectSerializerContext */
|
|
22
|
-
/** @typedef {import("../Module")} Module */
|
|
23
|
-
/** @typedef {import("../Module").ModuleId} ModuleId */
|
|
24
22
|
|
|
25
23
|
class HarmonyAcceptDependency extends NullDependency {
|
|
26
24
|
/**
|
|
@@ -91,26 +89,20 @@ HarmonyAcceptDependency.Template = class HarmonyAcceptDependencyTemplate extends
|
|
|
91
89
|
chunkGraph
|
|
92
90
|
} = templateContext;
|
|
93
91
|
|
|
94
|
-
/**
|
|
95
|
-
* Gets dependency module id.
|
|
96
|
-
* @param {Dependency} dependency the dependency to get module id for
|
|
97
|
-
* @returns {ModuleId | null} the module id or null if not found
|
|
98
|
-
*/
|
|
99
|
-
const getDependencyModuleId = (dependency) =>
|
|
100
|
-
chunkGraph.getModuleId(
|
|
101
|
-
/** @type {Module} */ (moduleGraph.getModule(dependency))
|
|
102
|
-
);
|
|
103
|
-
|
|
104
92
|
/**
|
|
105
93
|
* Checks whether this harmony accept dependency is related harmony import dependency.
|
|
106
94
|
* @param {Dependency} a the first dependency
|
|
107
95
|
* @param {Dependency} b the second dependency
|
|
108
96
|
* @returns {boolean} true if the dependencies are related
|
|
109
97
|
*/
|
|
110
|
-
const isRelatedHarmonyImportDependency = (a, b) =>
|
|
111
|
-
a
|
|
112
|
-
|
|
113
|
-
|
|
98
|
+
const isRelatedHarmonyImportDependency = (a, b) => {
|
|
99
|
+
if (a === b || !(b instanceof HarmonyImportDependency)) return false;
|
|
100
|
+
// Compare modules by reference: an unresolved import (ignored/failed, or a
|
|
101
|
+
// deferred lazy-barrel re-export) has no module, and a module not in any
|
|
102
|
+
// chunk has a null id — so comparing ids would crash or miss real matches.
|
|
103
|
+
const moduleA = moduleGraph.getModule(a);
|
|
104
|
+
return moduleA !== null && moduleA === moduleGraph.getModule(b);
|
|
105
|
+
};
|
|
114
106
|
|
|
115
107
|
/**
|
|
116
108
|
* HarmonyAcceptImportDependency lacks a lot of information, such as the defer property.
|
|
@@ -12,7 +12,6 @@ const {
|
|
|
12
12
|
getImportAttributes
|
|
13
13
|
} = require("../javascript/JavascriptParser");
|
|
14
14
|
const { getInnerGraphUtils } = require("../optimize/InnerGraph");
|
|
15
|
-
const memoize = require("../util/memoize");
|
|
16
15
|
const ConstDependency = require("./ConstDependency");
|
|
17
16
|
const HarmonyAcceptDependency = require("./HarmonyAcceptDependency");
|
|
18
17
|
const HarmonyAcceptImportDependency = require("./HarmonyAcceptImportDependency");
|
|
@@ -22,6 +21,10 @@ const {
|
|
|
22
21
|
ExportPresenceModes,
|
|
23
22
|
getNonOptionalPart
|
|
24
23
|
} = require("./HarmonyImportDependency");
|
|
24
|
+
const {
|
|
25
|
+
attachDependencyGuards,
|
|
26
|
+
isPresentByGuards
|
|
27
|
+
} = require("./HarmonyImportGuard");
|
|
25
28
|
const HarmonyImportSideEffectDependency = require("./HarmonyImportSideEffectDependency");
|
|
26
29
|
const HarmonyImportSpecifierDependency = require("./HarmonyImportSpecifierDependency");
|
|
27
30
|
const { ImportPhaseUtils, createGetImportPhase } = require("./ImportPhase");
|
|
@@ -46,10 +49,6 @@ const { ImportPhaseUtils, createGetImportPhase } = require("./ImportPhase");
|
|
|
46
49
|
/** @typedef {import("./ImportPhase").ImportPhaseType} ImportPhaseType */
|
|
47
50
|
/** @typedef {import("./HarmonyImportGuard").GuardFrame} GuardFrame */
|
|
48
51
|
|
|
49
|
-
/** @typedef {Map<string, Set<string>>} Guards Map of import root to guarded member keys */
|
|
50
|
-
|
|
51
|
-
const getHarmonyImportGuard = memoize(() => require("./HarmonyImportGuard"));
|
|
52
|
-
|
|
53
52
|
const harmonySpecifierTag = Symbol("harmony import");
|
|
54
53
|
|
|
55
54
|
// Shared placeholder: plain specifier references have no member ranges, so they
|
|
@@ -137,6 +136,12 @@ const findImportSpecifier = (parser, node) => {
|
|
|
137
136
|
parser,
|
|
138
137
|
/** @type {Expression} */ (node.object)
|
|
139
138
|
);
|
|
139
|
+
case "BinaryExpression":
|
|
140
|
+
// `"x" in ns` presence guard
|
|
141
|
+
return (
|
|
142
|
+
node.operator === "in" &&
|
|
143
|
+
findImportSpecifier(parser, /** @type {Expression} */ (node.right))
|
|
144
|
+
);
|
|
140
145
|
default:
|
|
141
146
|
return false;
|
|
142
147
|
}
|
|
@@ -175,21 +180,18 @@ module.exports = class HarmonyImportDependencyParserPlugin {
|
|
|
175
180
|
);
|
|
176
181
|
if (!harmonySettings) return this.exportPresenceMode;
|
|
177
182
|
|
|
183
|
+
if (this.exportPresenceMode === ExportPresenceModes.NONE) {
|
|
184
|
+
return this.exportPresenceMode;
|
|
185
|
+
}
|
|
186
|
+
|
|
178
187
|
const stack = /** @type {GuardFrame[] | undefined} */ (
|
|
179
188
|
parser.state.guardStack
|
|
180
189
|
);
|
|
181
|
-
if (
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (presence !== undefined) {
|
|
187
|
-
const members = presence.get(name);
|
|
188
|
-
if (members !== undefined && members.has(member)) {
|
|
189
|
-
return ExportPresenceModes.NONE;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
190
|
+
if (
|
|
191
|
+
stack !== undefined &&
|
|
192
|
+
isPresentByGuards(parser, stack, harmonySettings.name, ids[0])
|
|
193
|
+
) {
|
|
194
|
+
return ExportPresenceModes.NONE;
|
|
193
195
|
}
|
|
194
196
|
|
|
195
197
|
return this.exportPresenceMode;
|
|
@@ -354,7 +356,7 @@ module.exports = class HarmonyImportDependencyParserPlugin {
|
|
|
354
356
|
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
|
|
355
357
|
dep.call = parser.scope.inTaggedTemplateTag;
|
|
356
358
|
parser.state.module.addDependency(dep);
|
|
357
|
-
|
|
359
|
+
attachDependencyGuards(parser, dep);
|
|
358
360
|
getInnerGraphUtils(parser.state.compilation).onUsage(
|
|
359
361
|
parser.state,
|
|
360
362
|
(e) => (dep.usedByExports = e)
|
|
@@ -406,7 +408,7 @@ module.exports = class HarmonyImportDependencyParserPlugin {
|
|
|
406
408
|
);
|
|
407
409
|
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
|
|
408
410
|
parser.state.module.addDependency(dep);
|
|
409
|
-
|
|
411
|
+
attachDependencyGuards(parser, dep);
|
|
410
412
|
getInnerGraphUtils(parser.state.compilation).onUsage(
|
|
411
413
|
parser.state,
|
|
412
414
|
(e) => (dep.usedByExports = e)
|
|
@@ -463,7 +465,7 @@ module.exports = class HarmonyImportDependencyParserPlugin {
|
|
|
463
465
|
/** @type {boolean} */ (this.strictThisContextOnImports);
|
|
464
466
|
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
|
|
465
467
|
parser.state.module.addDependency(dep);
|
|
466
|
-
|
|
468
|
+
attachDependencyGuards(parser, dep);
|
|
467
469
|
if (args) parser.walkExpressions(args);
|
|
468
470
|
getInnerGraphUtils(parser.state.compilation).onUsage(
|
|
469
471
|
parser.state,
|
|
@@ -543,90 +545,15 @@ module.exports = class HarmonyImportDependencyParserPlugin {
|
|
|
543
545
|
parser.hooks.collectGuards.tap(PLUGIN_NAME, (expression) => {
|
|
544
546
|
if (parser.scope.isAsmJs) return;
|
|
545
547
|
|
|
546
|
-
// Presence guards: `"x" in ns` checks that suppress export-presence errors
|
|
547
|
-
// (consequent only).
|
|
548
|
-
/** @type {Guards | undefined} */
|
|
549
|
-
let presence;
|
|
550
|
-
if (this.exportPresenceMode !== ExportPresenceModes.NONE) {
|
|
551
|
-
/** @type {Guards} */
|
|
552
|
-
const guards = new Map();
|
|
553
|
-
|
|
554
|
-
/**
|
|
555
|
-
* Processes the provided expression.
|
|
556
|
-
* @param {Expression} expression expression
|
|
557
|
-
* @param {boolean} needTruthy need to be truthy
|
|
558
|
-
*/
|
|
559
|
-
const collect = (expression, needTruthy) => {
|
|
560
|
-
if (
|
|
561
|
-
expression.type === "UnaryExpression" &&
|
|
562
|
-
expression.operator === "!"
|
|
563
|
-
) {
|
|
564
|
-
collect(expression.argument, !needTruthy);
|
|
565
|
-
return;
|
|
566
|
-
} else if (expression.type === "LogicalExpression" && needTruthy) {
|
|
567
|
-
if (expression.operator === "&&") {
|
|
568
|
-
collect(expression.left, true);
|
|
569
|
-
collect(expression.right, true);
|
|
570
|
-
} else if (expression.operator === "||") {
|
|
571
|
-
const leftEvaluation = parser.evaluateExpression(expression.left);
|
|
572
|
-
const leftBool = leftEvaluation.asBool();
|
|
573
|
-
if (leftBool === false) {
|
|
574
|
-
collect(expression.right, true);
|
|
575
|
-
}
|
|
576
|
-
} else if (expression.operator === "??") {
|
|
577
|
-
const leftEvaluation = parser.evaluateExpression(expression.left);
|
|
578
|
-
const leftNullish = leftEvaluation.asNullish();
|
|
579
|
-
if (leftNullish === true) {
|
|
580
|
-
collect(expression.right, true);
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
return;
|
|
584
|
-
}
|
|
585
|
-
if (!needTruthy) return;
|
|
586
|
-
|
|
587
|
-
// Direct `"x" in ns` guards
|
|
588
|
-
if (
|
|
589
|
-
expression.type === "BinaryExpression" &&
|
|
590
|
-
expression.operator === "in"
|
|
591
|
-
) {
|
|
592
|
-
if (expression.right.type !== "Identifier") {
|
|
593
|
-
return;
|
|
594
|
-
}
|
|
595
|
-
const info = getInOperatorHarmonyImportInfo(
|
|
596
|
-
parser,
|
|
597
|
-
expression.left,
|
|
598
|
-
expression.right
|
|
599
|
-
);
|
|
600
|
-
if (!info) return;
|
|
601
|
-
|
|
602
|
-
const { settings, leftPart, members } = info;
|
|
603
|
-
// Only direct namespace guards
|
|
604
|
-
if (members.length > 0) return;
|
|
605
|
-
const guarded = guards.get(settings.name);
|
|
606
|
-
if (guarded) {
|
|
607
|
-
guarded.add(leftPart);
|
|
608
|
-
return;
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
guards.set(settings.name, new Set([leftPart]));
|
|
612
|
-
}
|
|
613
|
-
};
|
|
614
|
-
|
|
615
|
-
collect(expression, true);
|
|
616
|
-
|
|
617
|
-
if (guards.size > 0) presence = guards;
|
|
618
|
-
}
|
|
619
|
-
|
|
620
548
|
const hasSpecifier = findImportSpecifier(parser, expression);
|
|
621
549
|
const depStart = hasSpecifier
|
|
622
550
|
? /** @type {Module} */ (parser.state.module).dependencies.length
|
|
623
551
|
: undefined;
|
|
624
552
|
|
|
625
|
-
if (
|
|
553
|
+
if (depStart === undefined) return;
|
|
626
554
|
|
|
627
555
|
/** @type {GuardFrame} */
|
|
628
556
|
const consequent = {
|
|
629
|
-
presence,
|
|
630
557
|
test: expression,
|
|
631
558
|
depStart,
|
|
632
559
|
condition: true
|
|
@@ -15,16 +15,19 @@ const memoize = require("../util/memoize");
|
|
|
15
15
|
/** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
|
|
16
16
|
/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
|
|
17
17
|
/** @typedef {import("./CommonJsRequireDependency")} CommonJsRequireDependency */
|
|
18
|
+
/** @typedef {import("./HarmonyEvaluatedImportSpecifierDependency")} HarmonyEvaluatedImportSpecifierDependency */
|
|
18
19
|
/** @typedef {import("./HarmonyImportSpecifierDependency")} HarmonyImportSpecifierDependency */
|
|
19
20
|
/** @typedef {import("./ImportDependency")} ImportDependency */
|
|
20
21
|
|
|
21
22
|
// Tuple-encoded boolean formula over guard dependencies. Split into per-shape
|
|
22
23
|
// aliases so the recursive references resolve through array/object types.
|
|
24
|
+
// `v` = dependency-liveness atom, `p` = `"x" in ns` presence atom.
|
|
23
25
|
/** @typedef {["v", Dependency]} GuardAtom */
|
|
26
|
+
/** @typedef {["p", HarmonyEvaluatedImportSpecifierDependency]} GuardPresenceAtom */
|
|
24
27
|
/** @typedef {["?"]} GuardUnknown */
|
|
25
28
|
/** @typedef {["!", GuardFormula]} GuardNot */
|
|
26
29
|
/** @typedef {["&&" | "||" | "??", GuardFormula, GuardFormula]} GuardLogical */
|
|
27
|
-
/** @typedef {GuardAtom | GuardUnknown | GuardNot | GuardLogical} GuardFormula */
|
|
30
|
+
/** @typedef {GuardAtom | GuardPresenceAtom | GuardUnknown | GuardNot | GuardLogical} GuardFormula */
|
|
28
31
|
/** @typedef {{ formula: GuardFormula, value: boolean }} DependencyGuard branch guard: the dependency is live only when the formula evaluates to `value` */
|
|
29
32
|
/** @typedef {HarmonyImportSpecifierDependency | CommonJsRequireDependency | ImportDependency} GuardableDependency */
|
|
30
33
|
|
|
@@ -33,16 +36,19 @@ const memoize = require("../util/memoize");
|
|
|
33
36
|
* conditional branch body. Carries the `"x" in ns` presence guards and/or the
|
|
34
37
|
* dead-branch dependency guard for that branch.
|
|
35
38
|
* @typedef {object} GuardFrame
|
|
36
|
-
* @property {Map<string, Set<string>>=} presence `"x" in ns` presence guards active in this branch
|
|
37
39
|
* @property {Expression=} test the conditional test (for the lazily built formula)
|
|
38
40
|
* @property {number=} depStart dependency count before the test was walked
|
|
39
41
|
* @property {boolean=} condition branch truthiness the dependency guard is live for
|
|
40
|
-
* @property {GuardFormula | null=} formula memoized formula (null = no knowable atom)
|
|
42
|
+
* @property {GuardFormula | null=} formula memoized dependency-guard formula (null = no knowable atom)
|
|
43
|
+
* @property {GuardFormula | null=} presenceFormula memoized presence formula (null = no presence atom)
|
|
41
44
|
*/
|
|
42
45
|
|
|
43
46
|
const getHarmonyImportSpecifierDependency = memoize(() =>
|
|
44
47
|
require("./HarmonyImportSpecifierDependency")
|
|
45
48
|
);
|
|
49
|
+
const getHarmonyEvaluatedImportSpecifierDependency = memoize(() =>
|
|
50
|
+
require("./HarmonyEvaluatedImportSpecifierDependency")
|
|
51
|
+
);
|
|
46
52
|
|
|
47
53
|
// Tri-state truthiness: UNKNOWN means not statically known.
|
|
48
54
|
const UNKNOWN = 0;
|
|
@@ -62,12 +68,12 @@ const flip = (t) => (t === TRUE ? FALSE : t === FALSE ? TRUE : UNKNOWN);
|
|
|
62
68
|
const fromBool = (b) => (b ? TRUE : FALSE);
|
|
63
69
|
|
|
64
70
|
/**
|
|
65
|
-
* Build a
|
|
71
|
+
* Build a liveness formula from a conditional test AST.
|
|
66
72
|
* @param {Expression} test conditional test expression
|
|
67
73
|
* @param {Map<number, HarmonyImportSpecifierDependency>} depByRangeStart import-specifier deps in the test, keyed by range start
|
|
68
74
|
* @returns {GuardFormula | null} formula, or null when it has no knowable atom
|
|
69
75
|
*/
|
|
70
|
-
const
|
|
76
|
+
const buildLivenessFormula = (test, depByRangeStart) => {
|
|
71
77
|
/**
|
|
72
78
|
* @param {Expression} node ast node
|
|
73
79
|
* @returns {GuardFormula} formula node
|
|
@@ -104,6 +110,7 @@ const buildGuardFormula = (test, depByRangeStart) => {
|
|
|
104
110
|
const hasAtom = (formula) => {
|
|
105
111
|
switch (formula[0]) {
|
|
106
112
|
case "v":
|
|
113
|
+
case "p":
|
|
107
114
|
return true;
|
|
108
115
|
case "!":
|
|
109
116
|
return hasAtom(formula[1]);
|
|
@@ -122,7 +129,7 @@ const hasAtom = (formula) => {
|
|
|
122
129
|
* @param {RuntimeSpec} runtime runtime
|
|
123
130
|
* @returns {{ t: number, n: number }} truthy `t` and null `n` tri-states
|
|
124
131
|
*/
|
|
125
|
-
const
|
|
132
|
+
const evalLivenessFormula = (formula, moduleGraph, runtime) => {
|
|
126
133
|
switch (formula[0]) {
|
|
127
134
|
case "v": {
|
|
128
135
|
const dep = /** @type {HarmonyImportSpecifierDependency} */ (formula[1]);
|
|
@@ -145,12 +152,12 @@ const evalNode = (formula, moduleGraph, runtime) => {
|
|
|
145
152
|
case "!":
|
|
146
153
|
// `!x` always yields a boolean → never nullish.
|
|
147
154
|
return {
|
|
148
|
-
t: flip(
|
|
155
|
+
t: flip(evalLivenessFormula(formula[1], moduleGraph, runtime).t),
|
|
149
156
|
n: FALSE
|
|
150
157
|
};
|
|
151
158
|
case "&&": {
|
|
152
|
-
const l =
|
|
153
|
-
const r =
|
|
159
|
+
const l = evalLivenessFormula(formula[1], moduleGraph, runtime);
|
|
160
|
+
const r = evalLivenessFormula(formula[2], moduleGraph, runtime);
|
|
154
161
|
const t =
|
|
155
162
|
l.t === FALSE || r.t === FALSE
|
|
156
163
|
? FALSE
|
|
@@ -160,8 +167,8 @@ const evalNode = (formula, moduleGraph, runtime) => {
|
|
|
160
167
|
return { t, n: UNKNOWN };
|
|
161
168
|
}
|
|
162
169
|
case "||": {
|
|
163
|
-
const l =
|
|
164
|
-
const r =
|
|
170
|
+
const l = evalLivenessFormula(formula[1], moduleGraph, runtime);
|
|
171
|
+
const r = evalLivenessFormula(formula[2], moduleGraph, runtime);
|
|
165
172
|
const t =
|
|
166
173
|
l.t === TRUE || r.t === TRUE
|
|
167
174
|
? TRUE
|
|
@@ -171,8 +178,8 @@ const evalNode = (formula, moduleGraph, runtime) => {
|
|
|
171
178
|
return { t, n: UNKNOWN };
|
|
172
179
|
}
|
|
173
180
|
case "??": {
|
|
174
|
-
const l =
|
|
175
|
-
const r =
|
|
181
|
+
const l = evalLivenessFormula(formula[1], moduleGraph, runtime);
|
|
182
|
+
const r = evalLivenessFormula(formula[2], moduleGraph, runtime);
|
|
176
183
|
// `l ?? r` is l when l non-nullish, else r.
|
|
177
184
|
const t =
|
|
178
185
|
l.n === FALSE ? l.t : l.n === TRUE ? r.t : l.t === r.t ? l.t : UNKNOWN;
|
|
@@ -192,37 +199,201 @@ const evalNode = (formula, moduleGraph, runtime) => {
|
|
|
192
199
|
*/
|
|
193
200
|
const isDeadByGuards = (guards, moduleGraph, runtime) => {
|
|
194
201
|
for (const guard of guards) {
|
|
195
|
-
const t =
|
|
202
|
+
const t = evalLivenessFormula(guard.formula, moduleGraph, runtime).t;
|
|
196
203
|
if (t !== UNKNOWN && t !== (guard.value ? TRUE : FALSE)) return true;
|
|
197
204
|
}
|
|
198
205
|
return false;
|
|
199
206
|
};
|
|
200
207
|
|
|
201
208
|
/**
|
|
202
|
-
* Builds (once) the
|
|
203
|
-
*
|
|
209
|
+
* Builds (once) the liveness formula for a frame from the import specifier deps
|
|
210
|
+
* created while walking the test.
|
|
204
211
|
* @param {JavascriptParser} parser the parser
|
|
205
212
|
* @param {GuardFrame} frame guard frame
|
|
206
213
|
* @returns {GuardFormula | null} formula, or null when it has no knowable atom
|
|
207
214
|
*/
|
|
208
|
-
const
|
|
215
|
+
const buildFrameLivenessFormula = (parser, frame) => {
|
|
209
216
|
const HarmonyImportSpecifierDependency =
|
|
210
217
|
getHarmonyImportSpecifierDependency();
|
|
218
|
+
const HarmonyEvaluatedImportSpecifierDependency =
|
|
219
|
+
getHarmonyEvaluatedImportSpecifierDependency();
|
|
211
220
|
const deps = /** @type {Module} */ (parser.state.module).dependencies;
|
|
212
221
|
/** @type {Map<number, HarmonyImportSpecifierDependency>} */
|
|
213
222
|
const depByRangeStart = new Map();
|
|
214
223
|
for (let i = /** @type {number} */ (frame.depStart); i < deps.length; i++) {
|
|
215
224
|
const dep = deps[i];
|
|
216
|
-
|
|
225
|
+
// Exclude `"x" in ns` evaluated deps; those feed the presence formula only.
|
|
226
|
+
if (
|
|
227
|
+
dep instanceof HarmonyImportSpecifierDependency &&
|
|
228
|
+
!(dep instanceof HarmonyEvaluatedImportSpecifierDependency) &&
|
|
229
|
+
dep.range
|
|
230
|
+
) {
|
|
231
|
+
depByRangeStart.set(dep.range[0], dep);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return buildLivenessFormula(
|
|
235
|
+
/** @type {Expression} */ (frame.test),
|
|
236
|
+
depByRangeStart
|
|
237
|
+
);
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Build a presence formula from a conditional test AST. Statically-decided
|
|
242
|
+
* `||`/`??` operands are folded away at build time, so the result contains only
|
|
243
|
+
* `"x" in ns` presence atoms (plus `&&`/`!`).
|
|
244
|
+
* @param {JavascriptParser} parser the parser
|
|
245
|
+
* @param {Expression} test conditional test expression
|
|
246
|
+
* @param {Map<number, HarmonyEvaluatedImportSpecifierDependency>} depByRangeStart in-operator deps in the test, keyed by range start
|
|
247
|
+
* @returns {GuardFormula | null} formula, or null when it has no presence atom
|
|
248
|
+
*/
|
|
249
|
+
const buildPresenceFormula = (parser, test, depByRangeStart) => {
|
|
250
|
+
/**
|
|
251
|
+
* @param {Expression} node ast node
|
|
252
|
+
* @returns {GuardFormula} formula node
|
|
253
|
+
*/
|
|
254
|
+
const build = (node) => {
|
|
255
|
+
if (node.type === "UnaryExpression" && node.operator === "!") {
|
|
256
|
+
return ["!", build(/** @type {Expression} */ (node.argument))];
|
|
257
|
+
}
|
|
258
|
+
if (node.type === "LogicalExpression") {
|
|
259
|
+
if (node.operator === "&&") {
|
|
260
|
+
return [
|
|
261
|
+
"&&",
|
|
262
|
+
build(/** @type {Expression} */ (node.left)),
|
|
263
|
+
build(/** @type {Expression} */ (node.right))
|
|
264
|
+
];
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (node.operator === "||") {
|
|
268
|
+
if (parser.evaluateExpression(node.left).asBool() === false) {
|
|
269
|
+
return build(/** @type {Expression} */ (node.right));
|
|
270
|
+
}
|
|
271
|
+
if (parser.evaluateExpression(node.right).asBool() === false) {
|
|
272
|
+
return build(/** @type {Expression} */ (node.left));
|
|
273
|
+
}
|
|
274
|
+
return ["?"];
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (node.operator === "??") {
|
|
278
|
+
const nullish = parser.evaluateExpression(node.left).asNullish();
|
|
279
|
+
if (nullish === true) {
|
|
280
|
+
return build(/** @type {Expression} */ (node.right));
|
|
281
|
+
}
|
|
282
|
+
if (nullish === false) {
|
|
283
|
+
return build(/** @type {Expression} */ (node.left));
|
|
284
|
+
}
|
|
285
|
+
return ["?"];
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
const dep =
|
|
289
|
+
node.range && depByRangeStart.get(/** @type {number} */ (node.range[0]));
|
|
290
|
+
return dep ? ["p", dep] : ["?"];
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
const formula = build(test);
|
|
294
|
+
return hasAtom(formula) ? formula : null;
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Whether a presence formula guarantees `ns.member` is present. "Must be truthy"
|
|
299
|
+
* semantics: the member is guaranteed when some `"member" in ns` atom must hold
|
|
300
|
+
* for the branch condition. Statically-dead branches never reach here — the
|
|
301
|
+
* parser eliminates them before the branch body is walked.
|
|
302
|
+
* @param {GuardFormula} formula presence formula
|
|
303
|
+
* @param {string} name namespace binding name
|
|
304
|
+
* @param {string} member member key
|
|
305
|
+
* @param {boolean} needTruthy whether the formula must be truthy
|
|
306
|
+
* @returns {boolean} true when the member is guaranteed present
|
|
307
|
+
*/
|
|
308
|
+
const evalPresenceFormula = (formula, name, member, needTruthy) => {
|
|
309
|
+
switch (formula[0]) {
|
|
310
|
+
case "p": {
|
|
311
|
+
const dep = /** @type {HarmonyEvaluatedImportSpecifierDependency} */ (
|
|
312
|
+
formula[1]
|
|
313
|
+
);
|
|
314
|
+
return (
|
|
315
|
+
needTruthy &&
|
|
316
|
+
dep.directImport === true &&
|
|
317
|
+
dep.name === name &&
|
|
318
|
+
dep.ids.length === 1 &&
|
|
319
|
+
dep.ids[0] === member
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
case "!":
|
|
323
|
+
return evalPresenceFormula(formula[1], name, member, !needTruthy);
|
|
324
|
+
case "&&":
|
|
325
|
+
return (
|
|
326
|
+
needTruthy &&
|
|
327
|
+
(evalPresenceFormula(formula[1], name, member, true) ||
|
|
328
|
+
evalPresenceFormula(formula[2], name, member, true))
|
|
329
|
+
);
|
|
330
|
+
default:
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Builds (once) the presence formula for a frame from the in-operator deps
|
|
337
|
+
* created while walking the test.
|
|
338
|
+
* @param {JavascriptParser} parser the parser
|
|
339
|
+
* @param {GuardFrame} frame guard frame
|
|
340
|
+
* @returns {GuardFormula | null} formula, or null when it has no presence atom
|
|
341
|
+
*/
|
|
342
|
+
const buildFramePresenceFormula = (parser, frame) => {
|
|
343
|
+
const HarmonyEvaluatedImportSpecifierDependency =
|
|
344
|
+
getHarmonyEvaluatedImportSpecifierDependency();
|
|
345
|
+
const deps = /** @type {Module} */ (parser.state.module).dependencies;
|
|
346
|
+
/** @type {Map<number, HarmonyEvaluatedImportSpecifierDependency>} */
|
|
347
|
+
const depByRangeStart = new Map();
|
|
348
|
+
for (let i = /** @type {number} */ (frame.depStart); i < deps.length; i++) {
|
|
349
|
+
const dep = deps[i];
|
|
350
|
+
if (dep instanceof HarmonyEvaluatedImportSpecifierDependency && dep.range) {
|
|
217
351
|
depByRangeStart.set(dep.range[0], dep);
|
|
218
352
|
}
|
|
219
353
|
}
|
|
220
|
-
return
|
|
354
|
+
return buildPresenceFormula(
|
|
355
|
+
parser,
|
|
221
356
|
/** @type {Expression} */ (frame.test),
|
|
222
357
|
depByRangeStart
|
|
223
358
|
);
|
|
224
359
|
};
|
|
225
360
|
|
|
361
|
+
/**
|
|
362
|
+
* Whether an active presence guard proves `ns.member` present, suppressing
|
|
363
|
+
* export-presence errors. Each frame is evaluated against its branch condition,
|
|
364
|
+
* so the `else` of `if (!("x" in ns))` also guards `ns.x`.
|
|
365
|
+
* @param {JavascriptParser} parser the parser
|
|
366
|
+
* @param {GuardFrame[]} stack the guard stack
|
|
367
|
+
* @param {string} name namespace binding name
|
|
368
|
+
* @param {string} member member key
|
|
369
|
+
* @returns {boolean} true when a guard proves the member present
|
|
370
|
+
*/
|
|
371
|
+
const isPresentByGuards = (parser, stack, name, member) => {
|
|
372
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
373
|
+
const frame = stack[i];
|
|
374
|
+
if (frame.depStart === undefined) continue;
|
|
375
|
+
let formula = frame.presenceFormula;
|
|
376
|
+
if (formula === undefined) {
|
|
377
|
+
formula = frame.presenceFormula = buildFramePresenceFormula(
|
|
378
|
+
parser,
|
|
379
|
+
frame
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
if (
|
|
383
|
+
formula !== null &&
|
|
384
|
+
evalPresenceFormula(
|
|
385
|
+
formula,
|
|
386
|
+
name,
|
|
387
|
+
member,
|
|
388
|
+
/** @type {boolean} */ (frame.condition)
|
|
389
|
+
)
|
|
390
|
+
) {
|
|
391
|
+
return true;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return false;
|
|
395
|
+
};
|
|
396
|
+
|
|
226
397
|
/**
|
|
227
398
|
* Tags a freshly created dependency with the active dependency guards.
|
|
228
399
|
* @param {JavascriptParser} parser the parser
|
|
@@ -239,7 +410,7 @@ const attachDependencyGuards = (parser, dep) => {
|
|
|
239
410
|
if (frame.depStart === undefined) continue;
|
|
240
411
|
let formula = frame.formula;
|
|
241
412
|
if (formula === undefined) {
|
|
242
|
-
formula = frame.formula =
|
|
413
|
+
formula = frame.formula = buildFrameLivenessFormula(parser, frame);
|
|
243
414
|
}
|
|
244
415
|
if (formula === null) continue;
|
|
245
416
|
(guards || (guards = [])).push({
|
|
@@ -252,3 +423,4 @@ const attachDependencyGuards = (parser, dep) => {
|
|
|
252
423
|
|
|
253
424
|
module.exports.attachDependencyGuards = attachDependencyGuards;
|
|
254
425
|
module.exports.isDeadByGuards = isDeadByGuards;
|
|
426
|
+
module.exports.isPresentByGuards = isPresentByGuards;
|