webpack 5.99.5 → 5.99.7

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.
Files changed (169) hide show
  1. package/README.md +1 -1
  2. package/lib/APIPlugin.js +2 -2
  3. package/lib/AutomaticPrefetchPlugin.js +21 -22
  4. package/lib/BannerPlugin.js +31 -35
  5. package/lib/Cache.js +1 -1
  6. package/lib/Chunk.js +12 -9
  7. package/lib/CleanPlugin.js +5 -3
  8. package/lib/Compilation.js +45 -35
  9. package/lib/Compiler.js +6 -4
  10. package/lib/ContextExclusionPlugin.js +4 -2
  11. package/lib/ContextModule.js +2 -1
  12. package/lib/ContextReplacementPlugin.js +5 -3
  13. package/lib/DelegatedPlugin.js +4 -2
  14. package/lib/DllEntryPlugin.js +4 -2
  15. package/lib/DllPlugin.js +5 -3
  16. package/lib/DllReferencePlugin.js +56 -60
  17. package/lib/DynamicEntryPlugin.js +4 -2
  18. package/lib/EntryOptionPlugin.js +3 -1
  19. package/lib/EntryPlugin.js +4 -2
  20. package/lib/EnvironmentPlugin.js +4 -2
  21. package/lib/EvalDevToolModulePlugin.js +9 -7
  22. package/lib/EvalSourceMapDevToolPlugin.js +137 -138
  23. package/lib/ExternalsPlugin.js +3 -1
  24. package/lib/FlagDependencyExportsPlugin.js +2 -1
  25. package/lib/HotModuleReplacementPlugin.js +3 -3
  26. package/lib/IgnorePlugin.js +6 -4
  27. package/lib/IgnoreWarningsPlugin.js +4 -2
  28. package/lib/LibManifestPlugin.js +3 -4
  29. package/lib/LoaderOptionsPlugin.js +4 -2
  30. package/lib/LoaderTargetPlugin.js +4 -2
  31. package/lib/Module.js +29 -14
  32. package/lib/ModuleFilenameHelpers.js +1 -1
  33. package/lib/ModuleGraph.js +15 -10
  34. package/lib/ModuleInfoHeaderPlugin.js +11 -12
  35. package/lib/MultiCompiler.js +5 -3
  36. package/lib/NoEmitOnErrorsPlugin.js +5 -3
  37. package/lib/NormalModule.js +6 -2
  38. package/lib/NormalModuleReplacementPlugin.js +33 -36
  39. package/lib/PlatformPlugin.js +3 -1
  40. package/lib/PrefetchPlugin.js +5 -3
  41. package/lib/ProgressPlugin.js +23 -26
  42. package/lib/RecordIdsPlugin.js +73 -103
  43. package/lib/RuntimePlugin.js +34 -32
  44. package/lib/SourceMapDevToolPlugin.js +8 -6
  45. package/lib/Template.js +1 -1
  46. package/lib/WarnCaseSensitiveModulesPlugin.js +36 -37
  47. package/lib/WarnNoModeSetPlugin.js +3 -1
  48. package/lib/WatchIgnorePlugin.js +3 -1
  49. package/lib/WebpackError.js +11 -3
  50. package/lib/WebpackOptionsApply.js +22 -5
  51. package/lib/async-modules/InferAsyncModulesPlugin.js +25 -26
  52. package/lib/buildChunkGraph.js +7 -2
  53. package/lib/cache/IdleFileCachePlugin.js +12 -13
  54. package/lib/cache/MemoryCachePlugin.js +2 -1
  55. package/lib/cache/MemoryWithGcCachePlugin.js +10 -7
  56. package/lib/cache/PackFileCacheStrategy.js +13 -21
  57. package/lib/cache/ResolverCachePlugin.js +22 -22
  58. package/lib/cli.js +8 -4
  59. package/lib/config/defaults.js +2 -2
  60. package/lib/config/normalization.js +9 -3
  61. package/lib/config/target.js +6 -6
  62. package/lib/container/ContainerReferencePlugin.js +24 -26
  63. package/lib/container/ModuleFederationPlugin.js +2 -1
  64. package/lib/css/CssGenerator.js +1 -1
  65. package/lib/css/CssModulesPlugin.js +6 -4
  66. package/lib/css/CssParser.js +1 -1
  67. package/lib/debug/ProfilingPlugin.js +1 -1
  68. package/lib/dependencies/ContextDependencyHelpers.js +1 -1
  69. package/lib/dependencies/CssIcssImportDependency.js +3 -2
  70. package/lib/dependencies/HarmonyDetectionParserPlugin.js +9 -15
  71. package/lib/dependencies/HarmonyExportExpressionDependency.js +1 -1
  72. package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +2 -1
  73. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +1 -1
  74. package/lib/dependencies/HarmonyTopLevelThisParserPlugin.js +15 -15
  75. package/lib/dependencies/ImportMetaContextDependencyParserPlugin.js +4 -2
  76. package/lib/dependencies/JsonExportsDependency.js +1 -1
  77. package/lib/dependencies/LoaderPlugin.js +5 -3
  78. package/lib/dependencies/LocalModulesHelpers.js +1 -1
  79. package/lib/dependencies/RequireContextDependencyParserPlugin.js +46 -46
  80. package/lib/dependencies/RequireEnsureDependenciesBlockParserPlugin.js +94 -100
  81. package/lib/dependencies/RequireResolveDependency.js +1 -1
  82. package/lib/dependencies/WorkerPlugin.js +2 -2
  83. package/lib/esm/ModuleChunkLoadingPlugin.js +76 -57
  84. package/lib/esm/ModuleChunkLoadingRuntimeModule.js +11 -9
  85. package/lib/hmr/HotModuleReplacement.runtime.js +0 -1
  86. package/lib/hmr/JavascriptHotModuleReplacement.runtime.js +0 -1
  87. package/lib/hmr/LazyCompilationPlugin.js +17 -18
  88. package/lib/ids/ChunkModuleIdRangePlugin.js +6 -6
  89. package/lib/ids/DeterministicChunkIdsPlugin.js +1 -1
  90. package/lib/ids/DeterministicModuleIdsPlugin.js +48 -49
  91. package/lib/ids/HashedModuleIdsPlugin.js +4 -2
  92. package/lib/ids/NamedChunkIdsPlugin.js +6 -4
  93. package/lib/ids/NamedModuleIdsPlugin.js +6 -4
  94. package/lib/ids/NaturalChunkIdsPlugin.js +4 -2
  95. package/lib/ids/NaturalModuleIdsPlugin.js +4 -2
  96. package/lib/ids/OccurrenceChunkIdsPlugin.js +4 -2
  97. package/lib/ids/OccurrenceModuleIdsPlugin.js +4 -2
  98. package/lib/index.js +1 -1
  99. package/lib/javascript/BasicEvaluatedExpression.js +2 -2
  100. package/lib/javascript/JavascriptParser.js +670 -688
  101. package/lib/library/EnableLibraryPlugin.js +15 -2
  102. package/lib/library/ModuleLibraryPlugin.js +66 -43
  103. package/lib/logging/createConsoleLogger.js +0 -1
  104. package/lib/node/CommonJsChunkLoadingPlugin.js +71 -75
  105. package/lib/node/NodeEnvironmentPlugin.js +3 -1
  106. package/lib/node/NodeTemplatePlugin.js +2 -2
  107. package/lib/node/ReadFileCompileAsyncWasmPlugin.js +2 -2
  108. package/lib/node/ReadFileCompileWasmPlugin.js +3 -3
  109. package/lib/optimize/AggressiveMergingPlugin.js +1 -1
  110. package/lib/optimize/AggressiveSplittingPlugin.js +224 -232
  111. package/lib/optimize/ConcatenatedModule.js +12 -15
  112. package/lib/optimize/FlagIncludedChunksPlugin.js +92 -97
  113. package/lib/optimize/LimitChunkCountPlugin.js +4 -2
  114. package/lib/optimize/MangleExportsPlugin.js +15 -16
  115. package/lib/optimize/MinChunkSizePlugin.js +4 -2
  116. package/lib/optimize/ModuleConcatenationPlugin.js +4 -2
  117. package/lib/optimize/RealContentHashPlugin.js +4 -2
  118. package/lib/optimize/RemoveEmptyChunksPlugin.js +5 -3
  119. package/lib/optimize/RemoveParentModulesPlugin.js +4 -2
  120. package/lib/optimize/RuntimeChunkPlugin.js +17 -18
  121. package/lib/optimize/SplitChunksPlugin.js +9 -6
  122. package/lib/performance/SizeLimitsPlugin.js +3 -1
  123. package/lib/prefetch/ChunkPrefetchPreloadPlugin.js +61 -62
  124. package/lib/runtime/GetChunkFilenameRuntimeModule.js +3 -4
  125. package/lib/runtime/StartupChunkDependenciesPlugin.js +39 -42
  126. package/lib/schemes/DataUriPlugin.js +5 -3
  127. package/lib/schemes/FileUriPlugin.js +5 -3
  128. package/lib/schemes/HttpUriPlugin.js +32 -39
  129. package/lib/serialization/AggregateErrorSerializer.js +42 -0
  130. package/lib/serialization/BinaryMiddleware.js +22 -38
  131. package/lib/serialization/ErrorObjectSerializer.js +7 -2
  132. package/lib/serialization/FileMiddleware.js +29 -33
  133. package/lib/serialization/ObjectMiddleware.js +42 -30
  134. package/lib/serialization/Serializer.js +29 -18
  135. package/lib/serialization/SerializerMiddleware.js +105 -72
  136. package/lib/serialization/SingleItemMiddleware.js +4 -5
  137. package/lib/sharing/ProvideSharedPlugin.js +6 -4
  138. package/lib/stats/DefaultStatsFactoryPlugin.js +128 -57
  139. package/lib/stats/DefaultStatsPresetPlugin.js +25 -20
  140. package/lib/stats/DefaultStatsPrinterPlugin.js +486 -334
  141. package/lib/stats/StatsFactory.js +47 -10
  142. package/lib/stats/StatsPrinter.js +52 -31
  143. package/lib/util/ArrayQueue.js +1 -1
  144. package/lib/util/AsyncQueue.js +1 -1
  145. package/lib/util/TupleQueue.js +9 -7
  146. package/lib/util/TupleSet.js +37 -18
  147. package/lib/util/WeakTupleMap.js +50 -37
  148. package/lib/util/cleverMerge.js +2 -2
  149. package/lib/util/comparators.js +1 -1
  150. package/lib/util/concatenate.js +4 -2
  151. package/lib/util/createHash.js +1 -1
  152. package/lib/util/fs.js +1 -1
  153. package/lib/util/makeSerializable.js +1 -1
  154. package/lib/util/runtime.js +1 -0
  155. package/lib/util/serialization.js +50 -42
  156. package/lib/wasm-async/AsyncWebAssemblyGenerator.js +1 -1
  157. package/lib/wasm-async/AsyncWebAssemblyModulesPlugin.js +1 -1
  158. package/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js +2 -2
  159. package/lib/wasm-sync/WasmFinalizeExportsPlugin.js +55 -57
  160. package/lib/wasm-sync/WebAssemblyGenerator.js +1 -1
  161. package/lib/wasm-sync/WebAssemblyModulesPlugin.js +1 -1
  162. package/lib/web/FetchCompileWasmPlugin.js +2 -2
  163. package/lib/web/JsonpChunkLoadingPlugin.js +73 -74
  164. package/lib/webpack.js +1 -1
  165. package/lib/webworker/ImportScriptsChunkLoadingPlugin.js +77 -78
  166. package/package.json +4 -3
  167. package/schemas/WebpackOptions.check.js +1 -1
  168. package/schemas/WebpackOptions.json +24 -2
  169. package/types.d.ts +273 -128
@@ -280,10 +280,13 @@ class VariableInfo {
280
280
  /**
281
281
  * @typedef {object} TagInfo
282
282
  * @property {Tag} tag
283
- * @property {TagData} [data]
283
+ * @property {TagData=} data
284
284
  * @property {TagInfo | undefined} next
285
285
  */
286
286
 
287
+ const SCOPE_INFO_TERMINATED_RETURN = 1;
288
+ const SCOPE_INFO_TERMINATED_THROW = 2;
289
+
287
290
  /**
288
291
  * @typedef {object} ScopeInfo
289
292
  * @property {StackedMap<string, VariableInfo | ScopeInfo>} definitions
@@ -293,8 +296,7 @@ class VariableInfo {
293
296
  * @property {boolean} inTry
294
297
  * @property {boolean} isStrict
295
298
  * @property {boolean} isAsmJs
296
- * @property {boolean} inExecutedPath false for unknown state
297
- * @property {undefined|"return"|"throw"} terminated
299
+ * @property {undefined | 1 | 2} terminated
298
300
  */
299
301
 
300
302
  /** @typedef {[number, number]} Range */
@@ -310,9 +312,9 @@ class VariableInfo {
310
312
  * Helper function for joining two ranges into a single range. This is useful
311
313
  * when working with AST nodes, as it allows you to combine the ranges of child nodes
312
314
  * to create the range of the _parent node_.
313
- * @param {[number, number]} startRange start range to join
314
- * @param {[number, number]} endRange end range to join
315
- * @returns {[number, number]} joined range
315
+ * @param {Range} startRange start range to join
316
+ * @param {Range} endRange end range to join
317
+ * @returns {Range} joined range
316
318
  * @example
317
319
  * ```js
318
320
  * const startRange = [0, 5];
@@ -387,6 +389,8 @@ const EMPTY_COMMENT_OPTIONS = {
387
389
  errors: null
388
390
  };
389
391
 
392
+ const CLASS_NAME = "JavascriptParser";
393
+
390
394
  class JavascriptParser extends Parser {
391
395
  /**
392
396
  * @param {"module" | "script" | "auto"} sourceType default source type
@@ -598,7 +602,7 @@ class JavascriptParser extends Parser {
598
602
  }
599
603
 
600
604
  _initializeEvaluating() {
601
- this.hooks.evaluate.for("Literal").tap("JavascriptParser", _expr => {
605
+ this.hooks.evaluate.for("Literal").tap(CLASS_NAME, _expr => {
602
606
  const expr = /** @type {Literal} */ (_expr);
603
607
 
604
608
  switch (typeof expr.value) {
@@ -630,7 +634,7 @@ class JavascriptParser extends Parser {
630
634
  .setRange(/** @type {Range} */ (expr.range));
631
635
  }
632
636
  });
633
- this.hooks.evaluate.for("NewExpression").tap("JavascriptParser", _expr => {
637
+ this.hooks.evaluate.for("NewExpression").tap(CLASS_NAME, _expr => {
634
638
  const expr = /** @type {NewExpression} */ (_expr);
635
639
  const callee = expr.callee;
636
640
  if (callee.type !== "Identifier") return;
@@ -693,52 +697,50 @@ class JavascriptParser extends Parser {
693
697
  .setRegExp(flags ? new RegExp(regExp, flags) : new RegExp(regExp))
694
698
  .setRange(/** @type {Range} */ (expr.range));
695
699
  });
696
- this.hooks.evaluate
697
- .for("LogicalExpression")
698
- .tap("JavascriptParser", _expr => {
699
- const expr = /** @type {LogicalExpression} */ (_expr);
700
-
701
- const left = this.evaluateExpression(expr.left);
702
- let returnRight = false;
703
- /** @type {boolean | undefined} */
704
- let allowedRight;
705
- if (expr.operator === "&&") {
706
- const leftAsBool = left.asBool();
707
- if (leftAsBool === false)
708
- return left.setRange(/** @type {Range} */ (expr.range));
709
- returnRight = leftAsBool === true;
710
- allowedRight = false;
711
- } else if (expr.operator === "||") {
712
- const leftAsBool = left.asBool();
713
- if (leftAsBool === true)
714
- return left.setRange(/** @type {Range} */ (expr.range));
715
- returnRight = leftAsBool === false;
716
- allowedRight = true;
717
- } else if (expr.operator === "??") {
718
- const leftAsNullish = left.asNullish();
719
- if (leftAsNullish === false)
720
- return left.setRange(/** @type {Range} */ (expr.range));
721
- if (leftAsNullish !== true) return;
722
- returnRight = true;
723
- } else return;
724
- const right = this.evaluateExpression(expr.right);
725
- if (returnRight) {
726
- if (left.couldHaveSideEffects()) right.setSideEffects();
727
- return right.setRange(/** @type {Range} */ (expr.range));
728
- }
700
+ this.hooks.evaluate.for("LogicalExpression").tap(CLASS_NAME, _expr => {
701
+ const expr = /** @type {LogicalExpression} */ (_expr);
702
+
703
+ const left = this.evaluateExpression(expr.left);
704
+ let returnRight = false;
705
+ /** @type {boolean | undefined} */
706
+ let allowedRight;
707
+ if (expr.operator === "&&") {
708
+ const leftAsBool = left.asBool();
709
+ if (leftAsBool === false)
710
+ return left.setRange(/** @type {Range} */ (expr.range));
711
+ returnRight = leftAsBool === true;
712
+ allowedRight = false;
713
+ } else if (expr.operator === "||") {
714
+ const leftAsBool = left.asBool();
715
+ if (leftAsBool === true)
716
+ return left.setRange(/** @type {Range} */ (expr.range));
717
+ returnRight = leftAsBool === false;
718
+ allowedRight = true;
719
+ } else if (expr.operator === "??") {
720
+ const leftAsNullish = left.asNullish();
721
+ if (leftAsNullish === false)
722
+ return left.setRange(/** @type {Range} */ (expr.range));
723
+ if (leftAsNullish !== true) return;
724
+ returnRight = true;
725
+ } else return;
726
+ const right = this.evaluateExpression(expr.right);
727
+ if (returnRight) {
728
+ if (left.couldHaveSideEffects()) right.setSideEffects();
729
+ return right.setRange(/** @type {Range} */ (expr.range));
730
+ }
729
731
 
730
- const asBool = right.asBool();
732
+ const asBool = right.asBool();
731
733
 
732
- if (allowedRight === true && asBool === true) {
733
- return new BasicEvaluatedExpression()
734
- .setRange(/** @type {Range} */ (expr.range))
735
- .setTruthy();
736
- } else if (allowedRight === false && asBool === false) {
737
- return new BasicEvaluatedExpression()
738
- .setRange(/** @type {Range} */ (expr.range))
739
- .setFalsy();
740
- }
741
- });
734
+ if (allowedRight === true && asBool === true) {
735
+ return new BasicEvaluatedExpression()
736
+ .setRange(/** @type {Range} */ (expr.range))
737
+ .setTruthy();
738
+ } else if (allowedRight === false && asBool === false) {
739
+ return new BasicEvaluatedExpression()
740
+ .setRange(/** @type {Range} */ (expr.range))
741
+ .setFalsy();
742
+ }
743
+ });
742
744
 
743
745
  /**
744
746
  * In simple logical cases, we can use valueAsExpression to assist us in evaluating the expression on
@@ -808,551 +810,539 @@ class JavascriptParser extends Parser {
808
810
  }
809
811
  };
810
812
 
811
- this.hooks.evaluate
812
- .for("BinaryExpression")
813
- .tap("JavascriptParser", _expr => {
814
- const expr = /** @type {BinaryExpression} */ (_expr);
815
-
816
- /**
817
- * Evaluates a binary expression if and only if it is a const operation (e.g. 1 + 2, "a" + "b", etc.).
818
- * @template T
819
- * @param {(leftOperand: T, rightOperand: T) => boolean | number | bigint | string} operandHandler the handler for the operation (e.g. (a, b) => a + b)
820
- * @returns {BasicEvaluatedExpression | undefined} the evaluated expression
821
- */
822
- const handleConstOperation = operandHandler => {
823
- const left = this.evaluateExpression(expr.left);
824
- if (!left.isCompileTimeValue()) return;
813
+ this.hooks.evaluate.for("BinaryExpression").tap(CLASS_NAME, _expr => {
814
+ const expr = /** @type {BinaryExpression} */ (_expr);
825
815
 
826
- const right = this.evaluateExpression(expr.right);
827
- if (!right.isCompileTimeValue()) return;
816
+ /**
817
+ * Evaluates a binary expression if and only if it is a const operation (e.g. 1 + 2, "a" + "b", etc.).
818
+ * @template T
819
+ * @param {(leftOperand: T, rightOperand: T) => boolean | number | bigint | string} operandHandler the handler for the operation (e.g. (a, b) => a + b)
820
+ * @returns {BasicEvaluatedExpression | undefined} the evaluated expression
821
+ */
822
+ const handleConstOperation = operandHandler => {
823
+ const left = this.evaluateExpression(expr.left);
824
+ if (!left.isCompileTimeValue()) return;
828
825
 
829
- const result = operandHandler(
830
- left.asCompileTimeValue(),
831
- right.asCompileTimeValue()
832
- );
833
- return valueAsExpression(
834
- result,
835
- expr,
836
- left.couldHaveSideEffects() || right.couldHaveSideEffects()
837
- );
838
- };
826
+ const right = this.evaluateExpression(expr.right);
827
+ if (!right.isCompileTimeValue()) return;
839
828
 
840
- /**
841
- * Helper function to determine if two booleans are always different. This is used in `handleStrictEqualityComparison`
842
- * to determine if an expressions boolean or nullish conversion is equal or not.
843
- * @param {boolean} a first boolean to compare
844
- * @param {boolean} b second boolean to compare
845
- * @returns {boolean} true if the two booleans are always different, false otherwise
846
- */
847
- const isAlwaysDifferent = (a, b) =>
848
- (a === true && b === false) || (a === false && b === true);
829
+ const result = operandHandler(
830
+ /** @type {T} */ (left.asCompileTimeValue()),
831
+ /** @type {T} */ (right.asCompileTimeValue())
832
+ );
833
+ return valueAsExpression(
834
+ result,
835
+ expr,
836
+ left.couldHaveSideEffects() || right.couldHaveSideEffects()
837
+ );
838
+ };
849
839
 
840
+ /**
841
+ * Helper function to determine if two booleans are always different. This is used in `handleStrictEqualityComparison`
842
+ * to determine if an expressions boolean or nullish conversion is equal or not.
843
+ * @param {boolean} a first boolean to compare
844
+ * @param {boolean} b second boolean to compare
845
+ * @returns {boolean} true if the two booleans are always different, false otherwise
846
+ */
847
+ const isAlwaysDifferent = (a, b) =>
848
+ (a === true && b === false) || (a === false && b === true);
849
+
850
+ /**
851
+ * @param {BasicEvaluatedExpression} left left
852
+ * @param {BasicEvaluatedExpression} right right
853
+ * @param {BasicEvaluatedExpression} res res
854
+ * @param {boolean} eql true for "===" and false for "!=="
855
+ * @returns {BasicEvaluatedExpression | undefined} result
856
+ */
857
+ const handleTemplateStringCompare = (left, right, res, eql) => {
850
858
  /**
851
- * @param {BasicEvaluatedExpression} left left
852
- * @param {BasicEvaluatedExpression} right right
853
- * @param {BasicEvaluatedExpression} res res
854
- * @param {boolean} eql true for "===" and false for "!=="
855
- * @returns {BasicEvaluatedExpression | undefined} result
859
+ * @param {BasicEvaluatedExpression[]} parts parts
860
+ * @returns {string} value
856
861
  */
857
- const handleTemplateStringCompare = (left, right, res, eql) => {
858
- /**
859
- * @param {BasicEvaluatedExpression[]} parts parts
860
- * @returns {string} value
861
- */
862
- const getPrefix = parts => {
863
- let value = "";
864
- for (const p of parts) {
865
- const v = p.asString();
866
- if (v !== undefined) value += v;
867
- else break;
868
- }
869
- return value;
870
- };
871
- /**
872
- * @param {BasicEvaluatedExpression[]} parts parts
873
- * @returns {string} value
874
- */
875
- const getSuffix = parts => {
876
- let value = "";
877
- for (let i = parts.length - 1; i >= 0; i--) {
878
- const v = parts[i].asString();
879
- if (v !== undefined) value = v + value;
880
- else break;
881
- }
882
- return value;
883
- };
884
- const leftPrefix = getPrefix(
885
- /** @type {BasicEvaluatedExpression[]} */ (left.parts)
886
- );
887
- const rightPrefix = getPrefix(
888
- /** @type {BasicEvaluatedExpression[]} */ (right.parts)
889
- );
890
- const leftSuffix = getSuffix(
891
- /** @type {BasicEvaluatedExpression[]} */ (left.parts)
892
- );
893
- const rightSuffix = getSuffix(
894
- /** @type {BasicEvaluatedExpression[]} */ (right.parts)
895
- );
896
- const lenPrefix = Math.min(leftPrefix.length, rightPrefix.length);
897
- const lenSuffix = Math.min(leftSuffix.length, rightSuffix.length);
898
- const prefixMismatch =
899
- lenPrefix > 0 &&
900
- leftPrefix.slice(0, lenPrefix) !== rightPrefix.slice(0, lenPrefix);
901
- const suffixMismatch =
902
- lenSuffix > 0 &&
903
- leftSuffix.slice(-lenSuffix) !== rightSuffix.slice(-lenSuffix);
904
- if (prefixMismatch || suffixMismatch) {
905
- return res
906
- .setBoolean(!eql)
907
- .setSideEffects(
908
- left.couldHaveSideEffects() || right.couldHaveSideEffects()
909
- );
862
+ const getPrefix = parts => {
863
+ let value = "";
864
+ for (const p of parts) {
865
+ const v = p.asString();
866
+ if (v !== undefined) value += v;
867
+ else break;
910
868
  }
869
+ return value;
911
870
  };
912
-
913
871
  /**
914
- * Helper function to handle BinaryExpressions using strict equality comparisons (e.g. "===" and "!==").
915
- * @param {boolean} eql true for "===" and false for "!=="
916
- * @returns {BasicEvaluatedExpression | undefined} the evaluated expression
872
+ * @param {BasicEvaluatedExpression[]} parts parts
873
+ * @returns {string} value
917
874
  */
918
- const handleStrictEqualityComparison = eql => {
919
- const left = this.evaluateExpression(expr.left);
920
- const right = this.evaluateExpression(expr.right);
921
- const res = new BasicEvaluatedExpression();
922
- res.setRange(/** @type {Range} */ (expr.range));
923
-
924
- const leftConst = left.isCompileTimeValue();
925
- const rightConst = right.isCompileTimeValue();
926
-
927
- if (leftConst && rightConst) {
928
- return res
929
- .setBoolean(
930
- eql ===
931
- (left.asCompileTimeValue() === right.asCompileTimeValue())
932
- )
933
- .setSideEffects(
934
- left.couldHaveSideEffects() || right.couldHaveSideEffects()
935
- );
875
+ const getSuffix = parts => {
876
+ let value = "";
877
+ for (let i = parts.length - 1; i >= 0; i--) {
878
+ const v = parts[i].asString();
879
+ if (v !== undefined) value = v + value;
880
+ else break;
936
881
  }
882
+ return value;
883
+ };
884
+ const leftPrefix = getPrefix(
885
+ /** @type {BasicEvaluatedExpression[]} */ (left.parts)
886
+ );
887
+ const rightPrefix = getPrefix(
888
+ /** @type {BasicEvaluatedExpression[]} */ (right.parts)
889
+ );
890
+ const leftSuffix = getSuffix(
891
+ /** @type {BasicEvaluatedExpression[]} */ (left.parts)
892
+ );
893
+ const rightSuffix = getSuffix(
894
+ /** @type {BasicEvaluatedExpression[]} */ (right.parts)
895
+ );
896
+ const lenPrefix = Math.min(leftPrefix.length, rightPrefix.length);
897
+ const lenSuffix = Math.min(leftSuffix.length, rightSuffix.length);
898
+ const prefixMismatch =
899
+ lenPrefix > 0 &&
900
+ leftPrefix.slice(0, lenPrefix) !== rightPrefix.slice(0, lenPrefix);
901
+ const suffixMismatch =
902
+ lenSuffix > 0 &&
903
+ leftSuffix.slice(-lenSuffix) !== rightSuffix.slice(-lenSuffix);
904
+ if (prefixMismatch || suffixMismatch) {
905
+ return res
906
+ .setBoolean(!eql)
907
+ .setSideEffects(
908
+ left.couldHaveSideEffects() || right.couldHaveSideEffects()
909
+ );
910
+ }
911
+ };
937
912
 
938
- if (left.isArray() && right.isArray()) {
939
- return res
940
- .setBoolean(!eql)
941
- .setSideEffects(
942
- left.couldHaveSideEffects() || right.couldHaveSideEffects()
943
- );
944
- }
945
- if (left.isTemplateString() && right.isTemplateString()) {
946
- return handleTemplateStringCompare(left, right, res, eql);
947
- }
913
+ /**
914
+ * Helper function to handle BinaryExpressions using strict equality comparisons (e.g. "===" and "!==").
915
+ * @param {boolean} eql true for "===" and false for "!=="
916
+ * @returns {BasicEvaluatedExpression | undefined} the evaluated expression
917
+ */
918
+ const handleStrictEqualityComparison = eql => {
919
+ const left = this.evaluateExpression(expr.left);
920
+ const right = this.evaluateExpression(expr.right);
921
+ const res = new BasicEvaluatedExpression();
922
+ res.setRange(/** @type {Range} */ (expr.range));
948
923
 
949
- const leftPrimitive = left.isPrimitiveType();
950
- const rightPrimitive = right.isPrimitiveType();
924
+ const leftConst = left.isCompileTimeValue();
925
+ const rightConst = right.isCompileTimeValue();
951
926
 
952
- if (
953
- // Primitive !== Object or
954
- // compile-time object types are never equal to something at runtime
955
- (leftPrimitive === false &&
956
- (leftConst || rightPrimitive === true)) ||
957
- (rightPrimitive === false &&
958
- (rightConst || leftPrimitive === true)) ||
959
- // Different nullish or boolish status also means not equal
960
- isAlwaysDifferent(
961
- /** @type {boolean} */ (left.asBool()),
962
- /** @type {boolean} */ (right.asBool())
963
- ) ||
964
- isAlwaysDifferent(
965
- /** @type {boolean} */ (left.asNullish()),
966
- /** @type {boolean} */ (right.asNullish())
927
+ if (leftConst && rightConst) {
928
+ return res
929
+ .setBoolean(
930
+ eql === (left.asCompileTimeValue() === right.asCompileTimeValue())
967
931
  )
968
- ) {
969
- return res
970
- .setBoolean(!eql)
971
- .setSideEffects(
972
- left.couldHaveSideEffects() || right.couldHaveSideEffects()
973
- );
974
- }
975
- };
932
+ .setSideEffects(
933
+ left.couldHaveSideEffects() || right.couldHaveSideEffects()
934
+ );
935
+ }
976
936
 
977
- /**
978
- * Helper function to handle BinaryExpressions using abstract equality comparisons (e.g. "==" and "!=").
979
- * @param {boolean} eql true for "==" and false for "!="
980
- * @returns {BasicEvaluatedExpression | undefined} the evaluated expression
981
- */
982
- const handleAbstractEqualityComparison = eql => {
983
- const left = this.evaluateExpression(expr.left);
984
- const right = this.evaluateExpression(expr.right);
985
- const res = new BasicEvaluatedExpression();
986
- res.setRange(/** @type {Range} */ (expr.range));
987
-
988
- const leftConst = left.isCompileTimeValue();
989
- const rightConst = right.isCompileTimeValue();
990
-
991
- if (leftConst && rightConst) {
992
- return res
993
- .setBoolean(
994
- eql ===
995
- // eslint-disable-next-line eqeqeq
996
- (left.asCompileTimeValue() == right.asCompileTimeValue())
997
- )
998
- .setSideEffects(
999
- left.couldHaveSideEffects() || right.couldHaveSideEffects()
1000
- );
1001
- }
937
+ if (left.isArray() && right.isArray()) {
938
+ return res
939
+ .setBoolean(!eql)
940
+ .setSideEffects(
941
+ left.couldHaveSideEffects() || right.couldHaveSideEffects()
942
+ );
943
+ }
944
+ if (left.isTemplateString() && right.isTemplateString()) {
945
+ return handleTemplateStringCompare(left, right, res, eql);
946
+ }
1002
947
 
1003
- if (left.isArray() && right.isArray()) {
1004
- return res
1005
- .setBoolean(!eql)
1006
- .setSideEffects(
1007
- left.couldHaveSideEffects() || right.couldHaveSideEffects()
1008
- );
1009
- }
1010
- if (left.isTemplateString() && right.isTemplateString()) {
1011
- return handleTemplateStringCompare(left, right, res, eql);
1012
- }
1013
- };
948
+ const leftPrimitive = left.isPrimitiveType();
949
+ const rightPrimitive = right.isPrimitiveType();
1014
950
 
1015
- if (expr.operator === "+") {
1016
- const left = this.evaluateExpression(expr.left);
1017
- const right = this.evaluateExpression(expr.right);
1018
- const res = new BasicEvaluatedExpression();
1019
- if (left.isString()) {
1020
- if (right.isString()) {
1021
- res.setString(
1022
- /** @type {string} */ (left.string) +
1023
- /** @type {string} */ (right.string)
1024
- );
1025
- } else if (right.isNumber()) {
1026
- res.setString(/** @type {string} */ (left.string) + right.number);
1027
- } else if (
1028
- right.isWrapped() &&
1029
- right.prefix &&
1030
- right.prefix.isString()
1031
- ) {
1032
- // "left" + ("prefix" + inner + "postfix")
1033
- // => ("leftPrefix" + inner + "postfix")
1034
- res.setWrapped(
1035
- new BasicEvaluatedExpression()
1036
- .setString(
1037
- /** @type {string} */ (left.string) +
1038
- /** @type {string} */ (right.prefix.string)
1039
- )
1040
- .setRange(
1041
- joinRanges(
1042
- /** @type {Range} */ (left.range),
1043
- /** @type {Range} */ (right.prefix.range)
1044
- )
1045
- ),
1046
- right.postfix,
1047
- right.wrappedInnerExpressions
1048
- );
1049
- } else if (right.isWrapped()) {
1050
- // "left" + ([null] + inner + "postfix")
1051
- // => ("left" + inner + "postfix")
1052
- res.setWrapped(
1053
- left,
1054
- right.postfix,
1055
- right.wrappedInnerExpressions
1056
- );
1057
- } else {
1058
- // "left" + expr
1059
- // => ("left" + expr + "")
1060
- res.setWrapped(left, null, [right]);
1061
- }
1062
- } else if (left.isNumber()) {
1063
- if (right.isString()) {
1064
- res.setString(left.number + /** @type {string} */ (right.string));
1065
- } else if (right.isNumber()) {
1066
- res.setNumber(
1067
- /** @type {number} */ (left.number) +
1068
- /** @type {number} */ (right.number)
1069
- );
1070
- } else {
1071
- return;
1072
- }
1073
- } else if (left.isBigInt()) {
1074
- if (right.isBigInt()) {
1075
- res.setBigInt(
1076
- /** @type {bigint} */ (left.bigint) +
1077
- /** @type {bigint} */ (right.bigint)
1078
- );
1079
- }
1080
- } else if (left.isWrapped()) {
1081
- if (left.postfix && left.postfix.isString() && right.isString()) {
1082
- // ("prefix" + inner + "postfix") + "right"
1083
- // => ("prefix" + inner + "postfixRight")
1084
- res.setWrapped(
1085
- left.prefix,
1086
- new BasicEvaluatedExpression()
1087
- .setString(
1088
- /** @type {string} */ (left.postfix.string) +
1089
- /** @type {string} */ (right.string)
951
+ if (
952
+ // Primitive !== Object or
953
+ // compile-time object types are never equal to something at runtime
954
+ (leftPrimitive === false && (leftConst || rightPrimitive === true)) ||
955
+ (rightPrimitive === false &&
956
+ (rightConst || leftPrimitive === true)) ||
957
+ // Different nullish or boolish status also means not equal
958
+ isAlwaysDifferent(
959
+ /** @type {boolean} */ (left.asBool()),
960
+ /** @type {boolean} */ (right.asBool())
961
+ ) ||
962
+ isAlwaysDifferent(
963
+ /** @type {boolean} */ (left.asNullish()),
964
+ /** @type {boolean} */ (right.asNullish())
965
+ )
966
+ ) {
967
+ return res
968
+ .setBoolean(!eql)
969
+ .setSideEffects(
970
+ left.couldHaveSideEffects() || right.couldHaveSideEffects()
971
+ );
972
+ }
973
+ };
974
+
975
+ /**
976
+ * Helper function to handle BinaryExpressions using abstract equality comparisons (e.g. "==" and "!=").
977
+ * @param {boolean} eql true for "==" and false for "!="
978
+ * @returns {BasicEvaluatedExpression | undefined} the evaluated expression
979
+ */
980
+ const handleAbstractEqualityComparison = eql => {
981
+ const left = this.evaluateExpression(expr.left);
982
+ const right = this.evaluateExpression(expr.right);
983
+ const res = new BasicEvaluatedExpression();
984
+ res.setRange(/** @type {Range} */ (expr.range));
985
+
986
+ const leftConst = left.isCompileTimeValue();
987
+ const rightConst = right.isCompileTimeValue();
988
+
989
+ if (leftConst && rightConst) {
990
+ return res
991
+ .setBoolean(
992
+ eql ===
993
+ // eslint-disable-next-line eqeqeq
994
+ (left.asCompileTimeValue() == right.asCompileTimeValue())
995
+ )
996
+ .setSideEffects(
997
+ left.couldHaveSideEffects() || right.couldHaveSideEffects()
998
+ );
999
+ }
1000
+
1001
+ if (left.isArray() && right.isArray()) {
1002
+ return res
1003
+ .setBoolean(!eql)
1004
+ .setSideEffects(
1005
+ left.couldHaveSideEffects() || right.couldHaveSideEffects()
1006
+ );
1007
+ }
1008
+ if (left.isTemplateString() && right.isTemplateString()) {
1009
+ return handleTemplateStringCompare(left, right, res, eql);
1010
+ }
1011
+ };
1012
+
1013
+ if (expr.operator === "+") {
1014
+ const left = this.evaluateExpression(expr.left);
1015
+ const right = this.evaluateExpression(expr.right);
1016
+ const res = new BasicEvaluatedExpression();
1017
+ if (left.isString()) {
1018
+ if (right.isString()) {
1019
+ res.setString(
1020
+ /** @type {string} */ (left.string) +
1021
+ /** @type {string} */ (right.string)
1022
+ );
1023
+ } else if (right.isNumber()) {
1024
+ res.setString(/** @type {string} */ (left.string) + right.number);
1025
+ } else if (
1026
+ right.isWrapped() &&
1027
+ right.prefix &&
1028
+ right.prefix.isString()
1029
+ ) {
1030
+ // "left" + ("prefix" + inner + "postfix")
1031
+ // => ("leftPrefix" + inner + "postfix")
1032
+ res.setWrapped(
1033
+ new BasicEvaluatedExpression()
1034
+ .setString(
1035
+ /** @type {string} */ (left.string) +
1036
+ /** @type {string} */ (right.prefix.string)
1037
+ )
1038
+ .setRange(
1039
+ joinRanges(
1040
+ /** @type {Range} */ (left.range),
1041
+ /** @type {Range} */ (right.prefix.range)
1090
1042
  )
1091
- .setRange(
1092
- joinRanges(
1093
- /** @type {Range} */ (left.postfix.range),
1094
- /** @type {Range} */ (right.range)
1095
- )
1096
- ),
1097
- left.wrappedInnerExpressions
1098
- );
1099
- } else if (
1100
- left.postfix &&
1101
- left.postfix.isString() &&
1102
- right.isNumber()
1103
- ) {
1104
- // ("prefix" + inner + "postfix") + 123
1105
- // => ("prefix" + inner + "postfix123")
1106
- res.setWrapped(
1107
- left.prefix,
1108
- new BasicEvaluatedExpression()
1109
- .setString(
1110
- /** @type {string} */ (left.postfix.string) +
1111
- /** @type {number} */ (right.number)
1043
+ ),
1044
+ right.postfix,
1045
+ right.wrappedInnerExpressions
1046
+ );
1047
+ } else if (right.isWrapped()) {
1048
+ // "left" + ([null] + inner + "postfix")
1049
+ // => ("left" + inner + "postfix")
1050
+ res.setWrapped(left, right.postfix, right.wrappedInnerExpressions);
1051
+ } else {
1052
+ // "left" + expr
1053
+ // => ("left" + expr + "")
1054
+ res.setWrapped(left, null, [right]);
1055
+ }
1056
+ } else if (left.isNumber()) {
1057
+ if (right.isString()) {
1058
+ res.setString(left.number + /** @type {string} */ (right.string));
1059
+ } else if (right.isNumber()) {
1060
+ res.setNumber(
1061
+ /** @type {number} */ (left.number) +
1062
+ /** @type {number} */ (right.number)
1063
+ );
1064
+ } else {
1065
+ return;
1066
+ }
1067
+ } else if (left.isBigInt()) {
1068
+ if (right.isBigInt()) {
1069
+ res.setBigInt(
1070
+ /** @type {bigint} */ (left.bigint) +
1071
+ /** @type {bigint} */ (right.bigint)
1072
+ );
1073
+ }
1074
+ } else if (left.isWrapped()) {
1075
+ if (left.postfix && left.postfix.isString() && right.isString()) {
1076
+ // ("prefix" + inner + "postfix") + "right"
1077
+ // => ("prefix" + inner + "postfixRight")
1078
+ res.setWrapped(
1079
+ left.prefix,
1080
+ new BasicEvaluatedExpression()
1081
+ .setString(
1082
+ /** @type {string} */ (left.postfix.string) +
1083
+ /** @type {string} */ (right.string)
1084
+ )
1085
+ .setRange(
1086
+ joinRanges(
1087
+ /** @type {Range} */ (left.postfix.range),
1088
+ /** @type {Range} */ (right.range)
1112
1089
  )
1113
- .setRange(
1114
- joinRanges(
1115
- /** @type {Range} */ (left.postfix.range),
1116
- /** @type {Range} */ (right.range)
1117
- )
1118
- ),
1119
- left.wrappedInnerExpressions
1120
- );
1121
- } else if (right.isString()) {
1122
- // ("prefix" + inner + [null]) + "right"
1123
- // => ("prefix" + inner + "right")
1124
- res.setWrapped(left.prefix, right, left.wrappedInnerExpressions);
1125
- } else if (right.isNumber()) {
1126
- // ("prefix" + inner + [null]) + 123
1127
- // => ("prefix" + inner + "123")
1128
- res.setWrapped(
1129
- left.prefix,
1130
- new BasicEvaluatedExpression()
1131
- .setString(String(right.number))
1132
- .setRange(/** @type {Range} */ (right.range)),
1133
- left.wrappedInnerExpressions
1134
- );
1135
- } else if (right.isWrapped()) {
1136
- // ("prefix1" + inner1 + "postfix1") + ("prefix2" + inner2 + "postfix2")
1137
- // ("prefix1" + inner1 + "postfix1" + "prefix2" + inner2 + "postfix2")
1138
- res.setWrapped(
1139
- left.prefix,
1140
- right.postfix,
1141
- left.wrappedInnerExpressions &&
1142
- right.wrappedInnerExpressions &&
1143
- left.wrappedInnerExpressions
1144
- .concat(left.postfix ? [left.postfix] : [])
1145
- .concat(right.prefix ? [right.prefix] : [])
1146
- .concat(right.wrappedInnerExpressions)
1147
- );
1148
- } else {
1149
- // ("prefix" + inner + postfix) + expr
1150
- // => ("prefix" + inner + postfix + expr + [null])
1151
- res.setWrapped(
1152
- left.prefix,
1153
- null,
1154
- left.wrappedInnerExpressions &&
1155
- left.wrappedInnerExpressions.concat(
1156
- left.postfix ? [left.postfix, right] : [right]
1090
+ ),
1091
+ left.wrappedInnerExpressions
1092
+ );
1093
+ } else if (
1094
+ left.postfix &&
1095
+ left.postfix.isString() &&
1096
+ right.isNumber()
1097
+ ) {
1098
+ // ("prefix" + inner + "postfix") + 123
1099
+ // => ("prefix" + inner + "postfix123")
1100
+ res.setWrapped(
1101
+ left.prefix,
1102
+ new BasicEvaluatedExpression()
1103
+ .setString(
1104
+ /** @type {string} */ (left.postfix.string) +
1105
+ /** @type {number} */ (right.number)
1106
+ )
1107
+ .setRange(
1108
+ joinRanges(
1109
+ /** @type {Range} */ (left.postfix.range),
1110
+ /** @type {Range} */ (right.range)
1157
1111
  )
1158
- );
1159
- }
1112
+ ),
1113
+ left.wrappedInnerExpressions
1114
+ );
1160
1115
  } else if (right.isString()) {
1161
- // left + "right"
1162
- // => ([null] + left + "right")
1163
- res.setWrapped(null, right, [left]);
1116
+ // ("prefix" + inner + [null]) + "right"
1117
+ // => ("prefix" + inner + "right")
1118
+ res.setWrapped(left.prefix, right, left.wrappedInnerExpressions);
1119
+ } else if (right.isNumber()) {
1120
+ // ("prefix" + inner + [null]) + 123
1121
+ // => ("prefix" + inner + "123")
1122
+ res.setWrapped(
1123
+ left.prefix,
1124
+ new BasicEvaluatedExpression()
1125
+ .setString(String(right.number))
1126
+ .setRange(/** @type {Range} */ (right.range)),
1127
+ left.wrappedInnerExpressions
1128
+ );
1164
1129
  } else if (right.isWrapped()) {
1165
- // left + (prefix + inner + "postfix")
1166
- // => ([null] + left + prefix + inner + "postfix")
1130
+ // ("prefix1" + inner1 + "postfix1") + ("prefix2" + inner2 + "postfix2")
1131
+ // ("prefix1" + inner1 + "postfix1" + "prefix2" + inner2 + "postfix2")
1167
1132
  res.setWrapped(
1168
- null,
1133
+ left.prefix,
1169
1134
  right.postfix,
1170
- right.wrappedInnerExpressions &&
1171
- (right.prefix ? [left, right.prefix] : [left]).concat(
1172
- right.wrappedInnerExpressions
1173
- )
1135
+ left.wrappedInnerExpressions &&
1136
+ right.wrappedInnerExpressions &&
1137
+ left.wrappedInnerExpressions
1138
+ .concat(left.postfix ? [left.postfix] : [])
1139
+ .concat(right.prefix ? [right.prefix] : [])
1140
+ .concat(right.wrappedInnerExpressions)
1174
1141
  );
1175
1142
  } else {
1176
- return;
1143
+ // ("prefix" + inner + postfix) + expr
1144
+ // => ("prefix" + inner + postfix + expr + [null])
1145
+ res.setWrapped(
1146
+ left.prefix,
1147
+ null,
1148
+ left.wrappedInnerExpressions &&
1149
+ left.wrappedInnerExpressions.concat(
1150
+ left.postfix ? [left.postfix, right] : [right]
1151
+ )
1152
+ );
1177
1153
  }
1178
- if (left.couldHaveSideEffects() || right.couldHaveSideEffects())
1179
- res.setSideEffects();
1180
- res.setRange(/** @type {Range} */ (expr.range));
1181
- return res;
1182
- } else if (expr.operator === "-") {
1183
- return handleConstOperation((l, r) => l - r);
1184
- } else if (expr.operator === "*") {
1185
- return handleConstOperation((l, r) => l * r);
1186
- } else if (expr.operator === "/") {
1187
- return handleConstOperation((l, r) => l / r);
1188
- } else if (expr.operator === "**") {
1189
- return handleConstOperation((l, r) => l ** r);
1190
- } else if (expr.operator === "===") {
1191
- return handleStrictEqualityComparison(true);
1192
- } else if (expr.operator === "==") {
1193
- return handleAbstractEqualityComparison(true);
1194
- } else if (expr.operator === "!==") {
1195
- return handleStrictEqualityComparison(false);
1196
- } else if (expr.operator === "!=") {
1197
- return handleAbstractEqualityComparison(false);
1198
- } else if (expr.operator === "&") {
1199
- return handleConstOperation((l, r) => l & r);
1200
- } else if (expr.operator === "|") {
1201
- return handleConstOperation((l, r) => l | r);
1202
- } else if (expr.operator === "^") {
1203
- return handleConstOperation((l, r) => l ^ r);
1204
- } else if (expr.operator === ">>>") {
1205
- return handleConstOperation((l, r) => l >>> r);
1206
- } else if (expr.operator === ">>") {
1207
- return handleConstOperation((l, r) => l >> r);
1208
- } else if (expr.operator === "<<") {
1209
- return handleConstOperation((l, r) => l << r);
1210
- } else if (expr.operator === "<") {
1211
- return handleConstOperation((l, r) => l < r);
1212
- } else if (expr.operator === ">") {
1213
- return handleConstOperation((l, r) => l > r);
1214
- } else if (expr.operator === "<=") {
1215
- return handleConstOperation((l, r) => l <= r);
1216
- } else if (expr.operator === ">=") {
1217
- return handleConstOperation((l, r) => l >= r);
1218
- }
1219
- });
1220
- this.hooks.evaluate
1221
- .for("UnaryExpression")
1222
- .tap("JavascriptParser", _expr => {
1223
- const expr = /** @type {UnaryExpression} */ (_expr);
1224
-
1225
- /**
1226
- * Evaluates a UnaryExpression if and only if it is a basic const operator (e.g. +a, -a, ~a).
1227
- * @template T
1228
- * @param {(operand: T) => boolean | number | bigint | string} operandHandler handler for the operand
1229
- * @returns {BasicEvaluatedExpression | undefined} evaluated expression
1230
- */
1231
- const handleConstOperation = operandHandler => {
1232
- const argument = this.evaluateExpression(expr.argument);
1233
- if (!argument.isCompileTimeValue()) return;
1234
- const result = operandHandler(argument.asCompileTimeValue());
1235
- return valueAsExpression(
1236
- result,
1237
- expr,
1238
- argument.couldHaveSideEffects()
1154
+ } else if (right.isString()) {
1155
+ // left + "right"
1156
+ // => ([null] + left + "right")
1157
+ res.setWrapped(null, right, [left]);
1158
+ } else if (right.isWrapped()) {
1159
+ // left + (prefix + inner + "postfix")
1160
+ // => ([null] + left + prefix + inner + "postfix")
1161
+ res.setWrapped(
1162
+ null,
1163
+ right.postfix,
1164
+ right.wrappedInnerExpressions &&
1165
+ (right.prefix ? [left, right.prefix] : [left]).concat(
1166
+ right.wrappedInnerExpressions
1167
+ )
1239
1168
  );
1240
- };
1169
+ } else {
1170
+ return;
1171
+ }
1172
+ if (left.couldHaveSideEffects() || right.couldHaveSideEffects())
1173
+ res.setSideEffects();
1174
+ res.setRange(/** @type {Range} */ (expr.range));
1175
+ return res;
1176
+ } else if (expr.operator === "-") {
1177
+ return handleConstOperation((l, r) => l - r);
1178
+ } else if (expr.operator === "*") {
1179
+ return handleConstOperation((l, r) => l * r);
1180
+ } else if (expr.operator === "/") {
1181
+ return handleConstOperation((l, r) => l / r);
1182
+ } else if (expr.operator === "**") {
1183
+ return handleConstOperation((l, r) => l ** r);
1184
+ } else if (expr.operator === "===") {
1185
+ return handleStrictEqualityComparison(true);
1186
+ } else if (expr.operator === "==") {
1187
+ return handleAbstractEqualityComparison(true);
1188
+ } else if (expr.operator === "!==") {
1189
+ return handleStrictEqualityComparison(false);
1190
+ } else if (expr.operator === "!=") {
1191
+ return handleAbstractEqualityComparison(false);
1192
+ } else if (expr.operator === "&") {
1193
+ return handleConstOperation((l, r) => l & r);
1194
+ } else if (expr.operator === "|") {
1195
+ return handleConstOperation((l, r) => l | r);
1196
+ } else if (expr.operator === "^") {
1197
+ return handleConstOperation((l, r) => l ^ r);
1198
+ } else if (expr.operator === ">>>") {
1199
+ return handleConstOperation((l, r) => l >>> r);
1200
+ } else if (expr.operator === ">>") {
1201
+ return handleConstOperation((l, r) => l >> r);
1202
+ } else if (expr.operator === "<<") {
1203
+ return handleConstOperation((l, r) => l << r);
1204
+ } else if (expr.operator === "<") {
1205
+ return handleConstOperation((l, r) => l < r);
1206
+ } else if (expr.operator === ">") {
1207
+ return handleConstOperation((l, r) => l > r);
1208
+ } else if (expr.operator === "<=") {
1209
+ return handleConstOperation((l, r) => l <= r);
1210
+ } else if (expr.operator === ">=") {
1211
+ return handleConstOperation((l, r) => l >= r);
1212
+ }
1213
+ });
1214
+ this.hooks.evaluate.for("UnaryExpression").tap(CLASS_NAME, _expr => {
1215
+ const expr = /** @type {UnaryExpression} */ (_expr);
1216
+
1217
+ /**
1218
+ * Evaluates a UnaryExpression if and only if it is a basic const operator (e.g. +a, -a, ~a).
1219
+ * @template T
1220
+ * @param {(operand: T) => boolean | number | bigint | string} operandHandler handler for the operand
1221
+ * @returns {BasicEvaluatedExpression | undefined} evaluated expression
1222
+ */
1223
+ const handleConstOperation = operandHandler => {
1224
+ const argument = this.evaluateExpression(expr.argument);
1225
+ if (!argument.isCompileTimeValue()) return;
1226
+ const result = operandHandler(
1227
+ /** @type {T} */ (argument.asCompileTimeValue())
1228
+ );
1229
+ return valueAsExpression(result, expr, argument.couldHaveSideEffects());
1230
+ };
1241
1231
 
1242
- if (expr.operator === "typeof") {
1243
- switch (expr.argument.type) {
1244
- case "Identifier": {
1245
- const res = this.callHooksForName(
1246
- this.hooks.evaluateTypeof,
1247
- expr.argument.name,
1248
- expr
1249
- );
1250
- if (res !== undefined) return res;
1251
- break;
1252
- }
1253
- case "MetaProperty": {
1254
- const res = this.callHooksForName(
1255
- this.hooks.evaluateTypeof,
1256
- /** @type {string} */
1257
- (getRootName(expr.argument)),
1258
- expr
1259
- );
1260
- if (res !== undefined) return res;
1261
- break;
1262
- }
1263
- case "MemberExpression": {
1264
- const res = this.callHooksForExpression(
1265
- this.hooks.evaluateTypeof,
1266
- expr.argument,
1267
- expr
1268
- );
1269
- if (res !== undefined) return res;
1270
- break;
1271
- }
1272
- case "ChainExpression": {
1273
- const res = this.callHooksForExpression(
1274
- this.hooks.evaluateTypeof,
1275
- expr.argument.expression,
1276
- expr
1277
- );
1278
- if (res !== undefined) return res;
1279
- break;
1280
- }
1281
- case "FunctionExpression": {
1282
- return new BasicEvaluatedExpression()
1283
- .setString("function")
1284
- .setRange(/** @type {Range} */ (expr.range));
1285
- }
1286
- }
1287
- const arg = this.evaluateExpression(expr.argument);
1288
- if (arg.isUnknown()) return;
1289
- if (arg.isString()) {
1290
- return new BasicEvaluatedExpression()
1291
- .setString("string")
1292
- .setRange(/** @type {Range} */ (expr.range));
1293
- }
1294
- if (arg.isWrapped()) {
1295
- return new BasicEvaluatedExpression()
1296
- .setString("string")
1297
- .setSideEffects()
1298
- .setRange(/** @type {Range} */ (expr.range));
1299
- }
1300
- if (arg.isUndefined()) {
1301
- return new BasicEvaluatedExpression()
1302
- .setString("undefined")
1303
- .setRange(/** @type {Range} */ (expr.range));
1304
- }
1305
- if (arg.isNumber()) {
1306
- return new BasicEvaluatedExpression()
1307
- .setString("number")
1308
- .setRange(/** @type {Range} */ (expr.range));
1232
+ if (expr.operator === "typeof") {
1233
+ switch (expr.argument.type) {
1234
+ case "Identifier": {
1235
+ const res = this.callHooksForName(
1236
+ this.hooks.evaluateTypeof,
1237
+ expr.argument.name,
1238
+ expr
1239
+ );
1240
+ if (res !== undefined) return res;
1241
+ break;
1309
1242
  }
1310
- if (arg.isBigInt()) {
1311
- return new BasicEvaluatedExpression()
1312
- .setString("bigint")
1313
- .setRange(/** @type {Range} */ (expr.range));
1243
+ case "MetaProperty": {
1244
+ const res = this.callHooksForName(
1245
+ this.hooks.evaluateTypeof,
1246
+ /** @type {string} */
1247
+ (getRootName(expr.argument)),
1248
+ expr
1249
+ );
1250
+ if (res !== undefined) return res;
1251
+ break;
1314
1252
  }
1315
- if (arg.isBoolean()) {
1316
- return new BasicEvaluatedExpression()
1317
- .setString("boolean")
1318
- .setRange(/** @type {Range} */ (expr.range));
1253
+ case "MemberExpression": {
1254
+ const res = this.callHooksForExpression(
1255
+ this.hooks.evaluateTypeof,
1256
+ expr.argument,
1257
+ expr
1258
+ );
1259
+ if (res !== undefined) return res;
1260
+ break;
1319
1261
  }
1320
- if (arg.isConstArray() || arg.isRegExp() || arg.isNull()) {
1321
- return new BasicEvaluatedExpression()
1322
- .setString("object")
1323
- .setRange(/** @type {Range} */ (expr.range));
1262
+ case "ChainExpression": {
1263
+ const res = this.callHooksForExpression(
1264
+ this.hooks.evaluateTypeof,
1265
+ expr.argument.expression,
1266
+ expr
1267
+ );
1268
+ if (res !== undefined) return res;
1269
+ break;
1324
1270
  }
1325
- if (arg.isArray()) {
1271
+ case "FunctionExpression": {
1326
1272
  return new BasicEvaluatedExpression()
1327
- .setString("object")
1328
- .setSideEffects(arg.couldHaveSideEffects())
1273
+ .setString("function")
1329
1274
  .setRange(/** @type {Range} */ (expr.range));
1330
1275
  }
1331
- } else if (expr.operator === "!") {
1332
- const argument = this.evaluateExpression(expr.argument);
1333
- const bool = argument.asBool();
1334
- if (typeof bool !== "boolean") return;
1276
+ }
1277
+ const arg = this.evaluateExpression(expr.argument);
1278
+ if (arg.isUnknown()) return;
1279
+ if (arg.isString()) {
1335
1280
  return new BasicEvaluatedExpression()
1336
- .setBoolean(!bool)
1337
- .setSideEffects(argument.couldHaveSideEffects())
1281
+ .setString("string")
1338
1282
  .setRange(/** @type {Range} */ (expr.range));
1339
- } else if (expr.operator === "~") {
1340
- return handleConstOperation(v => ~v);
1341
- } else if (expr.operator === "+") {
1342
- // eslint-disable-next-line no-implicit-coercion
1343
- return handleConstOperation(v => +v);
1344
- } else if (expr.operator === "-") {
1345
- return handleConstOperation(v => -v);
1346
1283
  }
1347
- });
1284
+ if (arg.isWrapped()) {
1285
+ return new BasicEvaluatedExpression()
1286
+ .setString("string")
1287
+ .setSideEffects()
1288
+ .setRange(/** @type {Range} */ (expr.range));
1289
+ }
1290
+ if (arg.isUndefined()) {
1291
+ return new BasicEvaluatedExpression()
1292
+ .setString("undefined")
1293
+ .setRange(/** @type {Range} */ (expr.range));
1294
+ }
1295
+ if (arg.isNumber()) {
1296
+ return new BasicEvaluatedExpression()
1297
+ .setString("number")
1298
+ .setRange(/** @type {Range} */ (expr.range));
1299
+ }
1300
+ if (arg.isBigInt()) {
1301
+ return new BasicEvaluatedExpression()
1302
+ .setString("bigint")
1303
+ .setRange(/** @type {Range} */ (expr.range));
1304
+ }
1305
+ if (arg.isBoolean()) {
1306
+ return new BasicEvaluatedExpression()
1307
+ .setString("boolean")
1308
+ .setRange(/** @type {Range} */ (expr.range));
1309
+ }
1310
+ if (arg.isConstArray() || arg.isRegExp() || arg.isNull()) {
1311
+ return new BasicEvaluatedExpression()
1312
+ .setString("object")
1313
+ .setRange(/** @type {Range} */ (expr.range));
1314
+ }
1315
+ if (arg.isArray()) {
1316
+ return new BasicEvaluatedExpression()
1317
+ .setString("object")
1318
+ .setSideEffects(arg.couldHaveSideEffects())
1319
+ .setRange(/** @type {Range} */ (expr.range));
1320
+ }
1321
+ } else if (expr.operator === "!") {
1322
+ const argument = this.evaluateExpression(expr.argument);
1323
+ const bool = argument.asBool();
1324
+ if (typeof bool !== "boolean") return;
1325
+ return new BasicEvaluatedExpression()
1326
+ .setBoolean(!bool)
1327
+ .setSideEffects(argument.couldHaveSideEffects())
1328
+ .setRange(/** @type {Range} */ (expr.range));
1329
+ } else if (expr.operator === "~") {
1330
+ return handleConstOperation(v => ~v);
1331
+ } else if (expr.operator === "+") {
1332
+ // eslint-disable-next-line no-implicit-coercion
1333
+ return handleConstOperation(v => +v);
1334
+ } else if (expr.operator === "-") {
1335
+ return handleConstOperation(v => -v);
1336
+ }
1337
+ });
1348
1338
  this.hooks.evaluateTypeof
1349
1339
  .for("undefined")
1350
- .tap("JavascriptParser", expr =>
1340
+ .tap(CLASS_NAME, expr =>
1351
1341
  new BasicEvaluatedExpression()
1352
1342
  .setString("undefined")
1353
1343
  .setRange(/** @type {Range} */ (expr.range))
1354
1344
  );
1355
- this.hooks.evaluate.for("Identifier").tap("JavascriptParser", expr => {
1345
+ this.hooks.evaluate.for("Identifier").tap(CLASS_NAME, expr => {
1356
1346
  if (/** @type {Identifier} */ (expr).name === "undefined") {
1357
1347
  return new BasicEvaluatedExpression()
1358
1348
  .setUndefined()
@@ -1369,7 +1359,7 @@ class JavascriptParser extends Parser {
1369
1359
  let cachedExpression;
1370
1360
  /** @type {GetInfoResult | undefined} */
1371
1361
  let cachedInfo;
1372
- this.hooks.evaluate.for(exprType).tap("JavascriptParser", expr => {
1362
+ this.hooks.evaluate.for(exprType).tap(CLASS_NAME, expr => {
1373
1363
  const expression =
1374
1364
  /** @type {Identifier | ThisExpression | MemberExpression} */ (expr);
1375
1365
 
@@ -1394,7 +1384,7 @@ class JavascriptParser extends Parser {
1394
1384
  });
1395
1385
  this.hooks.evaluate
1396
1386
  .for(exprType)
1397
- .tap({ name: "JavascriptParser", stage: 100 }, expr => {
1387
+ .tap({ name: CLASS_NAME, stage: 100 }, expr => {
1398
1388
  const expression =
1399
1389
  /** @type {Identifier | ThisExpression | MemberExpression} */
1400
1390
  (expr);
@@ -1412,7 +1402,7 @@ class JavascriptParser extends Parser {
1412
1402
  .setRange(/** @type {Range} */ (expression.range));
1413
1403
  }
1414
1404
  });
1415
- this.hooks.finish.tap("JavascriptParser", () => {
1405
+ this.hooks.finish.tap(CLASS_NAME, () => {
1416
1406
  // Cleanup for GC
1417
1407
  cachedExpression = cachedInfo = undefined;
1418
1408
  });
@@ -1447,7 +1437,7 @@ class JavascriptParser extends Parser {
1447
1437
  };
1448
1438
  }
1449
1439
  });
1450
- this.hooks.evaluate.for("MetaProperty").tap("JavascriptParser", expr => {
1440
+ this.hooks.evaluate.for("MetaProperty").tap(CLASS_NAME, expr => {
1451
1441
  const metaProperty = /** @type {MetaProperty} */ (expr);
1452
1442
 
1453
1443
  return this.callHooksForName(
@@ -1464,7 +1454,7 @@ class JavascriptParser extends Parser {
1464
1454
  )
1465
1455
  );
1466
1456
 
1467
- this.hooks.evaluate.for("CallExpression").tap("JavascriptParser", _expr => {
1457
+ this.hooks.evaluate.for("CallExpression").tap(CLASS_NAME, _expr => {
1468
1458
  const expr = /** @type {CallExpression} */ (_expr);
1469
1459
  if (
1470
1460
  expr.callee.type === "MemberExpression" &&
@@ -1493,7 +1483,7 @@ class JavascriptParser extends Parser {
1493
1483
  });
1494
1484
  this.hooks.evaluateCallExpressionMember
1495
1485
  .for("indexOf")
1496
- .tap("JavascriptParser", (expr, param) => {
1486
+ .tap(CLASS_NAME, (expr, param) => {
1497
1487
  if (!param.isString()) return;
1498
1488
  if (expr.arguments.length === 0) return;
1499
1489
  const [arg1, arg2] = expr.arguments;
@@ -1521,7 +1511,7 @@ class JavascriptParser extends Parser {
1521
1511
  });
1522
1512
  this.hooks.evaluateCallExpressionMember
1523
1513
  .for("replace")
1524
- .tap("JavascriptParser", (expr, param) => {
1514
+ .tap(CLASS_NAME, (expr, param) => {
1525
1515
  if (!param.isString()) return;
1526
1516
  if (expr.arguments.length !== 2) return;
1527
1517
  if (expr.arguments[0].type === "SpreadElement") return;
@@ -1544,7 +1534,7 @@ class JavascriptParser extends Parser {
1544
1534
  for (const fn of ["substr", "substring", "slice"]) {
1545
1535
  this.hooks.evaluateCallExpressionMember
1546
1536
  .for(fn)
1547
- .tap("JavascriptParser", (expr, param) => {
1537
+ .tap(CLASS_NAME, (expr, param) => {
1548
1538
  if (!param.isString()) return;
1549
1539
  let arg1;
1550
1540
  let result;
@@ -1636,22 +1626,20 @@ class JavascriptParser extends Parser {
1636
1626
  };
1637
1627
  };
1638
1628
 
1639
- this.hooks.evaluate
1640
- .for("TemplateLiteral")
1641
- .tap("JavascriptParser", _node => {
1642
- const node = /** @type {TemplateLiteral} */ (_node);
1629
+ this.hooks.evaluate.for("TemplateLiteral").tap(CLASS_NAME, _node => {
1630
+ const node = /** @type {TemplateLiteral} */ (_node);
1643
1631
 
1644
- const { quasis, parts } = getSimplifiedTemplateResult("cooked", node);
1645
- if (parts.length === 1) {
1646
- return parts[0].setRange(/** @type {Range} */ (node.range));
1647
- }
1648
- return new BasicEvaluatedExpression()
1649
- .setTemplateString(quasis, parts, "cooked")
1650
- .setRange(/** @type {Range} */ (node.range));
1651
- });
1632
+ const { quasis, parts } = getSimplifiedTemplateResult("cooked", node);
1633
+ if (parts.length === 1) {
1634
+ return parts[0].setRange(/** @type {Range} */ (node.range));
1635
+ }
1636
+ return new BasicEvaluatedExpression()
1637
+ .setTemplateString(quasis, parts, "cooked")
1638
+ .setRange(/** @type {Range} */ (node.range));
1639
+ });
1652
1640
  this.hooks.evaluate
1653
1641
  .for("TaggedTemplateExpression")
1654
- .tap("JavascriptParser", _node => {
1642
+ .tap(CLASS_NAME, _node => {
1655
1643
  const node = /** @type {TaggedTemplateExpression} */ (_node);
1656
1644
  const tag = this.evaluateExpression(node.tag);
1657
1645
 
@@ -1668,7 +1656,7 @@ class JavascriptParser extends Parser {
1668
1656
 
1669
1657
  this.hooks.evaluateCallExpressionMember
1670
1658
  .for("concat")
1671
- .tap("JavascriptParser", (expr, param) => {
1659
+ .tap(CLASS_NAME, (expr, param) => {
1672
1660
  if (!param.isString() && !param.isWrapped()) return;
1673
1661
  let stringSuffix = null;
1674
1662
  let hasUnknownParams = false;
@@ -1738,7 +1726,7 @@ class JavascriptParser extends Parser {
1738
1726
  });
1739
1727
  this.hooks.evaluateCallExpressionMember
1740
1728
  .for("split")
1741
- .tap("JavascriptParser", (expr, param) => {
1729
+ .tap(CLASS_NAME, (expr, param) => {
1742
1730
  if (!param.isString()) return;
1743
1731
  if (expr.arguments.length !== 1) return;
1744
1732
  if (expr.arguments[0].type === "SpreadElement") return;
@@ -1760,101 +1748,95 @@ class JavascriptParser extends Parser {
1760
1748
  .setSideEffects(param.couldHaveSideEffects())
1761
1749
  .setRange(/** @type {Range} */ (expr.range));
1762
1750
  });
1763
- this.hooks.evaluate
1764
- .for("ConditionalExpression")
1765
- .tap("JavascriptParser", _expr => {
1766
- const expr = /** @type {ConditionalExpression} */ (_expr);
1767
-
1768
- const condition = this.evaluateExpression(expr.test);
1769
- const conditionValue = condition.asBool();
1770
- let res;
1771
- if (conditionValue === undefined) {
1772
- const consequent = this.evaluateExpression(expr.consequent);
1773
- const alternate = this.evaluateExpression(expr.alternate);
1774
- res = new BasicEvaluatedExpression();
1775
- if (consequent.isConditional()) {
1776
- res.setOptions(
1777
- /** @type {BasicEvaluatedExpression[]} */ (consequent.options)
1778
- );
1779
- } else {
1780
- res.setOptions([consequent]);
1781
- }
1782
- if (alternate.isConditional()) {
1783
- res.addOptions(
1784
- /** @type {BasicEvaluatedExpression[]} */ (alternate.options)
1785
- );
1786
- } else {
1787
- res.addOptions([alternate]);
1788
- }
1751
+ this.hooks.evaluate.for("ConditionalExpression").tap(CLASS_NAME, _expr => {
1752
+ const expr = /** @type {ConditionalExpression} */ (_expr);
1753
+
1754
+ const condition = this.evaluateExpression(expr.test);
1755
+ const conditionValue = condition.asBool();
1756
+ let res;
1757
+ if (conditionValue === undefined) {
1758
+ const consequent = this.evaluateExpression(expr.consequent);
1759
+ const alternate = this.evaluateExpression(expr.alternate);
1760
+ res = new BasicEvaluatedExpression();
1761
+ if (consequent.isConditional()) {
1762
+ res.setOptions(
1763
+ /** @type {BasicEvaluatedExpression[]} */ (consequent.options)
1764
+ );
1789
1765
  } else {
1790
- res = this.evaluateExpression(
1791
- conditionValue ? expr.consequent : expr.alternate
1766
+ res.setOptions([consequent]);
1767
+ }
1768
+ if (alternate.isConditional()) {
1769
+ res.addOptions(
1770
+ /** @type {BasicEvaluatedExpression[]} */ (alternate.options)
1792
1771
  );
1793
- if (condition.couldHaveSideEffects()) res.setSideEffects();
1772
+ } else {
1773
+ res.addOptions([alternate]);
1794
1774
  }
1795
- res.setRange(/** @type {Range} */ (expr.range));
1796
- return res;
1797
- });
1798
- this.hooks.evaluate
1799
- .for("ArrayExpression")
1800
- .tap("JavascriptParser", _expr => {
1801
- const expr = /** @type {ArrayExpression} */ (_expr);
1802
-
1803
- const items = expr.elements.map(
1804
- element =>
1805
- element !== null &&
1806
- element.type !== "SpreadElement" &&
1807
- this.evaluateExpression(element)
1775
+ } else {
1776
+ res = this.evaluateExpression(
1777
+ conditionValue ? expr.consequent : expr.alternate
1808
1778
  );
1809
- if (!items.every(Boolean)) return;
1810
- return new BasicEvaluatedExpression()
1811
- .setItems(/** @type {BasicEvaluatedExpression[]} */ (items))
1812
- .setRange(/** @type {Range} */ (expr.range));
1813
- });
1814
- this.hooks.evaluate
1815
- .for("ChainExpression")
1816
- .tap("JavascriptParser", _expr => {
1817
- const expr = /** @type {ChainExpression} */ (_expr);
1818
- /** @type {Expression[]} */
1819
- const optionalExpressionsStack = [];
1820
- /** @type {Expression|Super} */
1821
- let next = expr.expression;
1822
-
1823
- while (
1824
- next.type === "MemberExpression" ||
1825
- next.type === "CallExpression"
1826
- ) {
1827
- if (next.type === "MemberExpression") {
1828
- if (next.optional) {
1829
- // SuperNode can not be optional
1830
- optionalExpressionsStack.push(
1831
- /** @type {Expression} */ (next.object)
1832
- );
1833
- }
1834
- next = next.object;
1835
- } else {
1836
- if (next.optional) {
1837
- // SuperNode can not be optional
1838
- optionalExpressionsStack.push(
1839
- /** @type {Expression} */ (next.callee)
1840
- );
1841
- }
1842
- next = next.callee;
1779
+ if (condition.couldHaveSideEffects()) res.setSideEffects();
1780
+ }
1781
+ res.setRange(/** @type {Range} */ (expr.range));
1782
+ return res;
1783
+ });
1784
+ this.hooks.evaluate.for("ArrayExpression").tap(CLASS_NAME, _expr => {
1785
+ const expr = /** @type {ArrayExpression} */ (_expr);
1786
+
1787
+ const items = expr.elements.map(
1788
+ element =>
1789
+ element !== null &&
1790
+ element.type !== "SpreadElement" &&
1791
+ this.evaluateExpression(element)
1792
+ );
1793
+ if (!items.every(Boolean)) return;
1794
+ return new BasicEvaluatedExpression()
1795
+ .setItems(/** @type {BasicEvaluatedExpression[]} */ (items))
1796
+ .setRange(/** @type {Range} */ (expr.range));
1797
+ });
1798
+ this.hooks.evaluate.for("ChainExpression").tap(CLASS_NAME, _expr => {
1799
+ const expr = /** @type {ChainExpression} */ (_expr);
1800
+ /** @type {Expression[]} */
1801
+ const optionalExpressionsStack = [];
1802
+ /** @type {Expression|Super} */
1803
+ let next = expr.expression;
1804
+
1805
+ while (
1806
+ next.type === "MemberExpression" ||
1807
+ next.type === "CallExpression"
1808
+ ) {
1809
+ if (next.type === "MemberExpression") {
1810
+ if (next.optional) {
1811
+ // SuperNode can not be optional
1812
+ optionalExpressionsStack.push(
1813
+ /** @type {Expression} */ (next.object)
1814
+ );
1843
1815
  }
1816
+ next = next.object;
1817
+ } else {
1818
+ if (next.optional) {
1819
+ // SuperNode can not be optional
1820
+ optionalExpressionsStack.push(
1821
+ /** @type {Expression} */ (next.callee)
1822
+ );
1823
+ }
1824
+ next = next.callee;
1844
1825
  }
1826
+ }
1845
1827
 
1846
- while (optionalExpressionsStack.length > 0) {
1847
- const expression =
1848
- /** @type {Expression} */
1849
- (optionalExpressionsStack.pop());
1850
- const evaluated = this.evaluateExpression(expression);
1828
+ while (optionalExpressionsStack.length > 0) {
1829
+ const expression =
1830
+ /** @type {Expression} */
1831
+ (optionalExpressionsStack.pop());
1832
+ const evaluated = this.evaluateExpression(expression);
1851
1833
 
1852
- if (evaluated.asNullish()) {
1853
- return evaluated.setRange(/** @type {Range} */ (_expr.range));
1854
- }
1834
+ if (evaluated.asNullish()) {
1835
+ return evaluated.setRange(/** @type {Range} */ (_expr.range));
1855
1836
  }
1856
- return this.evaluateExpression(expr.expression);
1857
- });
1837
+ }
1838
+ return this.evaluateExpression(expr.expression);
1839
+ });
1858
1840
  }
1859
1841
 
1860
1842
  /**
@@ -2171,7 +2153,7 @@ class JavascriptParser extends Parser {
2171
2153
  this.blockPreWalkStatements(body);
2172
2154
  this.prevStatement = prev;
2173
2155
  this.walkStatements(body);
2174
- }, this.scope.inExecutedPath);
2156
+ }, true);
2175
2157
  }
2176
2158
 
2177
2159
  /**
@@ -2211,20 +2193,12 @@ class JavascriptParser extends Parser {
2211
2193
 
2212
2194
  this.scope.terminated =
2213
2195
  consequentTerminated && alternateTerminated
2214
- ? this.scope.terminated
2196
+ ? alternateTerminated
2215
2197
  : undefined;
2216
- } else {
2217
- const oldState = this.scope.inExecutedPath;
2218
-
2219
- this.scope.inExecutedPath = true;
2220
-
2221
- if (result) {
2222
- this.walkNestedStatement(statement.consequent);
2223
- } else if (statement.alternate) {
2224
- this.walkNestedStatement(statement.alternate);
2225
- }
2226
-
2227
- this.scope.inExecutedPath = oldState;
2198
+ } else if (result) {
2199
+ this.walkNestedStatement(statement.consequent);
2200
+ } else if (statement.alternate) {
2201
+ this.walkNestedStatement(statement.alternate);
2228
2202
  }
2229
2203
  }
2230
2204
 
@@ -2290,7 +2264,9 @@ class JavascriptParser extends Parser {
2290
2264
  if (this.scope.topLevelScope === true) return;
2291
2265
  if (this.hooks.terminate.call(statement)) {
2292
2266
  this.scope.terminated =
2293
- statement.type === "ReturnStatement" ? "return" : "throw";
2267
+ statement.type === "ReturnStatement"
2268
+ ? SCOPE_INFO_TERMINATED_RETURN
2269
+ : SCOPE_INFO_TERMINATED_THROW;
2294
2270
  }
2295
2271
  }
2296
2272
 
@@ -2337,14 +2313,20 @@ class JavascriptParser extends Parser {
2337
2313
  const handlerTerminated = this.scope.terminated;
2338
2314
  this.scope.terminated = undefined;
2339
2315
 
2340
- if (statement.finalizer) this.walkStatement(statement.finalizer);
2316
+ if (statement.finalizer) {
2317
+ this.walkStatement(statement.finalizer);
2318
+ }
2341
2319
 
2342
- if (
2343
- !this.scope.terminated &&
2320
+ const finalizerTerminated = this.scope.terminated;
2321
+ this.scope.terminated = undefined;
2322
+
2323
+ if (finalizerTerminated) {
2324
+ this.scope.terminated = finalizerTerminated;
2325
+ } else if (
2344
2326
  tryTerminated &&
2345
2327
  (statement.handler ? handlerTerminated : true)
2346
2328
  ) {
2347
- this.scope.terminated = tryTerminated;
2329
+ this.scope.terminated = handlerTerminated || tryTerminated;
2348
2330
  }
2349
2331
  }
2350
2332
 
@@ -2412,7 +2394,9 @@ class JavascriptParser extends Parser {
2412
2394
  if (statement.update) {
2413
2395
  this.walkExpression(statement.update);
2414
2396
  }
2397
+
2415
2398
  const body = statement.body;
2399
+
2416
2400
  if (body.type === "BlockStatement") {
2417
2401
  // no need to add additional scope
2418
2402
  const prev = this.prevStatement;
@@ -2446,8 +2430,11 @@ class JavascriptParser extends Parser {
2446
2430
  } else {
2447
2431
  this.walkPattern(statement.left);
2448
2432
  }
2433
+
2449
2434
  this.walkExpression(statement.right);
2435
+
2450
2436
  const body = statement.body;
2437
+
2451
2438
  if (body.type === "BlockStatement") {
2452
2439
  // no need to add additional scope
2453
2440
  const prev = this.prevStatement;
@@ -2484,8 +2471,11 @@ class JavascriptParser extends Parser {
2484
2471
  } else {
2485
2472
  this.walkPattern(statement.left);
2486
2473
  }
2474
+
2487
2475
  this.walkExpression(statement.right);
2476
+
2488
2477
  const body = statement.body;
2478
+
2489
2479
  if (body.type === "BlockStatement") {
2490
2480
  // no need to add additional scope
2491
2481
  const prev = this.prevStatement;
@@ -4074,7 +4064,6 @@ class JavascriptParser extends Parser {
4074
4064
  inTaggedTemplateTag: false,
4075
4065
  isStrict: oldScope.isStrict,
4076
4066
  isAsmJs: oldScope.isAsmJs,
4077
- inExecutedPath: false,
4078
4067
  terminated: undefined,
4079
4068
  definitions: oldScope.definitions.createChild()
4080
4069
  };
@@ -4103,7 +4092,6 @@ class JavascriptParser extends Parser {
4103
4092
  inTry: false,
4104
4093
  inShorthand: false,
4105
4094
  inTaggedTemplateTag: false,
4106
- inExecutedPath: true,
4107
4095
  isStrict: oldScope.isStrict,
4108
4096
  isAsmJs: oldScope.isAsmJs,
4109
4097
  terminated: undefined,
@@ -4136,7 +4124,6 @@ class JavascriptParser extends Parser {
4136
4124
  inTry: false,
4137
4125
  inShorthand: false,
4138
4126
  inTaggedTemplateTag: false,
4139
- inExecutedPath: true,
4140
4127
  isStrict: oldScope.isStrict,
4141
4128
  isAsmJs: oldScope.isAsmJs,
4142
4129
  terminated: undefined,
@@ -4168,7 +4155,6 @@ class JavascriptParser extends Parser {
4168
4155
  inTry: oldScope.inTry,
4169
4156
  inShorthand: false,
4170
4157
  inTaggedTemplateTag: false,
4171
- inExecutedPath,
4172
4158
  isStrict: oldScope.isStrict,
4173
4159
  isAsmJs: oldScope.isAsmJs,
4174
4160
  terminated: oldScope.terminated,
@@ -4179,10 +4165,7 @@ class JavascriptParser extends Parser {
4179
4165
 
4180
4166
  const terminated = this.scope.terminated;
4181
4167
 
4182
- if (
4183
- inExecutedPath &&
4184
- ((this.scope.inTry && terminated === "throw") || terminated === "return")
4185
- ) {
4168
+ if (inExecutedPath && terminated) {
4186
4169
  oldScope.terminated = terminated;
4187
4170
  }
4188
4171
 
@@ -4502,7 +4485,6 @@ class JavascriptParser extends Parser {
4502
4485
  inTry: false,
4503
4486
  inShorthand: false,
4504
4487
  inTaggedTemplateTag: false,
4505
- inExecutedPath: false,
4506
4488
  isStrict: false,
4507
4489
  isAsmJs: false,
4508
4490
  terminated: undefined,