webpack 5.110.3 → 5.111.0

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 (208) hide show
  1. package/README.md +5 -0
  2. package/lib/APIPlugin.js +23 -7
  3. package/lib/AutomaticPrefetchPlugin.js +1 -1
  4. package/lib/Cache.js +8 -3
  5. package/lib/CacheFacade.js +1 -1
  6. package/lib/CircularModulesPlugin.js +157 -55
  7. package/lib/CleanPlugin.js +2 -2
  8. package/lib/Compilation.js +320 -302
  9. package/lib/Compiler.js +209 -46
  10. package/lib/ContextModule.js +17 -81
  11. package/lib/ContextModuleFactory.js +1 -1
  12. package/lib/CopyPlugin.js +1054 -0
  13. package/lib/DefinePlugin.js +24 -8
  14. package/lib/ExternalModule.js +41 -14
  15. package/lib/FileSystemInfo.js +17 -14
  16. package/lib/FlagDependencyExportsPlugin.js +45 -21
  17. package/lib/HotModuleReplacementPlugin.js +53 -7
  18. package/lib/JavascriptMetaInfoPlugin.js +5 -3
  19. package/lib/MainTemplate.js +5 -1
  20. package/lib/Module.js +16 -7
  21. package/lib/MultiCompiler.js +1 -1
  22. package/lib/MultiWatching.js +1 -1
  23. package/lib/NormalModule.js +45 -12
  24. package/lib/NormalModuleFactory.js +1 -1
  25. package/lib/RuntimeGlobals.js +21 -7
  26. package/lib/RuntimeModule.js +28 -12
  27. package/lib/RuntimePlugin.js +30 -3
  28. package/lib/RuntimeTemplate.js +780 -475
  29. package/lib/SourceMapDevToolPlugin.js +1 -1
  30. package/lib/Watching.js +9 -0
  31. package/lib/WebpackOptionsApply.js +84 -91
  32. package/lib/asset/AssetGenerator.js +25 -8
  33. package/lib/asset/AssetModule.js +3 -0
  34. package/lib/asset/WebManifestParser.js +7 -5
  35. package/lib/cache/AddBuildDependenciesPlugin.js +33 -4
  36. package/lib/cache/IdleFileCachePlugin.js +2 -2
  37. package/lib/cache/PackFileCacheStrategy.js +14 -1
  38. package/lib/config/browserslistTargetHandler.js +19 -0
  39. package/lib/config/defaults.js +86 -50
  40. package/lib/config/normalization.js +82 -12
  41. package/lib/config/target.js +10 -0
  42. package/lib/container/ContainerReferencePlugin.js +6 -0
  43. package/lib/container/RemoteRuntimeModule.js +9 -0
  44. package/lib/container/declaredRemotes.js +39 -0
  45. package/lib/css/CssGenerator.js +8 -0
  46. package/lib/css/CssInjectStyleRuntimeModule.js +9 -0
  47. package/lib/css/CssLoadingRuntimeModule.js +94 -36
  48. package/lib/css/CssModule.js +1 -1
  49. package/lib/css/CssModulesPlugin.js +7 -6
  50. package/lib/css/CssParser.js +144 -11
  51. package/lib/css/CssServerStylesRuntimeModule.js +9 -0
  52. package/lib/css/cssMinify.js +11 -3
  53. package/lib/css/data.js +2814 -678
  54. package/lib/css/syntax.js +4082 -248
  55. package/lib/debug/ProfilingPlugin.js +4 -0
  56. package/lib/dependencies/AMDDefineDependencyParserPlugin.js +6 -3
  57. package/lib/dependencies/AMDRuntimeModules.js +18 -0
  58. package/lib/dependencies/CommonJsExportRequireDependency.js +3 -1
  59. package/lib/dependencies/CommonJsExportsParserPlugin.js +12 -13
  60. package/lib/dependencies/CommonJsImportsParserPlugin.js +1 -0
  61. package/lib/dependencies/CommonJsPlugin.js +19 -3
  62. package/lib/dependencies/CommonJsRequireDependency.js +14 -5
  63. package/lib/dependencies/CssIcssExportDependency.js +7 -1
  64. package/lib/dependencies/HarmonyCompatibilityDependency.js +58 -4
  65. package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +8 -3
  66. package/lib/dependencies/HarmonyExportExpressionDependency.js +44 -29
  67. package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +71 -13
  68. package/lib/dependencies/HarmonyExportInitFragment.js +99 -19
  69. package/lib/dependencies/HarmonyExportSpecifierDependency.js +18 -30
  70. package/lib/dependencies/HarmonyImportBareSideEffectDependency.js +59 -0
  71. package/lib/dependencies/HarmonyImportDependency.js +6 -1
  72. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +8 -1
  73. package/lib/dependencies/HarmonyImportSpecifierDependency.js +24 -1
  74. package/lib/dependencies/HarmonyModulesPlugin.js +20 -0
  75. package/lib/dependencies/HtmlEntryDependency.js +50 -0
  76. package/lib/dependencies/ImportMetaMainDependency.js +104 -0
  77. package/lib/dependencies/ImportMetaPlugin.js +59 -15
  78. package/lib/dependencies/SystemRuntimeModule.js +9 -0
  79. package/lib/dependencies/WorkerDependency.js +1 -2
  80. package/lib/dependencies/WorkletDependency.js +1 -2
  81. package/lib/dll/DelegatedModule.js +18 -1
  82. package/lib/dll/DllReferencePlugin.js +11 -8
  83. package/lib/dll/LibManifestPlugin.js +1 -1
  84. package/lib/errors/AnalyzableBailoutsWarning.js +41 -0
  85. package/lib/errors/DuplicateModulesWarning.js +12 -7
  86. package/lib/errors/EmptyCopyPatternWarning.js +26 -0
  87. package/lib/errors/ExternalStarReexportError.js +44 -0
  88. package/lib/errors/MissingSourceMapsWarning.js +36 -0
  89. package/lib/errors/ModuleParseError.js +2 -1
  90. package/lib/errors/UnusedAssetsWarning.js +36 -0
  91. package/lib/errors/UnusedFederationWarning.js +35 -0
  92. package/lib/errors/UnusedModulesWarning.js +38 -0
  93. package/lib/esm/ExportWebpackRequireRuntimeModule.js +9 -0
  94. package/lib/esm/ModuleChunkFormatPlugin.js +28 -3
  95. package/lib/esm/ModuleChunkLoadingPlugin.js +30 -11
  96. package/lib/esm/ModuleChunkLoadingRuntimeModule.js +92 -60
  97. package/lib/hmr/HotModuleReplacement.runtime.js +8 -2
  98. package/lib/hmr/HotModuleReplacementRuntimeModule.js +9 -0
  99. package/lib/html/HtmlGenerator.js +11 -5
  100. package/lib/html/HtmlModulesPlugin.js +331 -62
  101. package/lib/html/HtmlParser.js +13 -6
  102. package/lib/html/builtinEmbeddedRenderer.js +107 -0
  103. package/lib/html/data.js +121 -7
  104. package/lib/html/htmlMinify.js +84 -113
  105. package/lib/html/syntax.js +763 -426
  106. package/lib/ids/HashedModuleIdsPlugin.js +2 -1
  107. package/lib/index.js +3 -0
  108. package/lib/javascript/JavascriptModule.js +1 -0
  109. package/lib/javascript/JavascriptModulesPlugin.js +52 -24
  110. package/lib/javascript/JavascriptParser.js +1255 -728
  111. package/lib/javascript/StartupHelpers.js +27 -3
  112. package/lib/javascript/data.js +172 -0
  113. package/lib/javascript/grammar.js +5546 -0
  114. package/lib/javascript/parser.js +1494 -0
  115. package/lib/javascript/regexp.js +1762 -0
  116. package/lib/javascript/regexpData.js +51 -0
  117. package/lib/javascript/syntax.js +8696 -5225
  118. package/lib/library/ModuleLibraryPlugin.js +116 -5
  119. package/lib/node/ReadFileChunkLoadingRuntimeModule.js +22 -0
  120. package/lib/node/RequireChunkLoadingRuntimeModule.js +22 -0
  121. package/lib/optimize/ConcatenatedModule.js +97 -20
  122. package/lib/optimize/ConstExportsPlugin.js +50 -39
  123. package/lib/optimize/InlineExports.js +20 -123
  124. package/lib/optimize/MangleExportsPlugin.js +10 -0
  125. package/lib/optimize/ModuleConcatenationPlugin.js +24 -1
  126. package/lib/optimize/RealContentHashPlugin.js +68 -9
  127. package/lib/optimize/SideEffectsFlagPlugin.js +17 -9
  128. package/lib/performance/AnalyzableBailoutsPlugin.js +115 -0
  129. package/lib/performance/CacheEffectivenessPlugin.js +4 -3
  130. package/lib/performance/DuplicateModulesPlugin.js +57 -13
  131. package/lib/performance/EvalUsagePlugin.js +5 -2
  132. package/lib/performance/MissingSourceMapsPlugin.js +115 -0
  133. package/lib/performance/PureAnnotationsPlugin.js +6 -3
  134. package/lib/performance/TopLevelThisPlugin.js +5 -4
  135. package/lib/performance/UnusedAssetsPlugin.js +109 -0
  136. package/lib/performance/UnusedFederationPlugin.js +147 -0
  137. package/lib/performance/UnusedModulesPlugin.js +141 -0
  138. package/lib/performance/UnusedReexportsPlugin.js +1 -55
  139. package/lib/performance/isOnlyReexported.js +65 -0
  140. package/lib/prefetch/ChunkPrefetchFunctionRuntimeModule.js +25 -5
  141. package/lib/prefetch/ChunkPrefetchPreloadPlugin.js +2 -0
  142. package/lib/prefetch/ChunkPrefetchStartupRuntimeModule.js +9 -0
  143. package/lib/prefetch/ChunkPrefetchTriggerRuntimeModule.js +9 -0
  144. package/lib/prefetch/ChunkPreloadTriggerRuntimeModule.js +9 -0
  145. package/lib/prefetch/ResourceHintPlugin.js +1 -2
  146. package/lib/prefetch/ResourceHintRuntimeModule.js +9 -0
  147. package/lib/prefetch/StartupAssetHintRuntimeModule.js +9 -0
  148. package/lib/rules/RuleSetCompiler.js +211 -4
  149. package/lib/runtime/AsyncModuleRuntimeModule.js +100 -66
  150. package/lib/runtime/AutoPublicPathRuntimeModule.js +9 -0
  151. package/lib/runtime/BaseUriRuntimeModule.js +9 -0
  152. package/lib/runtime/ChunkNameRuntimeModule.js +9 -0
  153. package/lib/runtime/EnsureChunkRuntimeModule.js +35 -16
  154. package/lib/runtime/GetChunkFilenameRuntimeModule.js +9 -0
  155. package/lib/runtime/GetFullHashRuntimeModule.js +9 -0
  156. package/lib/runtime/GetMainFilenameRuntimeModule.js +9 -0
  157. package/lib/runtime/GetWorkletBootstrapRuntimeModule.js +9 -0
  158. package/lib/runtime/GlobalRuntimeModule.js +9 -0
  159. package/lib/runtime/HasOwnPropertyRuntimeModule.js +9 -0
  160. package/lib/runtime/HelperRuntimeModule.js +10 -0
  161. package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +330 -219
  162. package/lib/runtime/NonceRuntimeModule.js +9 -0
  163. package/lib/runtime/OnChunksLoadedRuntimeModule.js +67 -20
  164. package/lib/runtime/PublicPathRuntimeModule.js +9 -0
  165. package/lib/runtime/RuntimeIdRuntimeModule.js +9 -0
  166. package/lib/runtime/SpecNamespaceObjectRuntimeModule.js +90 -0
  167. package/lib/runtime/StartupChunkDependenciesRuntimeModule.js +9 -0
  168. package/lib/runtime/StartupEntrypointRuntimeModule.js +9 -0
  169. package/lib/runtime/SystemContextRuntimeModule.js +9 -0
  170. package/lib/runtime/ToBinaryRuntimeModule.js +9 -0
  171. package/lib/serialization/ObjectMiddleware.js +138 -106
  172. package/lib/sharing/ConsumeSharedPlugin.js +12 -0
  173. package/lib/sharing/ConsumeSharedRuntimeModule.js +11 -0
  174. package/lib/sharing/ShareRuntimeModule.js +9 -0
  175. package/lib/sharing/declaredShared.js +47 -0
  176. package/lib/stats/DefaultStatsFactoryPlugin.js +3 -1
  177. package/lib/stats/DefaultStatsPrinterPlugin.js +3 -0
  178. package/lib/util/SourceProcessor.js +76 -69
  179. package/lib/util/async.js +292 -0
  180. package/lib/util/buildDiagnostics.js +69 -0
  181. package/lib/util/fs.js +27 -1
  182. package/lib/util/handlerKeys.js +40 -0
  183. package/lib/util/identifier.js +20 -0
  184. package/lib/util/internalSerializables.js +4 -0
  185. package/lib/util/registerExternalSerializer.js +6 -45
  186. package/lib/wasm-async/AsyncWasmCompileRuntimeModule.js +9 -0
  187. package/lib/wasm-async/AsyncWasmLoadingRuntimeModule.js +9 -0
  188. package/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js +9 -0
  189. package/lib/web/JsonpChunkLoadingRuntimeModule.js +36 -0
  190. package/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js +19 -0
  191. package/package.json +22 -16
  192. package/schemas/WebpackOptions.check.js +1 -1
  193. package/schemas/WebpackOptions.json +379 -66
  194. package/schemas/plugins/LoaderOptionsPlugin.check.js +1 -1
  195. package/schemas/plugins/ManifestPlugin.check.js +1 -1
  196. package/schemas/plugins/SourceMapDevToolPlugin.check.js +1 -1
  197. package/schemas/plugins/asset/AssetGeneratorOptions.check.js +1 -1
  198. package/schemas/plugins/asset/AssetResourceGeneratorOptions.check.js +1 -1
  199. package/schemas/plugins/container/ContainerPlugin.check.js +1 -1
  200. package/schemas/plugins/container/ModuleFederationPlugin.check.js +1 -1
  201. package/schemas/plugins/debug/ProfilingPlugin.check.js +1 -1
  202. package/schemas/plugins/dll/DllReferencePlugin.check.js +1 -1
  203. package/schemas/plugins/ids/HashedModuleIdsPlugin.check.js +1 -1
  204. package/schemas/plugins/schemes/HttpUriPlugin.check.js +1 -1
  205. package/types.d.ts +1414 -314
  206. package/lib/dependencies/ExportBindingInitFragment.js +0 -191
  207. package/lib/errors/EntrypointOverlapWarning.js +0 -45
  208. package/lib/performance/EntrypointOverlapPlugin.js +0 -122
@@ -26,13 +26,7 @@ const {
26
26
  positionAt
27
27
  } = require("./syntax");
28
28
 
29
- /** @typedef {typeof import("acorn").Parser} AcornParser */
30
- /**
31
- * @import {
32
- * Options as AcornOptions,
33
- * ecmaVersion as EcmaVersion
34
- * } from "acorn"
35
- */
29
+ /** @import { EcmaVersion, ParserOptions, ParserPlugin } from "./syntax" */
36
30
  /**
37
31
  * @import {
38
32
  * AssignmentExpression,
@@ -151,25 +145,8 @@ const {
151
145
 
152
146
  /** @type {string[]} */
153
147
  const EMPTY_ARRAY = [];
154
-
155
- /**
156
- * Getter that reverses `arr` in place on first access and caches it. Lighter
157
- * than `memoize(() => arr.reverse())` (one closure instead of two) and called
158
- * per member expression on hot parse paths.
159
- * @template T
160
- * @param {T[]} arr array reversed lazily on first access
161
- * @returns {() => T[]} getter returning the reversed array
162
- */
163
- const lazyReverse = (arr) => {
164
- let reversed = false;
165
- return () => {
166
- if (!reversed) {
167
- arr.reverse();
168
- reversed = true;
169
- }
170
- return arr;
171
- };
172
- };
148
+ /** @type {MembersOptionals} */
149
+ const EMPTY_MEMBERS_OPTIONALS = [];
173
150
 
174
151
  const ALLOWED_MEMBER_TYPES_CALL_EXPRESSION = 0b01;
175
152
  const ALLOWED_MEMBER_TYPES_EXPRESSION = 0b10;
@@ -183,8 +160,11 @@ const RETURN_FALSE = () => false;
183
160
  // replacing three fresh closures per evaluated free variable.
184
161
  const RETURN_EMPTY_ARRAY = () => [];
185
162
 
163
+ // Shared no-result fallback for the identifier evaluators.
164
+ const RETURN_UNDEFINED = () => undefined;
165
+
186
166
  // Syntax: https://developer.mozilla.org/en/SpiderMonkey/Parser_API
187
- let parser = /** @type {AcornParser} */ (WebpackParser);
167
+ let parser = WebpackParser;
188
168
 
189
169
  /** @typedef {Record<string, string> & { _isLegacyAssert?: boolean }} ImportAttributes */
190
170
 
@@ -327,7 +307,7 @@ class VariableInfo {
327
307
  * @property {boolean=} ranges
328
308
  * @property {boolean=} allowHashBang
329
309
  * @property {boolean=} allowReturnOutsideFunction
330
- * @property {boolean=} lazyNodes internal: serve `range` lazily and skip acorn's location/range tracking
310
+ * @property {boolean=} lazyNodes internal: serve `range` lazily and skip the parser's location/range tracking
331
311
  * @property {Comment[]=} lazyComments internal: collect comments here without slicing their text eagerly
332
312
  * @property {boolean=} importPhases enable parsing of the import phase proposals (import defer / import source)
333
313
  * @property {boolean=} moduleFallback internal: for `auto`, let the parser downgrade module->script in place instead of re-parsing
@@ -430,24 +410,455 @@ const joinRanges = (startRange, endRange) => {
430
410
  * Helper function used to generate a string representation of a
431
411
  * [member expression](https://github.com/estree/estree/blob/master/es5.md#memberexpression).
432
412
  * @param {string} object object to name
433
- * @param {Members} membersReversed reversed list of members
413
+ * @param {Members} members members in source order
434
414
  * @returns {string} member expression as a string
435
415
  * @example
436
416
  * ```js
437
- * const membersReversed = ["property1", "property2", "property3"]; // Members parsed from the AST
438
- * const name = objectAndMembersToName("myObject", membersReversed);
417
+ * const members = ["property1", "property2", "property3"]; // Members parsed from the AST
418
+ * const name = objectAndMembersToName("myObject", members);
439
419
  *
440
420
  * console.log(name); // "myObject.property1.property2.property3"
441
421
  * ```
442
422
  */
443
- const objectAndMembersToName = (object, membersReversed) => {
423
+ const objectAndMembersToName = (object, members) => {
444
424
  let name = object;
445
- for (let i = membersReversed.length - 1; i >= 0; i--) {
446
- name = `${name}.${membersReversed[i]}`;
425
+ for (let i = 0; i < members.length; i++) {
426
+ name = `${name}.${members[i]}`;
447
427
  }
448
428
  return name;
449
429
  };
450
430
 
431
+ /**
432
+ * The range of the object below each of the first `count` members of a chain,
433
+ * in source order: what `extractMemberExpressionChain` reports as memberRanges.
434
+ * @param {Expression | Super} expression a member expression
435
+ * @param {number} count how many members the chain has
436
+ * @returns {MemberRanges} the ranges
437
+ */
438
+ const memberRangesOf = (expression, count) => {
439
+ /** @type {MemberRanges} */
440
+ const ranges = [];
441
+ let expr = /** @type {MemberExpression} */ (expression);
442
+ for (let i = 0; i < count; i++) {
443
+ const object = expr.object;
444
+ ranges.push(/** @type {Range} */ (object.range));
445
+ expr = /** @type {MemberExpression} */ (object);
446
+ }
447
+ if (count > 1) ranges.reverse();
448
+ return ranges;
449
+ };
450
+
451
+ /**
452
+ * In simple logical cases, we can use valueAsExpression to assist us in evaluating the expression on
453
+ * either side of a [BinaryExpression](https://github.com/estree/estree/blob/master/es5.md#binaryexpression).
454
+ * This supports scenarios in webpack like conditionally `import()`'ing modules based on some simple evaluation:
455
+ *
456
+ * ```js
457
+ * if (1 === 3) {
458
+ * import("./moduleA"); // webpack will auto evaluate this and not import the modules
459
+ * }
460
+ * ```
461
+ *
462
+ * Additional scenarios include evaluation of strings inside of dynamic import statements:
463
+ *
464
+ * ```js
465
+ * const foo = "foo";
466
+ * const bar = "bar";
467
+ *
468
+ * import("./" + foo + bar); // webpack will auto evaluate this into import("./foobar")
469
+ * ```
470
+ * @param {boolean | number | bigint | string} value the value to convert to an expression
471
+ * @param {BinaryExpression | UnaryExpression} expr the expression being evaluated
472
+ * @param {boolean} sideEffects whether the expression has side effects
473
+ * @returns {BasicEvaluatedExpression | undefined} the evaluated expression
474
+ * @example
475
+ *
476
+ * ```js
477
+ * const binaryExpr = new BinaryExpression("+",
478
+ * { type: "Literal", value: 2 },
479
+ * { type: "Literal", value: 3 }
480
+ * );
481
+ *
482
+ * const leftValue = 2;
483
+ * const rightValue = 3;
484
+ *
485
+ * const leftExpr = valueAsExpression(leftValue, binaryExpr.left, false);
486
+ * const rightExpr = valueAsExpression(rightValue, binaryExpr.right, false);
487
+ * const result = new BasicEvaluatedExpression()
488
+ * .setNumber(leftExpr.number + rightExpr.number)
489
+ * .setRange(binaryExpr.range);
490
+ *
491
+ * console.log(result.number); // Output: 5
492
+ * ```
493
+ */
494
+ const valueAsExpression = (value, expr, sideEffects) => {
495
+ switch (typeof value) {
496
+ case "boolean":
497
+ return new BasicEvaluatedExpression()
498
+ .setBoolean(value)
499
+ .setSideEffects(sideEffects)
500
+ .setRange(/** @type {Range} */ (expr.range));
501
+ case "number":
502
+ return new BasicEvaluatedExpression()
503
+ .setNumber(value)
504
+ .setSideEffects(sideEffects)
505
+ .setRange(/** @type {Range} */ (expr.range));
506
+ case "bigint":
507
+ return new BasicEvaluatedExpression()
508
+ .setBigInt(value)
509
+ .setSideEffects(sideEffects)
510
+ .setRange(/** @type {Range} */ (expr.range));
511
+ case "string":
512
+ return new BasicEvaluatedExpression()
513
+ .setString(value)
514
+ .setSideEffects(sideEffects)
515
+ .setRange(/** @type {Range} */ (expr.range));
516
+ }
517
+ };
518
+
519
+ // The constant operators, shared so evaluating `a - b` allocates no handler.
520
+
521
+ /**
522
+ * @param {EXPECTED_ANY} l left operand
523
+ * @param {EXPECTED_ANY} r right operand
524
+ * @returns {EXPECTED_ANY} the result
525
+ */
526
+ const SUBTRACT = (l, r) => l - r;
527
+
528
+ /**
529
+ * @param {EXPECTED_ANY} l left operand
530
+ * @param {EXPECTED_ANY} r right operand
531
+ * @returns {EXPECTED_ANY} the result
532
+ */
533
+ const MULTIPLY = (l, r) => l * r;
534
+
535
+ /**
536
+ * @param {EXPECTED_ANY} l left operand
537
+ * @param {EXPECTED_ANY} r right operand
538
+ * @returns {EXPECTED_ANY} the result
539
+ */
540
+ const DIVIDE = (l, r) => l / r;
541
+
542
+ /**
543
+ * @param {EXPECTED_ANY} l left operand
544
+ * @param {EXPECTED_ANY} r right operand
545
+ * @returns {EXPECTED_ANY} the result
546
+ */
547
+ const POWER = (l, r) => l ** r;
548
+
549
+ /**
550
+ * @param {EXPECTED_ANY} l left operand
551
+ * @param {EXPECTED_ANY} r right operand
552
+ * @returns {EXPECTED_ANY} the result
553
+ */
554
+ const BITWISE_AND = (l, r) => l & r;
555
+
556
+ /**
557
+ * @param {EXPECTED_ANY} l left operand
558
+ * @param {EXPECTED_ANY} r right operand
559
+ * @returns {EXPECTED_ANY} the result
560
+ */
561
+ const BITWISE_OR = (l, r) => l | r;
562
+
563
+ /**
564
+ * @param {EXPECTED_ANY} l left operand
565
+ * @param {EXPECTED_ANY} r right operand
566
+ * @returns {EXPECTED_ANY} the result
567
+ */
568
+ const BITWISE_XOR = (l, r) => l ^ r;
569
+
570
+ /**
571
+ * @param {EXPECTED_ANY} l left operand
572
+ * @param {EXPECTED_ANY} r right operand
573
+ * @returns {EXPECTED_ANY} the result
574
+ */
575
+ const UNSIGNED_SHIFT_RIGHT = (l, r) => l >>> r;
576
+
577
+ /**
578
+ * @param {EXPECTED_ANY} l left operand
579
+ * @param {EXPECTED_ANY} r right operand
580
+ * @returns {EXPECTED_ANY} the result
581
+ */
582
+ const SHIFT_RIGHT = (l, r) => l >> r;
583
+
584
+ /**
585
+ * @param {EXPECTED_ANY} l left operand
586
+ * @param {EXPECTED_ANY} r right operand
587
+ * @returns {EXPECTED_ANY} the result
588
+ */
589
+ const SHIFT_LEFT = (l, r) => l << r;
590
+
591
+ /**
592
+ * @param {EXPECTED_ANY} l left operand
593
+ * @param {EXPECTED_ANY} r right operand
594
+ * @returns {EXPECTED_ANY} the result
595
+ */
596
+ const LESS_THAN = (l, r) => l < r;
597
+
598
+ /**
599
+ * @param {EXPECTED_ANY} l left operand
600
+ * @param {EXPECTED_ANY} r right operand
601
+ * @returns {EXPECTED_ANY} the result
602
+ */
603
+ const GREATER_THAN = (l, r) => l > r;
604
+
605
+ /**
606
+ * @param {EXPECTED_ANY} l left operand
607
+ * @param {EXPECTED_ANY} r right operand
608
+ * @returns {EXPECTED_ANY} the result
609
+ */
610
+ const LESS_THAN_OR_EQUAL = (l, r) => l <= r;
611
+
612
+ /**
613
+ * @param {EXPECTED_ANY} l left operand
614
+ * @param {EXPECTED_ANY} r right operand
615
+ * @returns {EXPECTED_ANY} the result
616
+ */
617
+ const GREATER_THAN_OR_EQUAL = (l, r) => l >= r;
618
+
619
+ /**
620
+ * @param {EXPECTED_ANY} v operand
621
+ * @returns {EXPECTED_ANY} the result
622
+ */
623
+ const BITWISE_NOT = (v) => ~v;
624
+
625
+ /**
626
+ * @param {EXPECTED_ANY} v operand
627
+ * @returns {EXPECTED_ANY} the result
628
+ */
629
+ // eslint-disable-next-line no-implicit-coercion
630
+ const TO_NUMBER = (v) => +v;
631
+
632
+ /**
633
+ * @param {EXPECTED_ANY} v operand
634
+ * @returns {EXPECTED_ANY} the result
635
+ */
636
+ const NEGATE = (v) => -v;
637
+
638
+ /**
639
+ * Evaluates a binary expression if and only if it is a const operation (e.g. 1 + 2, "a" + "b", etc.).
640
+ * @template T
641
+ * @param {JavascriptParser} parser the parser evaluating the expression
642
+ * @param {BinaryExpression} expr the binary expression
643
+ * @param {(leftOperand: T, rightOperand: T) => boolean | number | bigint | string} operandHandler the handler for the operation (e.g. (a, b) => a + b)
644
+ * @returns {BasicEvaluatedExpression | undefined} the evaluated expression
645
+ */
646
+ const handleConstOperation = (parser, expr, operandHandler) => {
647
+ const left = parser.evaluateExpression(expr.left);
648
+ if (!left.isCompileTimeValue()) return;
649
+
650
+ const right = parser.evaluateExpression(expr.right);
651
+ if (!right.isCompileTimeValue()) return;
652
+
653
+ const result = operandHandler(
654
+ /** @type {T} */ (left.asCompileTimeValue()),
655
+ /** @type {T} */ (right.asCompileTimeValue())
656
+ );
657
+ return valueAsExpression(
658
+ result,
659
+ expr,
660
+ left.couldHaveSideEffects() || right.couldHaveSideEffects()
661
+ );
662
+ };
663
+
664
+ /**
665
+ * Helper function to determine if two booleans are always different. This is used in `handleStrictEqualityComparison`
666
+ * to determine if an expressions boolean or nullish conversion is equal or not.
667
+ * @param {boolean} a first boolean to compare
668
+ * @param {boolean} b second boolean to compare
669
+ * @returns {boolean} true if the two booleans are always different, false otherwise
670
+ */
671
+ const isAlwaysDifferent = (a, b) =>
672
+ (a === true && b === false) || (a === false && b === true);
673
+
674
+ /**
675
+ * Handle template string compare.
676
+ * @param {BasicEvaluatedExpression} left left
677
+ * @param {BasicEvaluatedExpression} right right
678
+ * @param {BinaryExpression} expr the comparison, for its range
679
+ * @param {boolean} eql true for "===" and false for "!=="
680
+ * @returns {BasicEvaluatedExpression | undefined} result
681
+ */
682
+ const handleTemplateStringCompare = (left, right, expr, eql) => {
683
+ /**
684
+ * Returns value.
685
+ * @param {BasicEvaluatedExpression[]} parts parts
686
+ * @returns {string} value
687
+ */
688
+ const getPrefix = (parts) => {
689
+ let value = "";
690
+ for (const p of parts) {
691
+ const v = p.asString();
692
+ if (v !== undefined) value += v;
693
+ else break;
694
+ }
695
+ return value;
696
+ };
697
+ /**
698
+ * Returns value.
699
+ * @param {BasicEvaluatedExpression[]} parts parts
700
+ * @returns {string} value
701
+ */
702
+ const getSuffix = (parts) => {
703
+ let value = "";
704
+ for (let i = parts.length - 1; i >= 0; i--) {
705
+ const v = parts[i].asString();
706
+ if (v !== undefined) value = v + value;
707
+ else break;
708
+ }
709
+ return value;
710
+ };
711
+ const leftPrefix = getPrefix(
712
+ /** @type {BasicEvaluatedExpression[]} */ (left.parts)
713
+ );
714
+ const rightPrefix = getPrefix(
715
+ /** @type {BasicEvaluatedExpression[]} */ (right.parts)
716
+ );
717
+ const leftSuffix = getSuffix(
718
+ /** @type {BasicEvaluatedExpression[]} */ (left.parts)
719
+ );
720
+ const rightSuffix = getSuffix(
721
+ /** @type {BasicEvaluatedExpression[]} */ (right.parts)
722
+ );
723
+ const lenPrefix = Math.min(leftPrefix.length, rightPrefix.length);
724
+ const lenSuffix = Math.min(leftSuffix.length, rightSuffix.length);
725
+ const prefixMismatch =
726
+ lenPrefix > 0 &&
727
+ leftPrefix.slice(0, lenPrefix) !== rightPrefix.slice(0, lenPrefix);
728
+ const suffixMismatch =
729
+ lenSuffix > 0 &&
730
+ leftSuffix.slice(-lenSuffix) !== rightSuffix.slice(-lenSuffix);
731
+ if (prefixMismatch || suffixMismatch) {
732
+ return new BasicEvaluatedExpression()
733
+ .setRange(/** @type {Range} */ (expr.range))
734
+ .setBoolean(!eql)
735
+ .setSideEffects(
736
+ left.couldHaveSideEffects() || right.couldHaveSideEffects()
737
+ );
738
+ }
739
+ };
740
+
741
+ /**
742
+ * Helper function to handle BinaryExpressions using strict equality comparisons (e.g. "===" and "!==").
743
+ * @param {JavascriptParser} parser the parser evaluating the expression
744
+ * @param {BinaryExpression} expr the binary expression
745
+ * @param {boolean} eql true for "===" and false for "!=="
746
+ * @returns {BasicEvaluatedExpression | undefined} the evaluated expression
747
+ */
748
+ const handleStrictEqualityComparison = (parser, expr, eql) => {
749
+ const left = parser.evaluateExpression(expr.left);
750
+ const right = parser.evaluateExpression(expr.right);
751
+
752
+ const leftConst = left.isCompileTimeValue();
753
+ const rightConst = right.isCompileTimeValue();
754
+
755
+ if (leftConst && rightConst) {
756
+ return new BasicEvaluatedExpression()
757
+ .setRange(/** @type {Range} */ (expr.range))
758
+ .setBoolean(
759
+ eql === (left.asCompileTimeValue() === right.asCompileTimeValue())
760
+ )
761
+ .setSideEffects(
762
+ left.couldHaveSideEffects() || right.couldHaveSideEffects()
763
+ );
764
+ }
765
+
766
+ if (left.isArray() && right.isArray()) {
767
+ return new BasicEvaluatedExpression()
768
+ .setRange(/** @type {Range} */ (expr.range))
769
+ .setBoolean(!eql)
770
+ .setSideEffects(
771
+ left.couldHaveSideEffects() || right.couldHaveSideEffects()
772
+ );
773
+ }
774
+ if (left.isTemplateString() && right.isTemplateString()) {
775
+ return handleTemplateStringCompare(left, right, expr, eql);
776
+ }
777
+
778
+ const leftPrimitive = left.isPrimitiveType();
779
+ const rightPrimitive = right.isPrimitiveType();
780
+
781
+ if (
782
+ // Primitive !== Object or
783
+ // compile-time object types are never equal to something at runtime
784
+ (leftPrimitive === false && (leftConst || rightPrimitive === true)) ||
785
+ (rightPrimitive === false && (rightConst || leftPrimitive === true)) ||
786
+ // Different nullish or boolish status also means not equal
787
+ isAlwaysDifferent(
788
+ /** @type {boolean} */ (left.asBool()),
789
+ /** @type {boolean} */ (right.asBool())
790
+ ) ||
791
+ isAlwaysDifferent(
792
+ /** @type {boolean} */ (left.asNullish()),
793
+ /** @type {boolean} */ (right.asNullish())
794
+ )
795
+ ) {
796
+ return new BasicEvaluatedExpression()
797
+ .setRange(/** @type {Range} */ (expr.range))
798
+ .setBoolean(!eql)
799
+ .setSideEffects(
800
+ left.couldHaveSideEffects() || right.couldHaveSideEffects()
801
+ );
802
+ }
803
+ };
804
+
805
+ /**
806
+ * Helper function to handle BinaryExpressions using abstract equality comparisons (e.g. "==" and "!=").
807
+ * @param {JavascriptParser} parser the parser evaluating the expression
808
+ * @param {BinaryExpression} expr the binary expression
809
+ * @param {boolean} eql true for "==" and false for "!="
810
+ * @returns {BasicEvaluatedExpression | undefined} the evaluated expression
811
+ */
812
+ const handleAbstractEqualityComparison = (parser, expr, eql) => {
813
+ const left = parser.evaluateExpression(expr.left);
814
+ const right = parser.evaluateExpression(expr.right);
815
+
816
+ const leftConst = left.isCompileTimeValue();
817
+ const rightConst = right.isCompileTimeValue();
818
+
819
+ if (leftConst && rightConst) {
820
+ return new BasicEvaluatedExpression()
821
+ .setRange(/** @type {Range} */ (expr.range))
822
+ .setBoolean(
823
+ eql ===
824
+ // eslint-disable-next-line eqeqeq
825
+ (left.asCompileTimeValue() == right.asCompileTimeValue())
826
+ )
827
+ .setSideEffects(
828
+ left.couldHaveSideEffects() || right.couldHaveSideEffects()
829
+ );
830
+ }
831
+
832
+ if (left.isArray() && right.isArray()) {
833
+ return new BasicEvaluatedExpression()
834
+ .setRange(/** @type {Range} */ (expr.range))
835
+ .setBoolean(!eql)
836
+ .setSideEffects(
837
+ left.couldHaveSideEffects() || right.couldHaveSideEffects()
838
+ );
839
+ }
840
+ if (left.isTemplateString() && right.isTemplateString()) {
841
+ return handleTemplateStringCompare(left, right, expr, eql);
842
+ }
843
+ };
844
+
845
+ /**
846
+ * Evaluates a UnaryExpression if and only if it is a basic const operator (e.g. +a, -a, ~a).
847
+ * @template T
848
+ * @param {JavascriptParser} parser the parser evaluating the expression
849
+ * @param {UnaryExpression} expr the unary expression
850
+ * @param {(operand: T) => boolean | number | bigint | string} operandHandler handler for the operand
851
+ * @returns {BasicEvaluatedExpression | undefined} evaluated expression
852
+ */
853
+ const handleUnaryConstOperation = (parser, expr, operandHandler) => {
854
+ const argument = parser.evaluateExpression(expr.argument);
855
+ if (!argument.isCompileTimeValue()) return;
856
+ const result = operandHandler(
857
+ /** @type {T} */ (argument.asCompileTimeValue())
858
+ );
859
+ return valueAsExpression(result, expr, argument.couldHaveSideEffects());
860
+ };
861
+
451
862
  /**
452
863
  * Grabs the name of a given expression and returns it as a string or undefined. Has particular
453
864
  * handling for [Identifiers](https://github.com/estree/estree/blob/master/es5.md#identifier),
@@ -802,6 +1213,32 @@ class JavascriptParser extends Parser {
802
1213
  // Offset of each line's first character, built on first getLocation use.
803
1214
  /** @type {number[] | undefined} */
804
1215
  this._lineStarts = undefined;
1216
+ // The last member expression resolved, kept while the node and the scope
1217
+ // it resolved in stay the same: the walk and an evaluate of one node meet
1218
+ /** @type {Expression | Super | undefined} */
1219
+ this._memberExpression = undefined;
1220
+ /** @type {ScopeInfo | undefined} */
1221
+ this._memberScope = undefined;
1222
+ /** @type {number} */
1223
+ this._memberMask = 0;
1224
+ /** @type {0 | 1 | 2} */
1225
+ this._memberType = 0;
1226
+ /** @type {string} */
1227
+ this._memberName = "";
1228
+ /** @type {string | VariableInfo | undefined} */
1229
+ this._memberRootInfo = undefined;
1230
+ /** @type {Members} */
1231
+ this._members = EMPTY_ARRAY;
1232
+ /** @type {MembersOptionals} */
1233
+ this._membersOptionals = EMPTY_MEMBERS_OPTIONALS;
1234
+ /** @type {CallExpression | undefined} */
1235
+ this._memberCall = undefined;
1236
+ /** @type {string} */
1237
+ this._memberCalleeName = "";
1238
+ /** @type {CalleeMembers} */
1239
+ this._memberCalleeMembers = EMPTY_ARRAY;
1240
+ /** @type {CallExpressionInfo | ExpressionExpressionInfo | undefined} */
1241
+ this._memberInfo = undefined;
805
1242
  // True while only the parser's own taps sit on evaluate.for("Identifier");
806
1243
  // recomputed per parse, gates the defined-identifier fast paths.
807
1244
  this._evalIdentOwnTaps = false;
@@ -809,6 +1246,22 @@ class JavascriptParser extends Parser {
809
1246
  // Reused for enterPatterns on every scope entry to avoid per-scope closures.
810
1247
  /** @type {OnIdentString} */
811
1248
  this._defineVariable = (ident) => this.defineVariable(ident);
1249
+ // The expression an identifier evaluator is running for, read by the
1250
+ // shared `_evaluateDefinedIdentifier` instead of a closure per evaluation
1251
+ /** @type {Identifier | ThisExpression | MemberExpression | undefined} */
1252
+ this._evaluatingExpression = undefined;
1253
+ /** @type {(name?: string) => BasicEvaluatedExpression | null | undefined} */
1254
+ this._evaluateDefinedIdentifier = (name) => {
1255
+ const hook = this.hooks.evaluateDefinedIdentifier.get(
1256
+ /** @type {string} */ (name)
1257
+ );
1258
+ if (hook !== undefined) {
1259
+ return hook.call(
1260
+ /** @type {Identifier | ThisExpression | MemberExpression} */
1261
+ (this._evaluatingExpression)
1262
+ );
1263
+ }
1264
+ };
812
1265
  this._initializeEvaluating();
813
1266
  // keyed taps are free for every other member chain, and a shadowed
814
1267
  // `arguments` binding never dispatches the free-variable hooks
@@ -867,7 +1320,7 @@ class JavascriptParser extends Parser {
867
1320
  const callee = expr.callee;
868
1321
  if (callee.type !== "Identifier") return;
869
1322
  if (callee.name !== "RegExp") {
870
- return this.callHooksForName(
1323
+ return this._callHooksForName1(
871
1324
  this.hooks.evaluateNewExpression,
872
1325
  callee.name,
873
1326
  expr
@@ -979,277 +1432,9 @@ class JavascriptParser extends Parser {
979
1432
  }
980
1433
  });
981
1434
 
982
- /**
983
- * In simple logical cases, we can use valueAsExpression to assist us in evaluating the expression on
984
- * either side of a [BinaryExpression](https://github.com/estree/estree/blob/master/es5.md#binaryexpression).
985
- * This supports scenarios in webpack like conditionally `import()`'ing modules based on some simple evaluation:
986
- *
987
- * ```js
988
- * if (1 === 3) {
989
- * import("./moduleA"); // webpack will auto evaluate this and not import the modules
990
- * }
991
- * ```
992
- *
993
- * Additional scenarios include evaluation of strings inside of dynamic import statements:
994
- *
995
- * ```js
996
- * const foo = "foo";
997
- * const bar = "bar";
998
- *
999
- * import("./" + foo + bar); // webpack will auto evaluate this into import("./foobar")
1000
- * ```
1001
- * @param {boolean | number | bigint | string} value the value to convert to an expression
1002
- * @param {BinaryExpression | UnaryExpression} expr the expression being evaluated
1003
- * @param {boolean} sideEffects whether the expression has side effects
1004
- * @returns {BasicEvaluatedExpression | undefined} the evaluated expression
1005
- * @example
1006
- *
1007
- * ```js
1008
- * const binaryExpr = new BinaryExpression("+",
1009
- * { type: "Literal", value: 2 },
1010
- * { type: "Literal", value: 3 }
1011
- * );
1012
- *
1013
- * const leftValue = 2;
1014
- * const rightValue = 3;
1015
- *
1016
- * const leftExpr = valueAsExpression(leftValue, binaryExpr.left, false);
1017
- * const rightExpr = valueAsExpression(rightValue, binaryExpr.right, false);
1018
- * const result = new BasicEvaluatedExpression()
1019
- * .setNumber(leftExpr.number + rightExpr.number)
1020
- * .setRange(binaryExpr.range);
1021
- *
1022
- * console.log(result.number); // Output: 5
1023
- * ```
1024
- */
1025
- const valueAsExpression = (value, expr, sideEffects) => {
1026
- switch (typeof value) {
1027
- case "boolean":
1028
- return new BasicEvaluatedExpression()
1029
- .setBoolean(value)
1030
- .setSideEffects(sideEffects)
1031
- .setRange(/** @type {Range} */ (expr.range));
1032
- case "number":
1033
- return new BasicEvaluatedExpression()
1034
- .setNumber(value)
1035
- .setSideEffects(sideEffects)
1036
- .setRange(/** @type {Range} */ (expr.range));
1037
- case "bigint":
1038
- return new BasicEvaluatedExpression()
1039
- .setBigInt(value)
1040
- .setSideEffects(sideEffects)
1041
- .setRange(/** @type {Range} */ (expr.range));
1042
- case "string":
1043
- return new BasicEvaluatedExpression()
1044
- .setString(value)
1045
- .setSideEffects(sideEffects)
1046
- .setRange(/** @type {Range} */ (expr.range));
1047
- }
1048
- };
1049
-
1050
1435
  this.hooks.evaluate.for("BinaryExpression").tap(CLASS_NAME, (_expr) => {
1051
1436
  const expr = /** @type {BinaryExpression} */ (_expr);
1052
1437
 
1053
- /**
1054
- * Evaluates a binary expression if and only if it is a const operation (e.g. 1 + 2, "a" + "b", etc.).
1055
- * @template T
1056
- * @param {(leftOperand: T, rightOperand: T) => boolean | number | bigint | string} operandHandler the handler for the operation (e.g. (a, b) => a + b)
1057
- * @returns {BasicEvaluatedExpression | undefined} the evaluated expression
1058
- */
1059
- const handleConstOperation = (operandHandler) => {
1060
- const left = this.evaluateExpression(expr.left);
1061
- if (!left.isCompileTimeValue()) return;
1062
-
1063
- const right = this.evaluateExpression(expr.right);
1064
- if (!right.isCompileTimeValue()) return;
1065
-
1066
- const result = operandHandler(
1067
- /** @type {T} */ (left.asCompileTimeValue()),
1068
- /** @type {T} */ (right.asCompileTimeValue())
1069
- );
1070
- return valueAsExpression(
1071
- result,
1072
- expr,
1073
- left.couldHaveSideEffects() || right.couldHaveSideEffects()
1074
- );
1075
- };
1076
-
1077
- /**
1078
- * Helper function to determine if two booleans are always different. This is used in `handleStrictEqualityComparison`
1079
- * to determine if an expressions boolean or nullish conversion is equal or not.
1080
- * @param {boolean} a first boolean to compare
1081
- * @param {boolean} b second boolean to compare
1082
- * @returns {boolean} true if the two booleans are always different, false otherwise
1083
- */
1084
- const isAlwaysDifferent = (a, b) =>
1085
- (a === true && b === false) || (a === false && b === true);
1086
-
1087
- /**
1088
- * Handle template string compare.
1089
- * @param {BasicEvaluatedExpression} left left
1090
- * @param {BasicEvaluatedExpression} right right
1091
- * @param {BasicEvaluatedExpression} res res
1092
- * @param {boolean} eql true for "===" and false for "!=="
1093
- * @returns {BasicEvaluatedExpression | undefined} result
1094
- */
1095
- const handleTemplateStringCompare = (left, right, res, eql) => {
1096
- /**
1097
- * Returns value.
1098
- * @param {BasicEvaluatedExpression[]} parts parts
1099
- * @returns {string} value
1100
- */
1101
- const getPrefix = (parts) => {
1102
- let value = "";
1103
- for (const p of parts) {
1104
- const v = p.asString();
1105
- if (v !== undefined) value += v;
1106
- else break;
1107
- }
1108
- return value;
1109
- };
1110
- /**
1111
- * Returns value.
1112
- * @param {BasicEvaluatedExpression[]} parts parts
1113
- * @returns {string} value
1114
- */
1115
- const getSuffix = (parts) => {
1116
- let value = "";
1117
- for (let i = parts.length - 1; i >= 0; i--) {
1118
- const v = parts[i].asString();
1119
- if (v !== undefined) value = v + value;
1120
- else break;
1121
- }
1122
- return value;
1123
- };
1124
- const leftPrefix = getPrefix(
1125
- /** @type {BasicEvaluatedExpression[]} */ (left.parts)
1126
- );
1127
- const rightPrefix = getPrefix(
1128
- /** @type {BasicEvaluatedExpression[]} */ (right.parts)
1129
- );
1130
- const leftSuffix = getSuffix(
1131
- /** @type {BasicEvaluatedExpression[]} */ (left.parts)
1132
- );
1133
- const rightSuffix = getSuffix(
1134
- /** @type {BasicEvaluatedExpression[]} */ (right.parts)
1135
- );
1136
- const lenPrefix = Math.min(leftPrefix.length, rightPrefix.length);
1137
- const lenSuffix = Math.min(leftSuffix.length, rightSuffix.length);
1138
- const prefixMismatch =
1139
- lenPrefix > 0 &&
1140
- leftPrefix.slice(0, lenPrefix) !== rightPrefix.slice(0, lenPrefix);
1141
- const suffixMismatch =
1142
- lenSuffix > 0 &&
1143
- leftSuffix.slice(-lenSuffix) !== rightSuffix.slice(-lenSuffix);
1144
- if (prefixMismatch || suffixMismatch) {
1145
- return res
1146
- .setBoolean(!eql)
1147
- .setSideEffects(
1148
- left.couldHaveSideEffects() || right.couldHaveSideEffects()
1149
- );
1150
- }
1151
- };
1152
-
1153
- /**
1154
- * Helper function to handle BinaryExpressions using strict equality comparisons (e.g. "===" and "!==").
1155
- * @param {boolean} eql true for "===" and false for "!=="
1156
- * @returns {BasicEvaluatedExpression | undefined} the evaluated expression
1157
- */
1158
- const handleStrictEqualityComparison = (eql) => {
1159
- const left = this.evaluateExpression(expr.left);
1160
- const right = this.evaluateExpression(expr.right);
1161
- const res = new BasicEvaluatedExpression();
1162
- res.setRange(/** @type {Range} */ (expr.range));
1163
-
1164
- const leftConst = left.isCompileTimeValue();
1165
- const rightConst = right.isCompileTimeValue();
1166
-
1167
- if (leftConst && rightConst) {
1168
- return res
1169
- .setBoolean(
1170
- eql === (left.asCompileTimeValue() === right.asCompileTimeValue())
1171
- )
1172
- .setSideEffects(
1173
- left.couldHaveSideEffects() || right.couldHaveSideEffects()
1174
- );
1175
- }
1176
-
1177
- if (left.isArray() && right.isArray()) {
1178
- return res
1179
- .setBoolean(!eql)
1180
- .setSideEffects(
1181
- left.couldHaveSideEffects() || right.couldHaveSideEffects()
1182
- );
1183
- }
1184
- if (left.isTemplateString() && right.isTemplateString()) {
1185
- return handleTemplateStringCompare(left, right, res, eql);
1186
- }
1187
-
1188
- const leftPrimitive = left.isPrimitiveType();
1189
- const rightPrimitive = right.isPrimitiveType();
1190
-
1191
- if (
1192
- // Primitive !== Object or
1193
- // compile-time object types are never equal to something at runtime
1194
- (leftPrimitive === false && (leftConst || rightPrimitive === true)) ||
1195
- (rightPrimitive === false &&
1196
- (rightConst || leftPrimitive === true)) ||
1197
- // Different nullish or boolish status also means not equal
1198
- isAlwaysDifferent(
1199
- /** @type {boolean} */ (left.asBool()),
1200
- /** @type {boolean} */ (right.asBool())
1201
- ) ||
1202
- isAlwaysDifferent(
1203
- /** @type {boolean} */ (left.asNullish()),
1204
- /** @type {boolean} */ (right.asNullish())
1205
- )
1206
- ) {
1207
- return res
1208
- .setBoolean(!eql)
1209
- .setSideEffects(
1210
- left.couldHaveSideEffects() || right.couldHaveSideEffects()
1211
- );
1212
- }
1213
- };
1214
-
1215
- /**
1216
- * Helper function to handle BinaryExpressions using abstract equality comparisons (e.g. "==" and "!=").
1217
- * @param {boolean} eql true for "==" and false for "!="
1218
- * @returns {BasicEvaluatedExpression | undefined} the evaluated expression
1219
- */
1220
- const handleAbstractEqualityComparison = (eql) => {
1221
- const left = this.evaluateExpression(expr.left);
1222
- const right = this.evaluateExpression(expr.right);
1223
- const res = new BasicEvaluatedExpression();
1224
- res.setRange(/** @type {Range} */ (expr.range));
1225
-
1226
- const leftConst = left.isCompileTimeValue();
1227
- const rightConst = right.isCompileTimeValue();
1228
-
1229
- if (leftConst && rightConst) {
1230
- return res
1231
- .setBoolean(
1232
- eql ===
1233
- // eslint-disable-next-line eqeqeq
1234
- (left.asCompileTimeValue() == right.asCompileTimeValue())
1235
- )
1236
- .setSideEffects(
1237
- left.couldHaveSideEffects() || right.couldHaveSideEffects()
1238
- );
1239
- }
1240
-
1241
- if (left.isArray() && right.isArray()) {
1242
- return res
1243
- .setBoolean(!eql)
1244
- .setSideEffects(
1245
- left.couldHaveSideEffects() || right.couldHaveSideEffects()
1246
- );
1247
- }
1248
- if (left.isTemplateString() && right.isTemplateString()) {
1249
- return handleTemplateStringCompare(left, right, res, eql);
1250
- }
1251
- };
1252
-
1253
1438
  if (expr.operator === "+") {
1254
1439
  const left = this.evaluateExpression(expr.left);
1255
1440
  const right = this.evaluateExpression(expr.right);
@@ -1416,41 +1601,41 @@ class JavascriptParser extends Parser {
1416
1601
  res.setRange(/** @type {Range} */ (expr.range));
1417
1602
  return res;
1418
1603
  } else if (expr.operator === "-") {
1419
- return handleConstOperation((l, r) => l - r);
1604
+ return handleConstOperation(this, expr, SUBTRACT);
1420
1605
  } else if (expr.operator === "*") {
1421
- return handleConstOperation((l, r) => l * r);
1606
+ return handleConstOperation(this, expr, MULTIPLY);
1422
1607
  } else if (expr.operator === "/") {
1423
- return handleConstOperation((l, r) => l / r);
1608
+ return handleConstOperation(this, expr, DIVIDE);
1424
1609
  } else if (expr.operator === "**") {
1425
- return handleConstOperation((l, r) => l ** r);
1610
+ return handleConstOperation(this, expr, POWER);
1426
1611
  } else if (expr.operator === "===") {
1427
- return handleStrictEqualityComparison(true);
1612
+ return handleStrictEqualityComparison(this, expr, true);
1428
1613
  } else if (expr.operator === "==") {
1429
- return handleAbstractEqualityComparison(true);
1614
+ return handleAbstractEqualityComparison(this, expr, true);
1430
1615
  } else if (expr.operator === "!==") {
1431
- return handleStrictEqualityComparison(false);
1616
+ return handleStrictEqualityComparison(this, expr, false);
1432
1617
  } else if (expr.operator === "!=") {
1433
- return handleAbstractEqualityComparison(false);
1618
+ return handleAbstractEqualityComparison(this, expr, false);
1434
1619
  } else if (expr.operator === "&") {
1435
- return handleConstOperation((l, r) => l & r);
1620
+ return handleConstOperation(this, expr, BITWISE_AND);
1436
1621
  } else if (expr.operator === "|") {
1437
- return handleConstOperation((l, r) => l | r);
1622
+ return handleConstOperation(this, expr, BITWISE_OR);
1438
1623
  } else if (expr.operator === "^") {
1439
- return handleConstOperation((l, r) => l ^ r);
1624
+ return handleConstOperation(this, expr, BITWISE_XOR);
1440
1625
  } else if (expr.operator === ">>>") {
1441
- return handleConstOperation((l, r) => l >>> r);
1626
+ return handleConstOperation(this, expr, UNSIGNED_SHIFT_RIGHT);
1442
1627
  } else if (expr.operator === ">>") {
1443
- return handleConstOperation((l, r) => l >> r);
1628
+ return handleConstOperation(this, expr, SHIFT_RIGHT);
1444
1629
  } else if (expr.operator === "<<") {
1445
- return handleConstOperation((l, r) => l << r);
1630
+ return handleConstOperation(this, expr, SHIFT_LEFT);
1446
1631
  } else if (expr.operator === "<") {
1447
- return handleConstOperation((l, r) => l < r);
1632
+ return handleConstOperation(this, expr, LESS_THAN);
1448
1633
  } else if (expr.operator === ">") {
1449
- return handleConstOperation((l, r) => l > r);
1634
+ return handleConstOperation(this, expr, GREATER_THAN);
1450
1635
  } else if (expr.operator === "<=") {
1451
- return handleConstOperation((l, r) => l <= r);
1636
+ return handleConstOperation(this, expr, LESS_THAN_OR_EQUAL);
1452
1637
  } else if (expr.operator === ">=") {
1453
- return handleConstOperation((l, r) => l >= r);
1638
+ return handleConstOperation(this, expr, GREATER_THAN_OR_EQUAL);
1454
1639
  } else if (expr.operator === "in") {
1455
1640
  // `x in y` always evaluates to a boolean, so it is never nullish
1456
1641
  const left = this.evaluateExpression(expr.left);
@@ -1466,25 +1651,10 @@ class JavascriptParser extends Parser {
1466
1651
  this.hooks.evaluate.for("UnaryExpression").tap(CLASS_NAME, (_expr) => {
1467
1652
  const expr = /** @type {UnaryExpression} */ (_expr);
1468
1653
 
1469
- /**
1470
- * Evaluates a UnaryExpression if and only if it is a basic const operator (e.g. +a, -a, ~a).
1471
- * @template T
1472
- * @param {(operand: T) => boolean | number | bigint | string} operandHandler handler for the operand
1473
- * @returns {BasicEvaluatedExpression | undefined} evaluated expression
1474
- */
1475
- const handleConstOperation = (operandHandler) => {
1476
- const argument = this.evaluateExpression(expr.argument);
1477
- if (!argument.isCompileTimeValue()) return;
1478
- const result = operandHandler(
1479
- /** @type {T} */ (argument.asCompileTimeValue())
1480
- );
1481
- return valueAsExpression(result, expr, argument.couldHaveSideEffects());
1482
- };
1483
-
1484
1654
  if (expr.operator === "typeof") {
1485
1655
  switch (expr.argument.type) {
1486
1656
  case "Identifier": {
1487
- const res = this.callHooksForName(
1657
+ const res = this._callHooksForName1(
1488
1658
  this.hooks.evaluateTypeof,
1489
1659
  expr.argument.name,
1490
1660
  expr
@@ -1493,7 +1663,7 @@ class JavascriptParser extends Parser {
1493
1663
  break;
1494
1664
  }
1495
1665
  case "MetaProperty": {
1496
- const res = this.callHooksForName(
1666
+ const res = this._callHooksForName1(
1497
1667
  this.hooks.evaluateTypeof,
1498
1668
  /** @type {string} */
1499
1669
  (getRootName(expr.argument)),
@@ -1579,12 +1749,11 @@ class JavascriptParser extends Parser {
1579
1749
  .setSideEffects(argument.couldHaveSideEffects())
1580
1750
  .setRange(/** @type {Range} */ (expr.range));
1581
1751
  } else if (expr.operator === "~") {
1582
- return handleConstOperation((v) => ~v);
1752
+ return handleUnaryConstOperation(this, expr, BITWISE_NOT);
1583
1753
  } else if (expr.operator === "+") {
1584
- // eslint-disable-next-line no-implicit-coercion
1585
- return handleConstOperation((v) => +v);
1754
+ return handleUnaryConstOperation(this, expr, TO_NUMBER);
1586
1755
  } else if (expr.operator === "-") {
1587
- return handleConstOperation((v) => -v);
1756
+ return handleUnaryConstOperation(this, expr, NEGATE);
1588
1757
  }
1589
1758
  });
1590
1759
  this.hooks.evaluateTypeof
@@ -1623,18 +1792,18 @@ class JavascriptParser extends Parser {
1623
1792
  cachedExpression = expression;
1624
1793
  cachedInfo = info;
1625
1794
  if (info !== undefined) {
1626
- return this.callHooksForInfoWithFallback(
1795
+ // saved around the call: a hook may evaluate another identifier
1796
+ const outer = this._evaluatingExpression;
1797
+ this._evaluatingExpression = expression;
1798
+ const result = this.callHooksForInfoWithFallback(
1627
1799
  this.hooks.evaluateIdentifier,
1628
1800
  info.name,
1629
- (_name) => undefined,
1630
- (name) => {
1631
- const hook = this.hooks.evaluateDefinedIdentifier.get(name);
1632
- if (hook !== undefined) {
1633
- return hook.call(expression);
1634
- }
1635
- },
1801
+ RETURN_UNDEFINED,
1802
+ this._evaluateDefinedIdentifier,
1636
1803
  expression
1637
1804
  );
1805
+ this._evaluatingExpression = outer;
1806
+ return result;
1638
1807
  }
1639
1808
  });
1640
1809
  this.hooks.evaluate
@@ -1699,7 +1868,7 @@ class JavascriptParser extends Parser {
1699
1868
  this.hooks.evaluate.for("MetaProperty").tap(CLASS_NAME, (expr) => {
1700
1869
  const metaProperty = /** @type {MetaProperty} */ (expr);
1701
1870
 
1702
- return this.callHooksForName(
1871
+ return this._callHooksForName1(
1703
1872
  this.hooks.evaluateIdentifier,
1704
1873
  /** @type {string} */
1705
1874
  (getRootName(metaProperty)),
@@ -1733,7 +1902,7 @@ class JavascriptParser extends Parser {
1733
1902
  return hook.call(expr, param);
1734
1903
  }
1735
1904
  } else if (expr.callee.type === "Identifier") {
1736
- return this.callHooksForName(
1905
+ return this._callHooksForName1(
1737
1906
  this.hooks.evaluateCallExpression,
1738
1907
  expr.callee.name,
1739
1908
  expr
@@ -2504,13 +2673,14 @@ class JavascriptParser extends Parser {
2504
2673
  * @param {BlockStatement | StaticBlock} statement block statement
2505
2674
  */
2506
2675
  walkBlockStatement(statement) {
2507
- this.inBlockScope(() => {
2508
- const body = statement.body;
2509
- const prev = this.prevStatement;
2510
- this.blockPreWalkStatements(body);
2511
- this.prevStatement = prev;
2512
- this.walkStatements(body);
2513
- }, true);
2676
+ const oldScope = this._enterBlockScope();
2677
+ const body = statement.body;
2678
+ const prev = this.prevStatement;
2679
+ this.blockPreWalkStatements(body);
2680
+ this.prevStatement = prev;
2681
+ this.walkStatements(body);
2682
+
2683
+ this._exitBlockScope(oldScope, true);
2514
2684
  }
2515
2685
 
2516
2686
  /**
@@ -2562,18 +2732,27 @@ class JavascriptParser extends Parser {
2562
2732
  if (result === undefined) {
2563
2733
  const guard = this.hooks.collectGuards.call(statement.test);
2564
2734
  this.walkExpression(statement.test);
2565
- this.walkGuardedBranch(guard ? guard.consequent : undefined, () =>
2566
- this.walkNestedStatement(statement.consequent)
2567
- );
2735
+ // allocate the branch closure only when a guard frame needs pushing
2736
+ if (guard && guard.consequent) {
2737
+ this.walkGuardedBranch(guard.consequent, () =>
2738
+ this.walkNestedStatement(statement.consequent)
2739
+ );
2740
+ } else {
2741
+ this.walkNestedStatement(statement.consequent);
2742
+ }
2568
2743
 
2569
2744
  const consequentTerminated = this.scope.terminated;
2570
2745
  this.scope.terminated = undefined;
2571
2746
 
2572
2747
  if (statement.alternate) {
2573
2748
  const alternate = statement.alternate;
2574
- this.walkGuardedBranch(guard ? guard.alternate : undefined, () =>
2575
- this.walkNestedStatement(alternate)
2576
- );
2749
+ if (guard && guard.alternate) {
2750
+ this.walkGuardedBranch(guard.alternate, () =>
2751
+ this.walkNestedStatement(alternate)
2752
+ );
2753
+ } else {
2754
+ this.walkNestedStatement(alternate);
2755
+ }
2577
2756
  }
2578
2757
 
2579
2758
  const alternateTerminated = this.scope.terminated;
@@ -2607,9 +2786,10 @@ class JavascriptParser extends Parser {
2607
2786
  const result = hook.call(statement);
2608
2787
  if (result === true) return;
2609
2788
  }
2610
- this.inBlockScope(() => {
2611
- this.walkNestedStatement(statement.body);
2612
- });
2789
+ const oldScope = this._enterBlockScope();
2790
+ this.walkNestedStatement(statement.body);
2791
+
2792
+ this._exitBlockScope(oldScope, false);
2613
2793
  }
2614
2794
 
2615
2795
  /**
@@ -2631,10 +2811,11 @@ class JavascriptParser extends Parser {
2631
2811
  statement
2632
2812
  );
2633
2813
  }
2634
- this.inBlockScope(() => {
2635
- this.walkExpression(statement.object);
2636
- this.walkNestedStatement(statement.body);
2637
- });
2814
+ const oldScope = this._enterBlockScope();
2815
+ this.walkExpression(statement.object);
2816
+ this.walkNestedStatement(statement.body);
2817
+
2818
+ this._exitBlockScope(oldScope, false);
2638
2819
  }
2639
2820
 
2640
2821
  /**
@@ -2747,10 +2928,11 @@ class JavascriptParser extends Parser {
2747
2928
  * @param {WhileStatement} statement while statement
2748
2929
  */
2749
2930
  walkWhileStatement(statement) {
2750
- this.inBlockScope(() => {
2751
- this.walkExpression(statement.test);
2752
- this.walkNestedStatement(statement.body);
2753
- });
2931
+ const oldScope = this._enterBlockScope();
2932
+ this.walkExpression(statement.test);
2933
+ this.walkNestedStatement(statement.body);
2934
+
2935
+ this._exitBlockScope(oldScope, false);
2754
2936
  }
2755
2937
 
2756
2938
  /**
@@ -2766,10 +2948,11 @@ class JavascriptParser extends Parser {
2766
2948
  * @param {DoWhileStatement} statement do while statement
2767
2949
  */
2768
2950
  walkDoWhileStatement(statement) {
2769
- this.inBlockScope(() => {
2770
- this.walkNestedStatement(statement.body);
2771
- this.walkExpression(statement.test);
2772
- });
2951
+ const oldScope = this._enterBlockScope();
2952
+ this.walkNestedStatement(statement.body);
2953
+ this.walkExpression(statement.test);
2954
+
2955
+ this._exitBlockScope(oldScope, false);
2773
2956
  }
2774
2957
 
2775
2958
  /**
@@ -2788,35 +2971,36 @@ class JavascriptParser extends Parser {
2788
2971
  * @param {ForStatement} statement for statement
2789
2972
  */
2790
2973
  walkForStatement(statement) {
2791
- this.inBlockScope(() => {
2792
- if (statement.init) {
2793
- if (statement.init.type === "VariableDeclaration") {
2794
- this.blockPreWalkVariableDeclaration(statement.init);
2795
- this.prevStatement = undefined;
2796
- this.walkStatement(statement.init);
2797
- } else {
2798
- this.walkExpression(statement.init);
2799
- }
2800
- }
2801
- if (statement.test) {
2802
- this.walkExpression(statement.test);
2803
- }
2804
- if (statement.update) {
2805
- this.walkExpression(statement.update);
2974
+ const oldScope = this._enterBlockScope();
2975
+ if (statement.init) {
2976
+ if (statement.init.type === "VariableDeclaration") {
2977
+ this.blockPreWalkVariableDeclaration(statement.init);
2978
+ this.prevStatement = undefined;
2979
+ this.walkStatement(statement.init);
2980
+ } else {
2981
+ this.walkExpression(statement.init);
2806
2982
  }
2983
+ }
2984
+ if (statement.test) {
2985
+ this.walkExpression(statement.test);
2986
+ }
2987
+ if (statement.update) {
2988
+ this.walkExpression(statement.update);
2989
+ }
2807
2990
 
2808
- const body = statement.body;
2991
+ const body = statement.body;
2809
2992
 
2810
- if (body.type === "BlockStatement") {
2811
- // no need to add additional scope
2812
- const prev = this.prevStatement;
2813
- this.blockPreWalkStatements(body.body);
2814
- this.prevStatement = prev;
2815
- this.walkStatements(body.body);
2816
- } else {
2817
- this.walkNestedStatement(body);
2818
- }
2819
- });
2993
+ if (body.type === "BlockStatement") {
2994
+ // no need to add additional scope
2995
+ const prev = this.prevStatement;
2996
+ this.blockPreWalkStatements(body.body);
2997
+ this.prevStatement = prev;
2998
+ this.walkStatements(body.body);
2999
+ } else {
3000
+ this.walkNestedStatement(body);
3001
+ }
3002
+
3003
+ this._exitBlockScope(oldScope, false);
2820
3004
  }
2821
3005
 
2822
3006
  /**
@@ -2835,28 +3019,29 @@ class JavascriptParser extends Parser {
2835
3019
  * @param {ForInStatement} statement for statement
2836
3020
  */
2837
3021
  walkForInStatement(statement) {
2838
- this.inBlockScope(() => {
2839
- if (statement.left.type === "VariableDeclaration") {
2840
- this.blockPreWalkVariableDeclaration(statement.left);
2841
- this.walkVariableDeclaration(statement.left);
2842
- } else {
2843
- this.walkPattern(statement.left);
2844
- }
3022
+ const oldScope = this._enterBlockScope();
3023
+ if (statement.left.type === "VariableDeclaration") {
3024
+ this.blockPreWalkVariableDeclaration(statement.left);
3025
+ this.walkVariableDeclaration(statement.left);
3026
+ } else {
3027
+ this.walkPattern(statement.left);
3028
+ }
2845
3029
 
2846
- this.walkExpression(statement.right);
3030
+ this.walkExpression(statement.right);
2847
3031
 
2848
- const body = statement.body;
3032
+ const body = statement.body;
2849
3033
 
2850
- if (body.type === "BlockStatement") {
2851
- // no need to add additional scope
2852
- const prev = this.prevStatement;
2853
- this.blockPreWalkStatements(body.body);
2854
- this.prevStatement = prev;
2855
- this.walkStatements(body.body);
2856
- } else {
2857
- this.walkNestedStatement(body);
2858
- }
2859
- });
3034
+ if (body.type === "BlockStatement") {
3035
+ // no need to add additional scope
3036
+ const prev = this.prevStatement;
3037
+ this.blockPreWalkStatements(body.body);
3038
+ this.prevStatement = prev;
3039
+ this.walkStatements(body.body);
3040
+ } else {
3041
+ this.walkNestedStatement(body);
3042
+ }
3043
+
3044
+ this._exitBlockScope(oldScope, false);
2860
3045
  }
2861
3046
 
2862
3047
  /**
@@ -2878,28 +3063,29 @@ class JavascriptParser extends Parser {
2878
3063
  * @param {ForOfStatement} statement for statement
2879
3064
  */
2880
3065
  walkForOfStatement(statement) {
2881
- this.inBlockScope(() => {
2882
- if (statement.left.type === "VariableDeclaration") {
2883
- this.blockPreWalkVariableDeclaration(statement.left);
2884
- this.walkVariableDeclaration(statement.left);
2885
- } else {
2886
- this.walkPattern(statement.left);
2887
- }
3066
+ const oldScope = this._enterBlockScope();
3067
+ if (statement.left.type === "VariableDeclaration") {
3068
+ this.blockPreWalkVariableDeclaration(statement.left);
3069
+ this.walkVariableDeclaration(statement.left);
3070
+ } else {
3071
+ this.walkPattern(statement.left);
3072
+ }
2888
3073
 
2889
- this.walkExpression(statement.right);
3074
+ this.walkExpression(statement.right);
2890
3075
 
2891
- const body = statement.body;
3076
+ const body = statement.body;
2892
3077
 
2893
- if (body.type === "BlockStatement") {
2894
- // no need to add additional scope
2895
- const prev = this.prevStatement;
2896
- this.blockPreWalkStatements(body.body);
2897
- this.prevStatement = prev;
2898
- this.walkStatements(body.body);
2899
- } else {
2900
- this.walkNestedStatement(body);
2901
- }
2902
- });
3078
+ if (body.type === "BlockStatement") {
3079
+ // no need to add additional scope
3080
+ const prev = this.prevStatement;
3081
+ this.blockPreWalkStatements(body.body);
3082
+ this.prevStatement = prev;
3083
+ this.walkStatements(body.body);
3084
+ } else {
3085
+ this.walkNestedStatement(body);
3086
+ }
3087
+
3088
+ this._exitBlockScope(oldScope, false);
2903
3089
  }
2904
3090
 
2905
3091
  /**
@@ -2925,19 +3111,20 @@ class JavascriptParser extends Parser {
2925
3111
  if (this._strictInModuleOutput) {
2926
3112
  this._checkStrictModeParams(statement.params);
2927
3113
  }
2928
- this.inFunctionScope(true, statement.params, () => {
2929
- for (const param of statement.params) {
2930
- this.walkPattern(param);
2931
- }
3114
+ const oldScope = this._enterFunctionScope(true, statement.params);
3115
+ for (const param of statement.params) {
3116
+ this.walkPattern(param);
3117
+ }
2932
3118
 
2933
- this.detectMode(statement.body.body);
3119
+ this.detectMode(statement.body.body);
2934
3120
 
2935
- const prev = this.prevStatement;
3121
+ const prev = this.prevStatement;
2936
3122
 
2937
- this.preWalkStatement(statement.body);
2938
- this.prevStatement = prev;
2939
- this.walkStatement(statement.body);
2940
- });
3123
+ this.preWalkStatement(statement.body);
3124
+ this.prevStatement = prev;
3125
+ this.walkStatement(statement.body);
3126
+
3127
+ this.scope = oldScope;
2941
3128
  this.scope.topLevelScope = wasTopLevel;
2942
3129
  }
2943
3130
 
@@ -3303,7 +3490,9 @@ class JavascriptParser extends Parser {
3303
3490
  /** @type {OnIdent | undefined} */
3304
3491
  let onIdent;
3305
3492
  const checkStrictBindings = this._strictInModuleOutput;
3306
- for (const declarator of statement.declarations) {
3493
+ const declarations = statement.declarations;
3494
+ for (let i = 0, len = declarations.length; i < len; i++) {
3495
+ const declarator = declarations[i];
3307
3496
  if (declarator.type !== "VariableDeclarator") continue;
3308
3497
  this.preWalkVariableDeclarator(declarator);
3309
3498
  if (this.hooks.preDeclarator.call(declarator, statement)) continue;
@@ -3312,7 +3501,7 @@ class JavascriptParser extends Parser {
3312
3501
  if (checkStrictBindings) this._checkStrictModeBinding(id.name, id);
3313
3502
  // fast path: plain `const x =` skips the enterPattern dispatch and
3314
3503
  // the per-declaration onIdent closure (only patterns need it)
3315
- if (!this.callHooksForName(this.hooks.pattern, id.name, id)) {
3504
+ if (!this._callHooksForName1(this.hooks.pattern, id.name, id)) {
3316
3505
  this._defineVariableForDeclaration(id.name, id, hookMap);
3317
3506
  }
3318
3507
  } else {
@@ -3452,7 +3641,9 @@ class JavascriptParser extends Parser {
3452
3641
  * @param {VariableDeclaration} statement variable declaration
3453
3642
  */
3454
3643
  walkVariableDeclaration(statement) {
3455
- for (const declarator of statement.declarations) {
3644
+ const declarations = statement.declarations;
3645
+ for (let i = 0, len = declarations.length; i < len; i++) {
3646
+ const declarator = declarations[i];
3456
3647
  switch (declarator.type) {
3457
3648
  case "VariableDeclarator": {
3458
3649
  const renameIdentifier =
@@ -3523,40 +3714,35 @@ class JavascriptParser extends Parser {
3523
3714
  * @param {SwitchCase[]} switchCases switch statement
3524
3715
  */
3525
3716
  walkSwitchCases(switchCases) {
3526
- this.inBlockScope(() => {
3527
- const len = switchCases.length;
3528
-
3529
- // we need to pre walk all statements first since we can have invalid code
3530
- // import A from "module";
3531
- // switch(1) {
3532
- // case 1:
3533
- // console.log(A); // should fail at runtime
3534
- // case 2:
3535
- // const A = 1;
3536
- // }
3537
- for (let index = 0; index < len; index++) {
3538
- const switchCase = switchCases[index];
3539
-
3540
- if (switchCase.consequent.length > 0) {
3541
- const prev = this.prevStatement;
3542
- this.blockPreWalkStatements(switchCase.consequent);
3543
- this.prevStatement = prev;
3544
- }
3717
+ const oldScope = this._enterBlockScope();
3718
+ const len = switchCases.length;
3719
+
3720
+ // pre-walk every case first: a `const` declared in a later case is already
3721
+ // in scope for an earlier case that reads it (and fails at runtime)
3722
+ for (let index = 0; index < len; index++) {
3723
+ const switchCase = switchCases[index];
3724
+
3725
+ if (switchCase.consequent.length > 0) {
3726
+ const prev = this.prevStatement;
3727
+ this.blockPreWalkStatements(switchCase.consequent);
3728
+ this.prevStatement = prev;
3545
3729
  }
3730
+ }
3546
3731
 
3547
- for (let index = 0; index < len; index++) {
3548
- const switchCase = switchCases[index];
3732
+ for (let index = 0; index < len; index++) {
3733
+ const switchCase = switchCases[index];
3549
3734
 
3550
- if (switchCase.test) {
3551
- this.walkExpression(switchCase.test);
3552
- }
3735
+ if (switchCase.test) {
3736
+ this.walkExpression(switchCase.test);
3737
+ }
3553
3738
 
3554
- if (switchCase.consequent.length > 0) {
3555
- this.walkStatements(switchCase.consequent);
3556
- this.scope.terminated = undefined;
3557
- }
3739
+ if (switchCase.consequent.length > 0) {
3740
+ this.walkStatements(switchCase.consequent);
3741
+ this.scope.terminated = undefined;
3558
3742
  }
3559
- });
3743
+ }
3744
+
3745
+ this._exitBlockScope(oldScope, false);
3560
3746
  }
3561
3747
 
3562
3748
  /**
@@ -3572,22 +3758,23 @@ class JavascriptParser extends Parser {
3572
3758
  * @param {CatchClause} catchClause catch clause
3573
3759
  */
3574
3760
  walkCatchClause(catchClause) {
3575
- this.inBlockScope(() => {
3576
- // Error binding is optional in catch clause since ECMAScript 2019
3577
- if (catchClause.param !== null) {
3578
- this.enterPattern(catchClause.param, (ident, identifier) => {
3579
- if (this._strictInModuleOutput) {
3580
- this._checkStrictModeBinding(ident, identifier);
3581
- }
3582
- this.defineVariable(ident);
3583
- });
3584
- this.walkPattern(catchClause.param);
3585
- }
3586
- const prev = this.prevStatement;
3587
- this.blockPreWalkStatement(catchClause.body);
3588
- this.prevStatement = prev;
3589
- this.walkStatement(catchClause.body);
3590
- }, true);
3761
+ const oldScope = this._enterBlockScope();
3762
+ // Error binding is optional in catch clause since ECMAScript 2019
3763
+ if (catchClause.param !== null) {
3764
+ this.enterPattern(catchClause.param, (ident, identifier) => {
3765
+ if (this._strictInModuleOutput) {
3766
+ this._checkStrictModeBinding(ident, identifier);
3767
+ }
3768
+ this.defineVariable(ident);
3769
+ });
3770
+ this.walkPattern(catchClause.param);
3771
+ }
3772
+ const prev = this.prevStatement;
3773
+ this.blockPreWalkStatement(catchClause.body);
3774
+ this.prevStatement = prev;
3775
+ this.walkStatement(catchClause.body);
3776
+
3777
+ this._exitBlockScope(oldScope, true);
3591
3778
  }
3592
3779
 
3593
3780
  /**
@@ -3885,19 +4072,20 @@ class JavascriptParser extends Parser {
3885
4072
  this._checkStrictModeBinding(expression.id.name, expression.id);
3886
4073
  }
3887
4074
  }
3888
- this.inFunctionScope(true, scopeParams, () => {
3889
- for (const param of expression.params) {
3890
- this.walkPattern(param);
3891
- }
4075
+ const oldScope = this._enterFunctionScope(true, scopeParams);
4076
+ for (const param of expression.params) {
4077
+ this.walkPattern(param);
4078
+ }
3892
4079
 
3893
- this.detectMode(expression.body.body);
4080
+ this.detectMode(expression.body.body);
3894
4081
 
3895
- const prev = this.prevStatement;
4082
+ const prev = this.prevStatement;
3896
4083
 
3897
- this.preWalkStatement(expression.body);
3898
- this.prevStatement = prev;
3899
- this.walkStatement(expression.body);
3900
- });
4084
+ this.preWalkStatement(expression.body);
4085
+ this.prevStatement = prev;
4086
+ this.walkStatement(expression.body);
4087
+
4088
+ this.scope = oldScope;
3901
4089
  this.scope.topLevelScope = wasTopLevel;
3902
4090
  }
3903
4091
 
@@ -3911,20 +4099,21 @@ class JavascriptParser extends Parser {
3911
4099
  if (this._strictInModuleOutput) {
3912
4100
  this._checkStrictModeParams(expression.params);
3913
4101
  }
3914
- this.inFunctionScope(false, expression.params, () => {
3915
- for (const param of expression.params) {
3916
- this.walkPattern(param);
3917
- }
3918
- if (expression.body.type === "BlockStatement") {
3919
- this.detectMode(expression.body.body);
3920
- const prev = this.prevStatement;
3921
- this.preWalkStatement(expression.body);
3922
- this.prevStatement = prev;
3923
- this.walkStatement(expression.body);
3924
- } else {
3925
- this.walkExpression(expression.body);
3926
- }
3927
- });
4102
+ const oldScope = this._enterFunctionScope(false, expression.params);
4103
+ for (const param of expression.params) {
4104
+ this.walkPattern(param);
4105
+ }
4106
+ if (expression.body.type === "BlockStatement") {
4107
+ this.detectMode(expression.body.body);
4108
+ const prev = this.prevStatement;
4109
+ this.preWalkStatement(expression.body);
4110
+ this.prevStatement = prev;
4111
+ this.walkStatement(expression.body);
4112
+ } else {
4113
+ this.walkExpression(expression.body);
4114
+ }
4115
+
4116
+ this.scope = oldScope;
3928
4117
  this.scope.topLevelScope = wasTopLevel;
3929
4118
  }
3930
4119
 
@@ -4185,7 +4374,7 @@ class JavascriptParser extends Parser {
4185
4374
  const renameIdentifier = this.getRenameIdentifier(expression.right);
4186
4375
  if (
4187
4376
  renameIdentifier &&
4188
- this.callHooksForInfo(
4377
+ this._callHooksForInfo1(
4189
4378
  this.hooks.canRename,
4190
4379
  renameIdentifier,
4191
4380
  expression.right
@@ -4193,7 +4382,7 @@ class JavascriptParser extends Parser {
4193
4382
  ) {
4194
4383
  // renaming "a = b;"
4195
4384
  if (
4196
- !this.callHooksForInfo(
4385
+ !this._callHooksForInfo1(
4197
4386
  this.hooks.rename,
4198
4387
  renameIdentifier,
4199
4388
  expression.right
@@ -4209,34 +4398,42 @@ class JavascriptParser extends Parser {
4209
4398
  return;
4210
4399
  }
4211
4400
  this.walkExpression(expression.right);
4212
- this.enterPattern(expression.left, (name, _decl) => {
4213
- if (!this.callHooksForName(this.hooks.assign, name, expression)) {
4214
- this.walkExpression(
4215
- /** @type {MemberExpression} */
4216
- (expression.left)
4217
- );
4218
- }
4219
- });
4401
+ // inlined enterPattern → enterIdentifier for the Identifier left side:
4402
+ // same hook sequence without the per-assignment closure
4403
+ const leftName = expression.left.name;
4404
+ if (
4405
+ !this._callHooksForName1(
4406
+ this.hooks.pattern,
4407
+ leftName,
4408
+ expression.left
4409
+ ) &&
4410
+ !this._callHooksForName1(this.hooks.assign, leftName, expression)
4411
+ ) {
4412
+ this.walkExpression(expression.left);
4413
+ }
4220
4414
  } else if (expression.left.type.endsWith("Pattern")) {
4221
4415
  this.walkExpression(expression.right);
4222
4416
  this.enterPattern(expression.left, (name, _decl) => {
4223
- if (!this.callHooksForName(this.hooks.assign, name, expression)) {
4417
+ if (!this._callHooksForName1(this.hooks.assign, name, expression)) {
4224
4418
  this.defineVariable(name);
4225
4419
  }
4226
4420
  });
4227
4421
  this.walkPattern(expression.left);
4228
4422
  } else if (expression.left.type === "MemberExpression") {
4229
- const exprName = this.getMemberExpressionInfo(
4230
- expression.left,
4231
- ALLOWED_MEMBER_TYPES_EXPRESSION
4232
- );
4233
4423
  if (
4234
- exprName &&
4424
+ this._resolveMemberExpression(
4425
+ expression.left,
4426
+ ALLOWED_MEMBER_TYPES_EXPRESSION
4427
+ ) === 1 &&
4428
+ this._hasHooksForInfo(
4429
+ this.hooks.assignMemberChain,
4430
+ /** @type {string | VariableInfo} */ (this._memberRootInfo)
4431
+ ) &&
4235
4432
  this.callHooksForInfo(
4236
4433
  this.hooks.assignMemberChain,
4237
- exprName.rootInfo,
4434
+ /** @type {string | VariableInfo} */ (this._memberRootInfo),
4238
4435
  expression,
4239
- exprName.getMembers()
4436
+ this._members
4240
4437
  )
4241
4438
  ) {
4242
4439
  return;
@@ -4261,15 +4458,24 @@ class JavascriptParser extends Parser {
4261
4458
  if (result === undefined) {
4262
4459
  const guard = this.hooks.collectGuards.call(expression.test);
4263
4460
  this.walkExpression(expression.test);
4264
- this.walkGuardedBranch(guard ? guard.consequent : undefined, () =>
4265
- this.walkExpression(expression.consequent)
4266
- );
4461
+ // allocate the branch closure only when a guard frame needs pushing
4462
+ if (guard && guard.consequent) {
4463
+ this.walkGuardedBranch(guard.consequent, () =>
4464
+ this.walkExpression(expression.consequent)
4465
+ );
4466
+ } else {
4467
+ this.walkExpression(expression.consequent);
4468
+ }
4267
4469
 
4268
4470
  if (expression.alternate) {
4269
4471
  const alternate = expression.alternate;
4270
- this.walkGuardedBranch(guard ? guard.alternate : undefined, () =>
4271
- this.walkExpression(alternate)
4272
- );
4472
+ if (guard && guard.alternate) {
4473
+ this.walkGuardedBranch(guard.alternate, () =>
4474
+ this.walkExpression(alternate)
4475
+ );
4476
+ } else {
4477
+ this.walkExpression(alternate);
4478
+ }
4273
4479
  }
4274
4480
  } else if (result) {
4275
4481
  this.walkExpression(expression.consequent);
@@ -4375,13 +4581,13 @@ class JavascriptParser extends Parser {
4375
4581
  const renameIdentifier = this.getRenameIdentifier(argOrThis);
4376
4582
  if (
4377
4583
  renameIdentifier &&
4378
- this.callHooksForInfo(
4584
+ this._callHooksForInfo1(
4379
4585
  this.hooks.canRename,
4380
4586
  renameIdentifier,
4381
4587
  /** @type {Expression} */
4382
4588
  (argOrThis)
4383
4589
  ) &&
4384
- !this.callHooksForInfo(
4590
+ !this._callHooksForInfo1(
4385
4591
  this.hooks.rename,
4386
4592
  renameIdentifier,
4387
4593
  /** @type {Expression} */
@@ -4422,26 +4628,27 @@ class JavascriptParser extends Parser {
4422
4628
  scopeParams.push(functionExpression.id.name);
4423
4629
  }
4424
4630
 
4425
- this.inFunctionScope(true, scopeParams, () => {
4426
- if (renameThis && !arrow) {
4427
- this.setVariable("this", renameThis);
4428
- }
4429
- for (let i = 0; i < varInfoForArgs.length; i++) {
4430
- const varInfo = varInfoForArgs[i];
4431
- if (!varInfo) continue;
4432
- if (!params[i] || params[i].type !== "Identifier") continue;
4433
- this.setVariable(/** @type {Identifier} */ (params[i]).name, varInfo);
4434
- }
4435
- if (functionExpression.body.type === "BlockStatement") {
4436
- this.detectMode(functionExpression.body.body);
4437
- const prev = this.prevStatement;
4438
- this.preWalkStatement(functionExpression.body);
4439
- this.prevStatement = prev;
4440
- this.walkStatement(functionExpression.body);
4441
- } else {
4442
- this.walkExpression(functionExpression.body);
4443
- }
4444
- });
4631
+ const oldScope = this._enterFunctionScope(true, scopeParams);
4632
+ if (renameThis && !arrow) {
4633
+ this.setVariable("this", renameThis);
4634
+ }
4635
+ for (let i = 0; i < varInfoForArgs.length; i++) {
4636
+ const varInfo = varInfoForArgs[i];
4637
+ if (!varInfo) continue;
4638
+ if (!params[i] || params[i].type !== "Identifier") continue;
4639
+ this.setVariable(/** @type {Identifier} */ (params[i]).name, varInfo);
4640
+ }
4641
+ if (functionExpression.body.type === "BlockStatement") {
4642
+ this.detectMode(functionExpression.body.body);
4643
+ const prev = this.prevStatement;
4644
+ this.preWalkStatement(functionExpression.body);
4645
+ this.prevStatement = prev;
4646
+ this.walkStatement(functionExpression.body);
4647
+ } else {
4648
+ this.walkExpression(functionExpression.body);
4649
+ }
4650
+
4651
+ this.scope = oldScope;
4445
4652
  this.scope.topLevelScope = wasTopLevel;
4446
4653
  }
4447
4654
 
@@ -4553,22 +4760,26 @@ class JavascriptParser extends Parser {
4553
4760
  }
4554
4761
  const callee = this.evaluateExpression(expression.callee);
4555
4762
  if (callee.isIdentifier()) {
4556
- const members =
4557
- /** @type {NonNullable<BasicEvaluatedExpression["getMembers"]>} */
4558
- (callee.getMembers)();
4559
- const result1 = this.callHooksForInfo(
4560
- this.hooks.callMemberChain,
4763
+ const rootInfo =
4561
4764
  /** @type {NonNullable<BasicEvaluatedExpression["rootInfo"]>} */
4562
- (callee.rootInfo),
4563
- expression,
4564
- members,
4565
- callee.getMembersOptionals
4566
- ? callee.getMembersOptionals()
4567
- : members.map(RETURN_FALSE),
4568
- callee.getMemberRanges ? callee.getMemberRanges() : []
4569
- );
4570
- if (result1 === true) return;
4571
- const result2 = this.callHooksForInfo(
4765
+ (callee.rootInfo);
4766
+ if (this._hasHooksForInfo(this.hooks.callMemberChain, rootInfo)) {
4767
+ const members =
4768
+ /** @type {NonNullable<BasicEvaluatedExpression["getMembers"]>} */
4769
+ (callee.getMembers)();
4770
+ const result1 = this.callHooksForInfo(
4771
+ this.hooks.callMemberChain,
4772
+ rootInfo,
4773
+ expression,
4774
+ members,
4775
+ callee.getMembersOptionals
4776
+ ? callee.getMembersOptionals()
4777
+ : members.map(RETURN_FALSE),
4778
+ callee.getMemberRanges ? callee.getMemberRanges() : []
4779
+ );
4780
+ if (result1 === true) return;
4781
+ }
4782
+ const result2 = this._callHooksForInfo1(
4572
4783
  this.hooks.call,
4573
4784
  /** @type {NonNullable<BasicEvaluatedExpression["identifier"]>} */
4574
4785
  (callee.identifier),
@@ -4597,68 +4808,112 @@ class JavascriptParser extends Parser {
4597
4808
  * @param {MemberExpression} expression member expression
4598
4809
  */
4599
4810
  walkMemberExpression(expression) {
4600
- const exprInfo = this.getMemberExpressionInfo(
4811
+ const type = this._resolveMemberExpression(
4601
4812
  expression,
4602
4813
  ALLOWED_MEMBER_TYPES_ALL
4603
4814
  );
4604
- if (exprInfo) {
4605
- switch (exprInfo.type) {
4606
- case "expression": {
4607
- const result1 = this.callHooksForInfo(
4608
- this.hooks.expression,
4609
- exprInfo.name,
4610
- expression
4611
- );
4612
- if (result1 === true) return;
4613
- const members = exprInfo.getMembers();
4614
- const membersOptionals = exprInfo.getMembersOptionals();
4615
- const memberRanges = exprInfo.getMemberRanges();
4616
- const result2 = this.callHooksForInfo(
4617
- this.hooks.expressionMemberChain,
4618
- exprInfo.rootInfo,
4619
- expression,
4620
- members,
4621
- membersOptionals,
4622
- memberRanges
4623
- );
4624
- if (result2 === true) return;
4625
- this.walkMemberExpressionWithExpressionName(
4626
- expression,
4627
- exprInfo.name,
4628
- exprInfo.rootInfo,
4629
- [...members],
4630
- () =>
4631
- this.callHooksForInfo(
4632
- this.hooks.unhandledExpressionMemberChain,
4633
- exprInfo.rootInfo,
4634
- expression,
4635
- members
4636
- )
4637
- );
4638
- return;
4639
- }
4640
- case "call": {
4641
- const result = this.callHooksForInfo(
4642
- this.hooks.memberChainOfCallMemberChain,
4643
- exprInfo.rootInfo,
4644
- expression,
4645
- exprInfo.getCalleeMembers(),
4646
- exprInfo.call,
4647
- exprInfo.getMembers(),
4648
- exprInfo.getMemberRanges()
4649
- );
4650
- if (result === true) return;
4651
- // Fast skip over the member chain as we already called memberChainOfCallMemberChain
4652
- // and call computed property are literals anyway
4653
- this.walkExpression(exprInfo.call);
4654
- return;
4655
- }
4815
+ if (type === 1) {
4816
+ // copied out first: a hook may resolve another chain and reuse the slots
4817
+ const name = this._memberName;
4818
+ const rootInfo = /** @type {string | VariableInfo} */ (
4819
+ this._memberRootInfo
4820
+ );
4821
+ const members = this._members;
4822
+ const membersOptionals = this._membersOptionals;
4823
+ const result1 = this._callHooksForInfo1(
4824
+ this.hooks.expression,
4825
+ name,
4826
+ expression
4827
+ );
4828
+ if (result1 === true) return;
4829
+ if (this._hasHooksForInfo(this.hooks.expressionMemberChain, rootInfo)) {
4830
+ const result2 = this.callHooksForInfo(
4831
+ this.hooks.expressionMemberChain,
4832
+ rootInfo,
4833
+ expression,
4834
+ members,
4835
+ membersOptionals,
4836
+ memberRangesOf(expression, members.length)
4837
+ );
4838
+ if (result2 === true) return;
4656
4839
  }
4840
+ this._walkMemberChain(
4841
+ expression,
4842
+ expression,
4843
+ name,
4844
+ rootInfo,
4845
+ members,
4846
+ members.length
4847
+ );
4848
+ return;
4849
+ }
4850
+ if (type === 2) {
4851
+ const call = /** @type {CallExpression} */ (this._memberCall);
4852
+ const result = this.callHooksForInfo(
4853
+ this.hooks.memberChainOfCallMemberChain,
4854
+ /** @type {string | VariableInfo} */ (this._memberRootInfo),
4855
+ expression,
4856
+ this._memberCalleeMembers,
4857
+ call,
4858
+ this._members,
4859
+ memberRangesOf(expression, this._members.length)
4860
+ );
4861
+ if (result === true) return;
4862
+ // Fast skip over the member chain as we already called memberChainOfCallMemberChain
4863
+ // and call computed property are literals anyway
4864
+ this.walkExpression(call);
4865
+ return;
4657
4866
  }
4658
4867
  this.walkExpression(expression.object);
4659
4868
  if (expression.computed === true) this.walkExpression(expression.property);
4660
4869
  }
4661
4870
 
4871
+ /**
4872
+ * The walk `walkMemberExpressionWithExpressionName` does over a resolved
4873
+ * chain, reading `members` up to `end` instead of popping a copy of it.
4874
+ * @param {MemberExpression} outer the whole chain, for the unhandled hook
4875
+ * @param {MemberExpression} expression the chain from here inwards
4876
+ * @param {string} name name of `expression`
4877
+ * @param {string | VariableInfo} rootInfo root info
4878
+ * @param {Members} members members of the whole chain, in source order
4879
+ * @param {number} end how many of `members` are inside `expression`
4880
+ * @returns {void}
4881
+ */
4882
+ _walkMemberChain(outer, expression, name, rootInfo, members, end) {
4883
+ if (expression.object.type === "MemberExpression") {
4884
+ const property = members[end - 1];
4885
+ name = name.slice(0, -property.length - 1);
4886
+ const result = this._callHooksForInfo1(
4887
+ this.hooks.expression,
4888
+ name,
4889
+ expression.object
4890
+ );
4891
+ if (result === true) return;
4892
+ this._walkMemberChain(
4893
+ outer,
4894
+ expression.object,
4895
+ name,
4896
+ rootInfo,
4897
+ members,
4898
+ end - 1
4899
+ );
4900
+ } else if (
4901
+ !this._hasHooksForInfo(
4902
+ this.hooks.unhandledExpressionMemberChain,
4903
+ rootInfo
4904
+ ) ||
4905
+ !this.callHooksForInfo(
4906
+ this.hooks.unhandledExpressionMemberChain,
4907
+ rootInfo,
4908
+ outer,
4909
+ members
4910
+ )
4911
+ ) {
4912
+ this.walkExpression(expression.object);
4913
+ }
4914
+ if (expression.computed === true) this.walkExpression(expression.property);
4915
+ }
4916
+
4662
4917
  /**
4663
4918
  * Walk member expression with expression name.
4664
4919
  * @template R
@@ -4684,7 +4939,7 @@ class JavascriptParser extends Parser {
4684
4939
  const property = members[members.length - 1];
4685
4940
  name = name.slice(0, -property.length - 1);
4686
4941
  members.pop();
4687
- const result = this.callHooksForInfo(
4942
+ const result = this._callHooksForInfo1(
4688
4943
  this.hooks.expression,
4689
4944
  name,
4690
4945
  expression.object
@@ -4708,7 +4963,7 @@ class JavascriptParser extends Parser {
4708
4963
  * @param {ThisExpression} expression this expression
4709
4964
  */
4710
4965
  walkThisExpression(expression) {
4711
- this.callHooksForName(this.hooks.expression, "this", expression);
4966
+ this._callHooksForName1(this.hooks.expression, "this", expression);
4712
4967
  }
4713
4968
 
4714
4969
  /**
@@ -4716,7 +4971,7 @@ class JavascriptParser extends Parser {
4716
4971
  * @param {Identifier} expression identifier
4717
4972
  */
4718
4973
  walkIdentifier(expression) {
4719
- this.callHooksForName(this.hooks.expression, expression.name, expression);
4974
+ this._callHooksForName1(this.hooks.expression, expression.name, expression);
4720
4975
  }
4721
4976
 
4722
4977
  /**
@@ -4882,6 +5137,27 @@ class JavascriptParser extends Parser {
4882
5137
  }
4883
5138
  }
4884
5139
 
5140
+ /**
5141
+ * Whether `_callHooksForInfo` would call any hook for this info, so a
5142
+ * caller can skip building the arguments for a name nothing taps.
5143
+ * @template T
5144
+ * @template R
5145
+ * @param {HookMap<SyncBailHook<T, R>>} hookMap hooks that would be called
5146
+ * @param {ExportedVariableInfo} info variable info
5147
+ * @returns {boolean} true when a hook is registered for the info
5148
+ */
5149
+ _hasHooksForInfo(hookMap, info) {
5150
+ if (typeof info === "string") return hookMap.get(info) !== undefined;
5151
+ if (!(info instanceof VariableInfo)) return false;
5152
+ let tagInfo = info.tagInfo;
5153
+ while (tagInfo !== undefined) {
5154
+ if (hookMap.get(tagInfo.tag) !== undefined) return true;
5155
+ tagInfo = tagInfo.next;
5156
+ }
5157
+ if (!info.isFree() && !info.isTagged()) return false;
5158
+ return hookMap.get(/** @type {string} */ (info.name)) !== undefined;
5159
+ }
5160
+
4885
5161
  /**
4886
5162
  * Call hooks for name with fallback.
4887
5163
  * @template T
@@ -4903,6 +5179,69 @@ class JavascriptParser extends Parser {
4903
5179
  );
4904
5180
  }
4905
5181
 
5182
+ /**
5183
+ * Single-argument twin of `callHooksForName` (must stay in sync with
5184
+ * `_callHooksForInfo`): every internal dispatch passes exactly one hook
5185
+ * argument, so this skips the rest-array collection and spread call the
5186
+ * generic path pays per identifier.
5187
+ * @template T
5188
+ * @template R
5189
+ * @param {HookMap<SyncBailHook<T, R>>} hookMap hooks that should be called
5190
+ * @param {string} name key in map
5191
+ * @param {AsArray<T>[0]} arg the single hook argument
5192
+ * @returns {R | undefined} result of hook
5193
+ */
5194
+ _callHooksForName1(hookMap, name, arg) {
5195
+ return this._callHooksForInfo1(hookMap, this.getVariableInfo(name), arg);
5196
+ }
5197
+
5198
+ /**
5199
+ * Single-argument twin of `callHooksForInfo` (must stay in sync with
5200
+ * `_callHooksForInfo`), for the hot dispatch sites that pass one argument
5201
+ * and no fallback/defined callbacks.
5202
+ * @template T
5203
+ * @template R
5204
+ * @param {HookMap<SyncBailHook<T, R>>} hookMap hooks that should be called
5205
+ * @param {ExportedVariableInfo} info variable info
5206
+ * @param {AsArray<T>[0]} arg the single hook argument
5207
+ * @returns {R | undefined} result of hook
5208
+ */
5209
+ _callHooksForInfo1(hookMap, info, arg) {
5210
+ /** @type {string} */
5211
+ let name;
5212
+ if (typeof info === "string") {
5213
+ name = info;
5214
+ } else {
5215
+ if (!(info instanceof VariableInfo)) {
5216
+ return;
5217
+ }
5218
+ let tagInfo = info.tagInfo;
5219
+ while (tagInfo !== undefined) {
5220
+ const hook =
5221
+ /** @type {SyncBailHook<[AsArray<T>[0]], R> | undefined} */
5222
+ (hookMap.get(tagInfo.tag));
5223
+ if (hook !== undefined) {
5224
+ this.currentTagData = tagInfo.data;
5225
+ const result = hook.call(arg);
5226
+ this.currentTagData = undefined;
5227
+ if (result !== undefined) return result;
5228
+ }
5229
+ tagInfo = tagInfo.next;
5230
+ }
5231
+ if (!info.isFree() && !info.isTagged()) {
5232
+ return;
5233
+ }
5234
+ name = /** @type {string} */ (info.name);
5235
+ }
5236
+ const hook =
5237
+ /** @type {SyncBailHook<[AsArray<T>[0]], R> | undefined} */
5238
+ (hookMap.get(name));
5239
+ if (hook !== undefined) {
5240
+ const result = hook.call(arg);
5241
+ if (result !== undefined) return result;
5242
+ }
5243
+ }
5244
+
4906
5245
  /**
4907
5246
  * Processes the provided param.
4908
5247
  * @deprecated
@@ -4971,26 +5310,8 @@ class JavascriptParser extends Parser {
4971
5310
  * @returns {void}
4972
5311
  */
4973
5312
  inFunctionScope(hasThis, params, fn) {
4974
- const oldScope = this.scope;
4975
- this.scope = {
4976
- topLevelScope: oldScope.topLevelScope,
4977
- inTry: false,
4978
- inShorthand: false,
4979
- inTaggedTemplateTag: false,
4980
- isStrict: oldScope.isStrict,
4981
- isAsmJs: oldScope.isAsmJs,
4982
- terminated: undefined,
4983
- definitions: oldScope.definitions.createChild()
4984
- };
4985
-
4986
- if (hasThis) {
4987
- this.undefineVariable("this");
4988
- }
4989
-
4990
- this.enterPatterns(params, this._defineVariable);
4991
-
5313
+ const oldScope = this._enterFunctionScope(hasThis, params);
4992
5314
  fn();
4993
-
4994
5315
  this.scope = oldScope;
4995
5316
  }
4996
5317
 
@@ -5001,6 +5322,18 @@ class JavascriptParser extends Parser {
5001
5322
  * @returns {void}
5002
5323
  */
5003
5324
  inBlockScope(fn, inExecutedPath = false) {
5325
+ const oldScope = this._enterBlockScope();
5326
+ fn();
5327
+ this._exitBlockScope(oldScope, inExecutedPath);
5328
+ }
5329
+
5330
+ /**
5331
+ * Enters a fresh block scope: the closure-free core of `inBlockScope`, used
5332
+ * directly by the internal walkers so each block does not allocate a
5333
+ * closure and context. Pass the returned scope to `_exitBlockScope`.
5334
+ * @returns {ScopeInfo} the previous scope
5335
+ */
5336
+ _enterBlockScope() {
5004
5337
  const oldScope = this.scope;
5005
5338
  this.scope = {
5006
5339
  topLevelScope: oldScope.topLevelScope,
@@ -5012,18 +5345,50 @@ class JavascriptParser extends Parser {
5012
5345
  terminated: oldScope.terminated,
5013
5346
  definitions: oldScope.definitions.createChild()
5014
5347
  };
5348
+ return oldScope;
5349
+ }
5015
5350
 
5016
- fn();
5017
-
5351
+ /**
5352
+ * Leaves a block scope entered with `_enterBlockScope`.
5353
+ * @param {ScopeInfo} oldScope scope returned by `_enterBlockScope`
5354
+ * @param {boolean} inExecutedPath whether termination propagates to the outer scope
5355
+ * @returns {void}
5356
+ */
5357
+ _exitBlockScope(oldScope, inExecutedPath) {
5018
5358
  const terminated = this.scope.terminated;
5019
-
5020
5359
  if (inExecutedPath && terminated) {
5021
5360
  oldScope.terminated = terminated;
5022
5361
  }
5023
-
5024
5362
  this.scope = oldScope;
5025
5363
  }
5026
5364
 
5365
+ /**
5366
+ * Enters a fresh function scope: the closure-free core of `inFunctionScope`,
5367
+ * used directly by the internal walkers. Restore `this.scope` to the
5368
+ * returned scope when leaving.
5369
+ * @param {boolean} hasThis true, when this is defined
5370
+ * @param {(Pattern | string)[]} params scope params
5371
+ * @returns {ScopeInfo} the previous scope
5372
+ */
5373
+ _enterFunctionScope(hasThis, params) {
5374
+ const oldScope = this.scope;
5375
+ this.scope = {
5376
+ topLevelScope: oldScope.topLevelScope,
5377
+ inTry: false,
5378
+ inShorthand: false,
5379
+ inTaggedTemplateTag: false,
5380
+ isStrict: oldScope.isStrict,
5381
+ isAsmJs: oldScope.isAsmJs,
5382
+ terminated: undefined,
5383
+ definitions: oldScope.definitions.createChild()
5384
+ };
5385
+ if (hasThis) {
5386
+ this.undefineVariable("this");
5387
+ }
5388
+ this.enterPatterns(params, this._defineVariable);
5389
+ return oldScope;
5390
+ }
5391
+
5027
5392
  /**
5028
5393
  * Processes the provided statement.
5029
5394
  * @param {(Directive | Statement | ModuleDeclaration)[]} statements statements
@@ -5103,7 +5468,7 @@ class JavascriptParser extends Parser {
5103
5468
  * @param {OnIdent} onIdent callback
5104
5469
  */
5105
5470
  enterIdentifier(pattern, onIdent) {
5106
- if (!this.callHooksForName(this.hooks.pattern, pattern.name, pattern)) {
5471
+ if (!this._callHooksForName1(this.hooks.pattern, pattern.name, pattern)) {
5107
5472
  onIdent(pattern.name, pattern);
5108
5473
  }
5109
5474
  }
@@ -5408,6 +5773,7 @@ class JavascriptParser extends Parser {
5408
5773
  this.destructuringAssignmentProperties = undefined;
5409
5774
  }
5410
5775
  this.hooks.finish.call(ast, comments);
5776
+ this._releaseMemberExpression();
5411
5777
  this.scope = oldScope;
5412
5778
  this.state = oldState;
5413
5779
  this.comments = oldComments;
@@ -5452,6 +5818,9 @@ class JavascriptParser extends Parser {
5452
5818
  `(${source})`,
5453
5819
  {
5454
5820
  sourceType: this.sourceType,
5821
+ // ranges alone take the lazy path, which serves `range` from a
5822
+ // getter and tracks no locations — as the module parse does
5823
+ ranges: true,
5455
5824
  importPhases
5456
5825
  },
5457
5826
  this.options.parse
@@ -5753,7 +6122,15 @@ class JavascriptParser extends Parser {
5753
6122
  };
5754
6123
  }
5755
6124
  }
5756
- return /** @type {DependencyLocation} */ (node.loc);
6125
+ const loc = node.loc;
6126
+ if (!loc) {
6127
+ return /** @type {DependencyLocation} */ (/** @type {unknown} */ (loc));
6128
+ }
6129
+ // a plain copy: a foreign parser's location class must not reach the cache
6130
+ return {
6131
+ start: { line: loc.start.line, column: loc.start.column },
6132
+ end: { line: loc.end.line, column: loc.end.column }
6133
+ };
5757
6134
  }
5758
6135
 
5759
6136
  /**
@@ -5995,6 +6372,7 @@ class JavascriptParser extends Parser {
5995
6372
  });
5996
6373
  }
5997
6374
  this.scope.definitions.set(name, newInfo);
6375
+ this._memberExpression = undefined;
5998
6376
  }
5999
6377
 
6000
6378
  /**
@@ -6011,6 +6389,7 @@ class JavascriptParser extends Parser {
6011
6389
  return;
6012
6390
  }
6013
6391
  this.scope.definitions.set(name, this.scope);
6392
+ this._memberExpression = undefined;
6014
6393
  }
6015
6394
 
6016
6395
  /**
@@ -6019,6 +6398,7 @@ class JavascriptParser extends Parser {
6019
6398
  */
6020
6399
  undefineVariable(name) {
6021
6400
  this.scope.definitions.delete(name);
6401
+ this._memberExpression = undefined;
6022
6402
  }
6023
6403
 
6024
6404
  /**
@@ -6071,6 +6451,7 @@ class JavascriptParser extends Parser {
6071
6451
  * @returns {void}
6072
6452
  */
6073
6453
  setVariable(name, variableInfo) {
6454
+ this._memberExpression = undefined;
6074
6455
  if (typeof variableInfo === "string") {
6075
6456
  if (variableInfo === name) {
6076
6457
  this.scope.definitions.delete(name);
@@ -6242,69 +6623,219 @@ class JavascriptParser extends Parser {
6242
6623
  /** @typedef {{ type: "expression", rootInfo: string | VariableInfo, name: string, getMembers: () => Members, getMembersOptionals: () => MembersOptionals, getMemberRanges: () => MemberRanges }} ExpressionExpressionInfo */
6243
6624
 
6244
6625
  /**
6245
- * Gets member expression info.
6626
+ * Resolves a member expression into the `_member*` slots, once per node and
6627
+ * scope. Returns 0 when rejected, 1 for an `expression`, 2 for a `call`.
6246
6628
  * @param {Expression | Super} expression a member expression
6247
- * @param {number} allowedTypes which types should be returned, presented in bit mask
6248
- * @returns {CallExpressionInfo | ExpressionExpressionInfo | undefined} expression info
6629
+ * @param {number} allowedTypes which types may be returned, presented in bit mask
6630
+ * @returns {0 | 1 | 2} the kind resolved
6249
6631
  */
6250
- getMemberExpressionInfo(expression, allowedTypes) {
6632
+ _resolveMemberExpression(expression, allowedTypes) {
6633
+ if (
6634
+ expression === this._memberExpression &&
6635
+ this._memberScope === this.scope
6636
+ ) {
6637
+ const type = this._memberType;
6638
+ if (type !== 0) {
6639
+ // the root kind is a fact of the node; the mask only says whether to report it
6640
+ const bit =
6641
+ type === 2
6642
+ ? ALLOWED_MEMBER_TYPES_CALL_EXPRESSION
6643
+ : ALLOWED_MEMBER_TYPES_EXPRESSION;
6644
+ return (allowedTypes & bit) === 0 ? 0 : type;
6645
+ }
6646
+ if ((allowedTypes & ~this._memberMask) === 0) return 0;
6647
+ }
6648
+ this._memberExpression = expression;
6649
+ this._memberScope = this.scope;
6650
+ this._memberMask = allowedTypes;
6651
+ this._memberType = 0;
6652
+ this._memberInfo = undefined;
6251
6653
  // Resolve the root first (no allocation); most calls reject here and so
6252
- // never build the member arrays via extractMemberExpressionChain.
6654
+ // never build the member arrays
6253
6655
  const object = this.getMemberExpressionRoot(expression);
6254
6656
  switch (object.type) {
6255
6657
  case "CallExpression": {
6256
- if ((allowedTypes & ALLOWED_MEMBER_TYPES_CALL_EXPRESSION) === 0) return;
6658
+ if ((allowedTypes & ALLOWED_MEMBER_TYPES_CALL_EXPRESSION) === 0) {
6659
+ return 0;
6660
+ }
6257
6661
  const calleeExpr = object.callee;
6258
6662
  const callee =
6259
6663
  calleeExpr.type === "MemberExpression"
6260
6664
  ? this.getMemberExpressionRoot(calleeExpr)
6261
6665
  : calleeExpr;
6262
6666
  const rootName = getRootName(callee);
6263
- if (!rootName) return;
6264
- const result = this.getNameInfoFromVariable(rootName);
6265
- if (!result) return;
6266
- const { info: rootInfo, name: resolvedRoot } = result;
6267
- const rootMembers =
6268
- calleeExpr.type === "MemberExpression"
6269
- ? this.extractMemberExpressionChain(calleeExpr).members
6270
- : EMPTY_ARRAY;
6271
- const { members, membersOptionals, memberRanges } =
6272
- this.extractMemberExpressionChain(expression);
6667
+ if (!rootName) return this._rejectMemberExpression();
6668
+ const resolvedRoot = this._resolveMemberRoot(rootName);
6669
+ if (resolvedRoot === undefined) return this._rejectMemberExpression();
6670
+ /** @type {CalleeMembers} */
6671
+ let rootMembers = EMPTY_ARRAY;
6672
+ if (calleeExpr.type === "MemberExpression") {
6673
+ this._extractMembers(calleeExpr);
6674
+ rootMembers = this._members;
6675
+ }
6676
+ this._extractMembers(expression);
6273
6677
  const calleeName = objectAndMembersToName(resolvedRoot, rootMembers);
6274
- return {
6275
- type: "call",
6276
- call: object,
6277
- calleeName,
6278
- rootInfo,
6279
- getCalleeMembers: lazyReverse(rootMembers),
6280
- name: objectAndMembersToName(`${calleeName}()`, members),
6281
- getMembers: lazyReverse(members),
6282
- getMembersOptionals: lazyReverse(membersOptionals),
6283
- getMemberRanges: lazyReverse(memberRanges)
6284
- };
6678
+ this._memberType = 2;
6679
+ this._memberCall = object;
6680
+ this._memberCalleeName = calleeName;
6681
+ this._memberCalleeMembers = rootMembers;
6682
+ this._memberName = objectAndMembersToName(
6683
+ `${calleeName}()`,
6684
+ this._members
6685
+ );
6686
+ return 2;
6285
6687
  }
6286
6688
  case "Identifier":
6287
6689
  case "MetaProperty":
6288
6690
  case "ThisExpression": {
6289
- if ((allowedTypes & ALLOWED_MEMBER_TYPES_EXPRESSION) === 0) return;
6691
+ if ((allowedTypes & ALLOWED_MEMBER_TYPES_EXPRESSION) === 0) return 0;
6290
6692
  const rootName = getRootName(object);
6291
- if (!rootName) return;
6693
+ if (!rootName) return this._rejectMemberExpression();
6694
+ const resolvedRoot = this._resolveMemberRoot(rootName);
6695
+ if (resolvedRoot === undefined) return this._rejectMemberExpression();
6696
+ this._extractMembers(expression);
6697
+ this._memberType = 1;
6698
+ this._memberName = objectAndMembersToName(resolvedRoot, this._members);
6699
+ return 1;
6700
+ }
6701
+ }
6702
+ return this._rejectMemberExpression();
6703
+ }
6292
6704
 
6293
- const result = this.getNameInfoFromVariable(rootName);
6294
- if (!result) return;
6295
- const { info: rootInfo, name: resolvedRoot } = result;
6296
- const { members, membersOptionals, memberRanges } =
6297
- this.extractMemberExpressionChain(expression);
6298
- return {
6299
- type: "expression",
6300
- name: objectAndMembersToName(resolvedRoot, members),
6301
- rootInfo,
6302
- getMembers: lazyReverse(members),
6303
- getMembersOptionals: lazyReverse(membersOptionals),
6304
- getMemberRanges: lazyReverse(memberRanges)
6305
- };
6705
+ /**
6706
+ * Records that the node resolves to nothing under any mask.
6707
+ * @returns {0} rejected
6708
+ */
6709
+ _rejectMemberExpression() {
6710
+ this._memberMask = ALLOWED_MEMBER_TYPES_ALL;
6711
+ return 0;
6712
+ }
6713
+
6714
+ /**
6715
+ * `getNameInfoFromVariable` for the resolver: stores the info in
6716
+ * `_memberRootInfo` and returns the name, or undefined when rejected.
6717
+ * @param {string} rootName name of the root variable
6718
+ * @returns {string | undefined} the resolved root name
6719
+ */
6720
+ _resolveMemberRoot(rootName) {
6721
+ const info = this.getVariableInfo(rootName);
6722
+ if (info instanceof VariableInfo && info.name) {
6723
+ if (!info.isFree() && !info.isTagged()) return;
6724
+ this._memberRootInfo = info;
6725
+ return info.name;
6726
+ }
6727
+ if (typeof info !== "string") return;
6728
+ this._memberRootInfo = info;
6729
+ return info;
6730
+ }
6731
+
6732
+ /**
6733
+ * Fills `_members` and `_membersOptionals` for the chain, in source order.
6734
+ * @param {Expression | Super} expression a member expression
6735
+ * @returns {void}
6736
+ */
6737
+ _extractMembers(expression) {
6738
+ /** @type {Node} */
6739
+ let expr = expression;
6740
+ /** @type {Members} */
6741
+ const members = [];
6742
+ /** @type {MembersOptionals} */
6743
+ const membersOptionals = [];
6744
+ while (expr.type === "MemberExpression") {
6745
+ if (expr.computed) {
6746
+ const prop = expr.property;
6747
+ if (prop.type === "Literal") {
6748
+ members.push(`${prop.value}`);
6749
+ } else if (
6750
+ prop.type === "TemplateLiteral" &&
6751
+ prop.expressions.length === 0 &&
6752
+ typeof prop.quasis[0].value.cooked === "string"
6753
+ ) {
6754
+ members.push(prop.quasis[0].value.cooked);
6755
+ } else {
6756
+ break;
6757
+ }
6758
+ } else {
6759
+ if (expr.property.type !== "Identifier") break;
6760
+ members.push(expr.property.name);
6306
6761
  }
6762
+ membersOptionals.push(expr.optional);
6763
+ expr = expr.object;
6764
+ }
6765
+ if (members.length > 1) {
6766
+ members.reverse();
6767
+ membersOptionals.reverse();
6768
+ }
6769
+ this._members = members;
6770
+ this._membersOptionals = membersOptionals;
6771
+ }
6772
+
6773
+ /**
6774
+ * Drops the resolved member expression, so no AST outlives its parse.
6775
+ * @returns {void}
6776
+ */
6777
+ _releaseMemberExpression() {
6778
+ this._memberExpression = undefined;
6779
+ this._memberScope = undefined;
6780
+ this._memberType = 0;
6781
+ this._memberRootInfo = undefined;
6782
+ this._members = EMPTY_ARRAY;
6783
+ this._membersOptionals = EMPTY_MEMBERS_OPTIONALS;
6784
+ this._memberCall = undefined;
6785
+ this._memberCalleeMembers = EMPTY_ARRAY;
6786
+ this._memberInfo = undefined;
6787
+ }
6788
+
6789
+ /**
6790
+ * Gets member expression info.
6791
+ * @param {Expression | Super} expression a member expression
6792
+ * @param {number} allowedTypes which types should be returned, presented in bit mask
6793
+ * @returns {CallExpressionInfo | ExpressionExpressionInfo | undefined} expression info
6794
+ */
6795
+ getMemberExpressionInfo(expression, allowedTypes) {
6796
+ const type = this._resolveMemberExpression(expression, allowedTypes);
6797
+ if (type === 0) return;
6798
+ const cached = this._memberInfo;
6799
+ if (cached !== undefined) return cached;
6800
+ const members = this._members;
6801
+ const membersOptionals = this._membersOptionals;
6802
+ /** @type {MemberRanges | undefined} */
6803
+ let memberRanges;
6804
+ const getMembers = () => members;
6805
+ const getMembersOptionals = () => membersOptionals;
6806
+ const getMemberRanges = () =>
6807
+ memberRanges ||
6808
+ (memberRanges = memberRangesOf(expression, members.length));
6809
+ const rootInfo = /** @type {string | VariableInfo} */ (
6810
+ this._memberRootInfo
6811
+ );
6812
+ /** @type {CallExpressionInfo | ExpressionExpressionInfo} */
6813
+ let info;
6814
+ if (type === 2) {
6815
+ const calleeMembers = this._memberCalleeMembers;
6816
+ info = {
6817
+ type: "call",
6818
+ call: /** @type {CallExpression} */ (this._memberCall),
6819
+ calleeName: this._memberCalleeName,
6820
+ rootInfo,
6821
+ getCalleeMembers: () => calleeMembers,
6822
+ name: this._memberName,
6823
+ getMembers,
6824
+ getMembersOptionals,
6825
+ getMemberRanges
6826
+ };
6827
+ } else {
6828
+ info = {
6829
+ type: "expression",
6830
+ name: this._memberName,
6831
+ rootInfo,
6832
+ getMembers,
6833
+ getMembersOptionals,
6834
+ getMemberRanges
6835
+ };
6307
6836
  }
6837
+ this._memberInfo = info;
6838
+ return info;
6308
6839
  }
6309
6840
 
6310
6841
  /**
@@ -6373,7 +6904,10 @@ class JavascriptParser extends Parser {
6373
6904
  // syntax so `auto` avoids a second full parse in the common case
6374
6905
  parserOptions.moduleFallback = type === "auto";
6375
6906
  }
6376
- const wantRanges = parserOptions.ranges === true;
6907
+ // `locations` are acorn's to track, so the lazy path serves the callers
6908
+ // that ask for ranges alone — which is every one inside webpack
6909
+ const wantLazyNodes =
6910
+ parserOptions.ranges === true && parserOptions.locations !== true;
6377
6911
  /**
6378
6912
  * Returns parse result.
6379
6913
  * @param {string} code source code
@@ -6388,34 +6922,27 @@ class JavascriptParser extends Parser {
6388
6922
  /** @type {Comment[]} */
6389
6923
  const comments = [];
6390
6924
 
6391
- // Whenever ranges are wanted (the main parse path and the concatenation
6392
- // re-parse), have the parser plugin serve `range` on demand instead of
6393
- // eagerly. `wantRanges` is hoisted since the auto-fallback re-enters
6394
- // with the same (mutated) options object.
6395
- options.lazyNodes = wantRanges;
6396
- if (wantRanges) {
6925
+ // Ranges alone (the main parse path and the concatenation re-parse) are
6926
+ // served on demand; hoisted, since the auto-fallback re-enters here.
6927
+ options.lazyNodes = wantLazyNodes;
6928
+ if (wantLazyNodes) {
6397
6929
  // the lazy plugin replaces acorn's native tracking — disabling it
6398
6930
  // here lets WebpackParser skip its defensive options copy
6399
- options.locations = false;
6400
6931
  options.ranges = false;
6401
6932
  }
6402
6933
 
6403
6934
  if (options.comments) {
6404
- if (wantRanges) {
6935
+ if (wantLazyNodes) {
6405
6936
  // the parser plugin collects the comments itself, deferring the
6406
6937
  // text slice until some hook actually reads `value`
6407
6938
  options.lazyComments = comments;
6408
6939
  } else {
6409
- /** @type {AcornOptions} */
6410
- (options).onComment =
6411
- /** @type {import("acorn").Comment[]} */
6412
- (/** @type {unknown} */ (comments));
6940
+ /** @type {ParserOptions} */
6941
+ (options).onComment = comments;
6413
6942
  }
6414
6943
  }
6415
6944
 
6416
- const ast =
6417
- /** @type {Program} */
6418
- (parser.parse(code, /** @type {AcornOptions} */ (options)));
6945
+ const ast = /** @type {Program} */ (parser.parse(code, options));
6419
6946
 
6420
6947
  return { ast, comments };
6421
6948
  };
@@ -6451,7 +6978,7 @@ class JavascriptParser extends Parser {
6451
6978
  // release per-parse state retained on the (possibly reused) options
6452
6979
  // object — the comments array retains source text slices
6453
6980
  parserOptions.lazyComments = undefined;
6454
- /** @type {AcornOptions} */
6981
+ /** @type {ParserOptions} */
6455
6982
  (parserOptions).onComment = undefined;
6456
6983
 
6457
6984
  if (threw) {
@@ -6463,7 +6990,7 @@ class JavascriptParser extends Parser {
6463
6990
 
6464
6991
  /**
6465
6992
  * Returns parser.
6466
- * @param {((BaseParser: AcornParser) => AcornParser)[]} plugins parser plugin
6993
+ * @param {ParserPlugin[]} plugins parser plugin
6467
6994
  * @returns {typeof JavascriptParser} parser
6468
6995
  */
6469
6996
  static extend(...plugins) {