@depup/webpack 5.110.3-depup.0 → 5.111.1-depup.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 (283) hide show
  1. package/README.md +7 -8
  2. package/changes.json +7 -11
  3. package/lib/APIPlugin.js +26 -11
  4. package/lib/AutomaticPrefetchPlugin.js +1 -1
  5. package/lib/Cache.js +8 -3
  6. package/lib/CacheFacade.js +1 -1
  7. package/lib/CircularModulesPlugin.js +157 -55
  8. package/lib/CleanPlugin.js +2 -2
  9. package/lib/CompatibilityPlugin.js +3 -4
  10. package/lib/Compilation.js +336 -337
  11. package/lib/Compiler.js +213 -51
  12. package/lib/ConstPlugin.js +16 -132
  13. package/lib/CopyPlugin.js +1054 -0
  14. package/lib/DefinePlugin.js +24 -8
  15. package/lib/DependencyTemplate.js +2 -1
  16. package/lib/DotenvPlugin.js +24 -28
  17. package/lib/ErrorHelpers.js +113 -0
  18. package/lib/ExportsInfo.js +20 -16
  19. package/lib/ExternalModule.js +47 -22
  20. package/lib/FileSystemInfo.js +17 -14
  21. package/lib/FlagDependencyExportsPlugin.js +53 -34
  22. package/lib/FlagDependencyUsagePlugin.js +9 -13
  23. package/lib/Generator.js +24 -2
  24. package/lib/HotModuleReplacementPlugin.js +55 -7
  25. package/lib/IgnorePlugin.js +1 -1
  26. package/lib/JavascriptMetaInfoPlugin.js +5 -3
  27. package/lib/LoaderOptionsPlugin.js +1 -1
  28. package/lib/MainTemplate.js +5 -1
  29. package/lib/Module.js +19 -10
  30. package/lib/ModuleGraph.js +6 -9
  31. package/lib/MultiCompiler.js +7 -17
  32. package/lib/MultiWatching.js +1 -1
  33. package/lib/NormalModule.js +135 -50
  34. package/lib/NormalModuleFactory.js +1 -1
  35. package/lib/RuntimeGlobals.js +21 -7
  36. package/lib/RuntimeModule.js +28 -12
  37. package/lib/RuntimePlugin.js +30 -3
  38. package/lib/RuntimeTemplate.js +813 -518
  39. package/lib/SourceMapDevToolPlugin.js +54 -104
  40. package/lib/TemplatedPathPlugin.js +16 -59
  41. package/lib/Watching.js +9 -0
  42. package/lib/WebpackOptionsApply.js +84 -91
  43. package/lib/asset/AssetBytesGenerator.js +7 -1
  44. package/lib/asset/AssetGenerator.js +50 -24
  45. package/lib/asset/AssetModule.js +3 -0
  46. package/lib/asset/AssetSourceGenerator.js +7 -1
  47. package/lib/asset/WebManifestParser.js +7 -5
  48. package/lib/buildChunkGraph.js +11 -15
  49. package/lib/cache/AddBuildDependenciesPlugin.js +33 -4
  50. package/lib/cache/IdleFileCachePlugin.js +2 -2
  51. package/lib/cache/MemoryCachePlugin.js +4 -4
  52. package/lib/cache/MemoryWithGcCachePlugin.js +7 -8
  53. package/lib/cache/PackFileCacheStrategy.js +17 -19
  54. package/lib/cache/getLazyHashedEtag.js +3 -5
  55. package/lib/config/browserslistTargetHandler.js +17 -4
  56. package/lib/config/defaults.js +138 -128
  57. package/lib/config/getClaimedAssetTypes.js +10 -11
  58. package/lib/config/normalization.js +82 -12
  59. package/lib/config/target.js +13 -4
  60. package/lib/container/ContainerReferencePlugin.js +6 -0
  61. package/lib/container/RemoteRuntimeModule.js +9 -0
  62. package/lib/container/declaredRemotes.js +39 -0
  63. package/lib/{ContextExclusionPlugin.js → context/ContextExclusionPlugin.js} +1 -1
  64. package/lib/{ContextModule.js → context/ContextModule.js} +54 -115
  65. package/lib/{ContextModuleFactory.js → context/ContextModuleFactory.js} +19 -21
  66. package/lib/{ContextReplacementPlugin.js → context/ContextReplacementPlugin.js} +4 -4
  67. package/lib/css/CssGenerator.js +27 -11
  68. package/lib/css/CssInjectStyleRuntimeModule.js +9 -0
  69. package/lib/css/CssLoadingRuntimeModule.js +94 -36
  70. package/lib/css/CssModule.js +1 -1
  71. package/lib/css/CssModulesPlugin.js +7 -6
  72. package/lib/css/CssParser.js +162 -31
  73. package/lib/css/CssServerStylesRuntimeModule.js +9 -0
  74. package/lib/css/cssMinify.js +43 -32
  75. package/lib/css/data.js +3514 -869
  76. package/lib/css/syntax.js +6370 -1220
  77. package/lib/debug/ProfilingPlugin.js +7 -4
  78. package/lib/dependencies/AMDDefineDependencyParserPlugin.js +6 -3
  79. package/lib/dependencies/AMDRuntimeModules.js +18 -0
  80. package/lib/dependencies/CommonJsExportRequireDependency.js +13 -12
  81. package/lib/dependencies/CommonJsExportsParserPlugin.js +12 -13
  82. package/lib/dependencies/CommonJsImportsParserPlugin.js +5 -5
  83. package/lib/dependencies/CommonJsPlugin.js +19 -3
  84. package/lib/dependencies/CommonJsRequireDependency.js +14 -5
  85. package/lib/dependencies/CommonJsSelfReferenceDependency.js +2 -4
  86. package/lib/dependencies/ContextDependency.js +1 -1
  87. package/lib/dependencies/ContextDependencyHelpers.js +3 -9
  88. package/lib/dependencies/ContextElementDependency.js +1 -1
  89. package/lib/dependencies/CssIcssExportDependency.js +13 -9
  90. package/lib/dependencies/CssIcssImportDependency.js +3 -6
  91. package/lib/dependencies/CssUrlDependency.js +3 -5
  92. package/lib/dependencies/HarmonyCompatibilityDependency.js +67 -22
  93. package/lib/dependencies/HarmonyDetectionParserPlugin.js +3 -4
  94. package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +10 -7
  95. package/lib/dependencies/HarmonyExportExpressionDependency.js +47 -33
  96. package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +74 -17
  97. package/lib/dependencies/HarmonyExportInitFragment.js +110 -23
  98. package/lib/dependencies/HarmonyExportSpecifierDependency.js +25 -58
  99. package/lib/dependencies/HarmonyImportBareSideEffectDependency.js +59 -0
  100. package/lib/dependencies/HarmonyImportDependency.js +6 -1
  101. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +111 -17
  102. package/lib/dependencies/HarmonyImportSideEffectDependency.js +3 -4
  103. package/lib/dependencies/HarmonyImportSpecifierDependency.js +40 -3
  104. package/lib/dependencies/HarmonyModulesPlugin.js +20 -0
  105. package/lib/dependencies/HtmlEntryDependency.js +124 -102
  106. package/lib/dependencies/HtmlInlineScriptDependency.js +3 -4
  107. package/lib/dependencies/HtmlInlineStyleDependency.js +3 -4
  108. package/lib/dependencies/ImportMetaContextDependencyParserPlugin.js +1 -1
  109. package/lib/dependencies/ImportMetaGlobHelpers.js +1 -1
  110. package/lib/dependencies/ImportMetaMainDependency.js +104 -0
  111. package/lib/dependencies/ImportMetaPlugin.js +67 -25
  112. package/lib/dependencies/ImportParserPlugin.js +1 -1
  113. package/lib/dependencies/LoaderPlugin.js +26 -1
  114. package/lib/dependencies/RequireContextDependencyParserPlugin.js +1 -1
  115. package/lib/dependencies/SystemRuntimeModule.js +9 -0
  116. package/lib/dependencies/URLContextDependency.js +1 -1
  117. package/lib/dependencies/URLDependency.js +6 -5
  118. package/lib/dependencies/WorkerAndWorkletPlugin.js +15 -21
  119. package/lib/dependencies/WorkerDependency.js +7 -7
  120. package/lib/dependencies/WorkletDependency.js +7 -11
  121. package/lib/dll/DelegatedModule.js +18 -1
  122. package/lib/dll/DllReferencePlugin.js +11 -8
  123. package/lib/dll/LibManifestPlugin.js +1 -1
  124. package/lib/errors/AnalyzableBailoutsWarning.js +41 -0
  125. package/lib/errors/DuplicateModulesWarning.js +12 -7
  126. package/lib/errors/EmptyCopyPatternWarning.js +26 -0
  127. package/lib/errors/ExternalStarReexportError.js +44 -0
  128. package/lib/errors/HookWebpackError.js +3 -2
  129. package/lib/errors/MissingSourceMapsWarning.js +36 -0
  130. package/lib/errors/ModuleParseError.js +2 -1
  131. package/lib/errors/UnusedAssetsWarning.js +36 -0
  132. package/lib/errors/UnusedFederationWarning.js +35 -0
  133. package/lib/errors/UnusedModulesWarning.js +38 -0
  134. package/lib/esm/ExportWebpackRequireRuntimeModule.js +9 -0
  135. package/lib/esm/ModuleChunkFormatPlugin.js +28 -3
  136. package/lib/esm/ModuleChunkLoadingPlugin.js +30 -11
  137. package/lib/esm/ModuleChunkLoadingRuntimeModule.js +92 -60
  138. package/lib/hmr/HotModuleReplacement.runtime.js +8 -2
  139. package/lib/hmr/HotModuleReplacementRuntimeModule.js +9 -0
  140. package/lib/hmr/LazyCompilationPlugin.js +3 -5
  141. package/lib/html/HtmlGenerator.js +72 -87
  142. package/lib/html/HtmlModulesPlugin.js +418 -200
  143. package/lib/html/HtmlParser.js +110 -147
  144. package/lib/html/builtinEmbeddedRenderer.js +107 -0
  145. package/lib/html/data.js +142 -7
  146. package/lib/html/htmlMinify.js +92 -122
  147. package/lib/html/syntax.js +1618 -1391
  148. package/lib/ids/HashedModuleIdsPlugin.js +2 -1
  149. package/lib/index.js +49 -3
  150. package/lib/javascript/ChunkFormatHelpers.js +4 -3
  151. package/lib/javascript/JavascriptGenerator.js +8 -1
  152. package/lib/javascript/JavascriptModule.js +1 -0
  153. package/lib/javascript/JavascriptModulesPlugin.js +73 -72
  154. package/lib/javascript/JavascriptParser.js +1970 -900
  155. package/lib/javascript/StartupHelpers.js +27 -3
  156. package/lib/javascript/data.js +172 -0
  157. package/lib/javascript/grammar.js +5546 -0
  158. package/lib/javascript/parser.js +1500 -0
  159. package/lib/javascript/regexp.js +1762 -0
  160. package/lib/javascript/regexpData.js +51 -0
  161. package/lib/javascript/syntax.js +9061 -5262
  162. package/lib/json/JsonGenerator.js +7 -1
  163. package/lib/library/ModuleLibraryPlugin.js +171 -132
  164. package/lib/loaders/LoaderRunner.js +137 -9
  165. package/lib/node/NodeWatchFileSystem.js +3 -9
  166. package/lib/node/ReadFileChunkLoadingRuntimeModule.js +22 -0
  167. package/lib/node/RequireChunkLoadingRuntimeModule.js +22 -0
  168. package/lib/optimize/AggressiveMergingPlugin.js +3 -5
  169. package/lib/optimize/ConcatenatedModule.js +178 -120
  170. package/lib/optimize/ConstExportsPlugin.js +50 -39
  171. package/lib/optimize/FlagIncludedChunksPlugin.js +3 -11
  172. package/lib/optimize/InlineExports.js +20 -123
  173. package/lib/optimize/InnerGraphPlugin.js +7 -13
  174. package/lib/optimize/LimitChunkCountPlugin.js +5 -8
  175. package/lib/optimize/MangleExportsPlugin.js +13 -4
  176. package/lib/optimize/ModuleConcatenationPlugin.js +27 -5
  177. package/lib/optimize/RealContentHashPlugin.js +68 -9
  178. package/lib/optimize/SideEffectsFlagPlugin.js +24 -28
  179. package/lib/optimize/SplitChunksPlugin.js +2 -4
  180. package/lib/performance/AnalyzableBailoutsPlugin.js +115 -0
  181. package/lib/performance/BroadContextsPlugin.js +1 -1
  182. package/lib/performance/CacheEffectivenessPlugin.js +7 -7
  183. package/lib/performance/DuplicateModulesPlugin.js +57 -13
  184. package/lib/performance/EvalUsagePlugin.js +5 -2
  185. package/lib/performance/MissingSourceMapsPlugin.js +115 -0
  186. package/lib/performance/PureAnnotationsPlugin.js +6 -3
  187. package/lib/performance/SizeLimitsPlugin.js +2 -4
  188. package/lib/performance/TopLevelThisPlugin.js +5 -4
  189. package/lib/performance/UnusedAssetsPlugin.js +109 -0
  190. package/lib/performance/UnusedFederationPlugin.js +147 -0
  191. package/lib/performance/UnusedModulesPlugin.js +141 -0
  192. package/lib/performance/UnusedReexportsPlugin.js +1 -55
  193. package/lib/performance/isOnlyReexported.js +65 -0
  194. package/lib/prefetch/ChunkPrefetchFunctionRuntimeModule.js +25 -5
  195. package/lib/prefetch/ChunkPrefetchPreloadPlugin.js +8 -8
  196. package/lib/prefetch/ChunkPrefetchStartupRuntimeModule.js +9 -0
  197. package/lib/prefetch/ChunkPrefetchTriggerRuntimeModule.js +9 -0
  198. package/lib/prefetch/ChunkPreloadTriggerRuntimeModule.js +9 -0
  199. package/lib/prefetch/ResourceHintPlugin.js +18 -23
  200. package/lib/prefetch/ResourceHintRuntimeModule.js +9 -0
  201. package/lib/prefetch/StartupAssetHintRuntimeModule.js +16 -10
  202. package/lib/rules/RuleSetCompiler.js +211 -4
  203. package/lib/runtime/AsyncModuleRuntimeModule.js +98 -66
  204. package/lib/runtime/AutoPublicPathRuntimeModule.js +9 -0
  205. package/lib/runtime/BaseUriRuntimeModule.js +9 -0
  206. package/lib/runtime/ChunkNameRuntimeModule.js +9 -0
  207. package/lib/runtime/EnsureChunkRuntimeModule.js +35 -16
  208. package/lib/runtime/GetChunkFilenameRuntimeModule.js +12 -4
  209. package/lib/runtime/GetFullHashRuntimeModule.js +9 -0
  210. package/lib/runtime/GetMainFilenameRuntimeModule.js +9 -0
  211. package/lib/runtime/GetWorkletBootstrapRuntimeModule.js +9 -0
  212. package/lib/runtime/GlobalRuntimeModule.js +11 -4
  213. package/lib/runtime/HasOwnPropertyRuntimeModule.js +9 -0
  214. package/lib/runtime/HelperRuntimeModule.js +10 -0
  215. package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +305 -224
  216. package/lib/runtime/NonceRuntimeModule.js +9 -0
  217. package/lib/runtime/OnChunksLoadedRuntimeModule.js +67 -20
  218. package/lib/runtime/PublicPathRuntimeModule.js +9 -0
  219. package/lib/runtime/RuntimeIdRuntimeModule.js +9 -0
  220. package/lib/runtime/SpecNamespaceObjectRuntimeModule.js +90 -0
  221. package/lib/runtime/StartupChunkDependenciesRuntimeModule.js +9 -0
  222. package/lib/runtime/StartupEntrypointRuntimeModule.js +9 -0
  223. package/lib/runtime/SystemContextRuntimeModule.js +9 -0
  224. package/lib/runtime/ToBinaryRuntimeModule.js +9 -0
  225. package/lib/schemes/DataUriPlugin.js +3 -6
  226. package/lib/schemes/VirtualUrlPlugin.js +1 -1
  227. package/lib/serialization/BinaryMiddleware.js +9 -9
  228. package/lib/serialization/FileMiddleware.js +16 -16
  229. package/lib/serialization/ObjectMiddleware.js +168 -139
  230. package/lib/sharing/ConsumeSharedPlugin.js +12 -0
  231. package/lib/sharing/ConsumeSharedRuntimeModule.js +11 -0
  232. package/lib/sharing/ShareRuntimeModule.js +9 -0
  233. package/lib/sharing/declaredShared.js +47 -0
  234. package/lib/stats/DefaultStatsFactoryPlugin.js +6 -5
  235. package/lib/stats/DefaultStatsPrinterPlugin.js +3 -0
  236. package/lib/typescript/TypeScriptPlugin.js +2 -3
  237. package/lib/url/URLParserPlugin.js +6 -8
  238. package/lib/util/LocConverter.js +4 -7
  239. package/lib/util/SourceProcessor.js +91 -91
  240. package/lib/util/async.js +292 -0
  241. package/lib/util/buildDiagnostics.js +69 -0
  242. package/lib/util/chainedImports.js +3 -4
  243. package/lib/util/concatenate.js +4 -4
  244. package/lib/util/createHooksRegistry.js +1 -2
  245. package/lib/util/dataURL.js +2 -3
  246. package/lib/util/deterministicGrouping.js +8 -13
  247. package/lib/util/findGraphRoots.js +6 -13
  248. package/lib/util/fs.js +27 -1
  249. package/lib/util/globUtils.js +1 -1
  250. package/lib/util/handlerKeys.js +40 -0
  251. package/lib/util/hash/wasm-hash.js +3 -4
  252. package/lib/util/identifier.js +20 -0
  253. package/lib/util/internalSerializables.js +5 -1
  254. package/lib/util/registerExternalSerializer.js +6 -45
  255. package/lib/util/semver.js +5 -3
  256. package/lib/wasm-async/AsyncWasmCompileRuntimeModule.js +9 -0
  257. package/lib/wasm-async/AsyncWasmLoadingRuntimeModule.js +9 -0
  258. package/lib/wasm-async/AsyncWebAssemblyGenerator.js +6 -1
  259. package/lib/wasm-async/AsyncWebAssemblyJavascriptGenerator.js +5 -1
  260. package/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js +12 -4
  261. package/lib/wasm-sync/WebAssemblyGenerator.js +6 -1
  262. package/lib/wasm-sync/WebAssemblyJavascriptGenerator.js +5 -1
  263. package/lib/wasm-sync/WebAssemblyUtils.js +2 -4
  264. package/lib/web/JsonpChunkLoadingRuntimeModule.js +36 -0
  265. package/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js +19 -0
  266. package/package.json +55 -46
  267. package/schemas/WebpackOptions.check.js +1 -1
  268. package/schemas/WebpackOptions.json +379 -66
  269. package/schemas/plugins/LoaderOptionsPlugin.check.js +1 -1
  270. package/schemas/plugins/ManifestPlugin.check.js +1 -1
  271. package/schemas/plugins/SourceMapDevToolPlugin.check.js +1 -1
  272. package/schemas/plugins/asset/AssetGeneratorOptions.check.js +1 -1
  273. package/schemas/plugins/asset/AssetResourceGeneratorOptions.check.js +1 -1
  274. package/schemas/plugins/container/ContainerPlugin.check.js +1 -1
  275. package/schemas/plugins/container/ModuleFederationPlugin.check.js +1 -1
  276. package/schemas/plugins/debug/ProfilingPlugin.check.js +1 -1
  277. package/schemas/plugins/dll/DllReferencePlugin.check.js +1 -1
  278. package/schemas/plugins/ids/HashedModuleIdsPlugin.check.js +1 -1
  279. package/schemas/plugins/schemes/HttpUriPlugin.check.js +1 -1
  280. package/types.d.ts +1723 -604
  281. package/lib/dependencies/ExportBindingInitFragment.js +0 -191
  282. package/lib/errors/EntrypointOverlapWarning.js +0 -45
  283. package/lib/performance/EntrypointOverlapPlugin.js +0 -122
@@ -17,22 +17,23 @@ const {
17
17
  parseCommentOptionsInRange
18
18
  } = require("../util/magicComment");
19
19
  const BasicEvaluatedExpression = require("./BasicEvaluatedExpression");
20
+ const { nonASCIIwhitespace } = require("./parser");
20
21
  const {
22
+ BLOCK_DECLARATIONS,
23
+ HOISTED_DECLARATIONS,
21
24
  LEGACY_ASSERT_ATTRIBUTES,
25
+ MAX_MASKED_INDEX,
26
+ MODULE_DECLARATIONS,
22
27
  WebpackParser,
23
28
  buildLineStarts,
29
+ declaresIntoBlock,
24
30
  hasOctalEscape,
25
31
  isIdentifierChar,
32
+ overflowFrom,
26
33
  positionAt
27
34
  } = require("./syntax");
28
35
 
29
- /** @typedef {typeof import("acorn").Parser} AcornParser */
30
- /**
31
- * @import {
32
- * Options as AcornOptions,
33
- * ecmaVersion as EcmaVersion
34
- * } from "acorn"
35
- */
36
+ /** @import { EcmaVersion, ParserOptions, ParserPlugin } from "./syntax" */
36
37
  /**
37
38
  * @import {
38
39
  * AssignmentExpression,
@@ -143,6 +144,9 @@ const {
143
144
  /** @typedef {(ident: string) => void} OnIdentString */
144
145
  /** @typedef {(ident: string, identifier: Identifier) => void} OnIdent */
145
146
  /** @typedef {StatementPathItem[]} StatementPath */
147
+ /** @typedef {(Statement | ModuleDeclaration)[]} HoistedDeclarations the declarations one scope hoists, in source order */
148
+ /** @typedef {(ModuleDeclaration | Statement | undefined)[]} ModuleDeclarations the program's import and re-export declarations in source order, each followed by the statement written before it */
149
+ /** @typedef {number | number[]} BlockDeclarations which statements of one list declare into its block scope: a mask of their indices, or the indices themselves once one is too large to mask */
146
150
 
147
151
  /** @typedef {Set<DestructuringAssignmentProperty>} DestructuringAssignmentProperties */
148
152
 
@@ -151,25 +155,34 @@ const {
151
155
 
152
156
  /** @type {string[]} */
153
157
  const EMPTY_ARRAY = [];
158
+ /** @type {HoistedDeclarations} */
159
+ const EMPTY_HOISTED_DECLARATIONS = [];
160
+ // what the module pass reports; `export default` is the block pass's
161
+ const MODULE_STATEMENT_TYPES = new Set([
162
+ "ImportDeclaration",
163
+ "ExportNamedDeclaration",
164
+ "ExportAllDeclaration"
165
+ ]);
166
+ // what the parser records per scope, and so what the hoisting pass reaches
167
+ const RECORDED_STATEMENT_TYPES = new Set([
168
+ "VariableDeclaration",
169
+ "FunctionDeclaration",
170
+ "ClassDeclaration"
171
+ ]);
172
+ /** @type {ModuleDeclarations} */
173
+ const EMPTY_MODULE_DECLARATIONS = [];
174
+ // what the parser records per statement list, and so what the block pass
175
+ // reaches. An expression statement is recorded only when it assigns to an
176
+ // object pattern, so a tap on that type is not served from the list.
177
+ const BLOCK_DECLARATION_TYPES = new Set([
178
+ "VariableDeclaration",
179
+ "ClassDeclaration",
180
+ "ExportNamedDeclaration",
181
+ "ExportDefaultDeclaration"
182
+ ]);
154
183
 
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
- };
184
+ /** @type {MembersOptionals} */
185
+ const EMPTY_MEMBERS_OPTIONALS = [];
173
186
 
174
187
  const ALLOWED_MEMBER_TYPES_CALL_EXPRESSION = 0b01;
175
188
  const ALLOWED_MEMBER_TYPES_EXPRESSION = 0b10;
@@ -183,8 +196,11 @@ const RETURN_FALSE = () => false;
183
196
  // replacing three fresh closures per evaluated free variable.
184
197
  const RETURN_EMPTY_ARRAY = () => [];
185
198
 
199
+ // Shared no-result fallback for the identifier evaluators.
200
+ const RETURN_UNDEFINED = () => undefined;
201
+
186
202
  // Syntax: https://developer.mozilla.org/en/SpiderMonkey/Parser_API
187
- let parser = /** @type {AcornParser} */ (WebpackParser);
203
+ let parser = WebpackParser;
188
204
 
189
205
  /** @typedef {Record<string, string> & { _isLegacyAssert?: boolean }} ImportAttributes */
190
206
 
@@ -327,7 +343,7 @@ class VariableInfo {
327
343
  * @property {boolean=} ranges
328
344
  * @property {boolean=} allowHashBang
329
345
  * @property {boolean=} allowReturnOutsideFunction
330
- * @property {boolean=} lazyNodes internal: serve `range` lazily and skip acorn's location/range tracking
346
+ * @property {boolean=} lazyNodes internal: serve `range` lazily and skip the parser's location/range tracking
331
347
  * @property {Comment[]=} lazyComments internal: collect comments here without slicing their text eagerly
332
348
  * @property {boolean=} importPhases enable parsing of the import phase proposals (import defer / import source)
333
349
  * @property {boolean=} moduleFallback internal: for `auto`, let the parser downgrade module->script in place instead of re-parsing
@@ -374,6 +390,7 @@ class VariableInfo {
374
390
 
375
391
  /** @typedef {string[]} CalleeMembers */
376
392
  /** @typedef {string[]} Members */
393
+ /** @typedef {UpdateExpression | ForInStatement | ForOfStatement | AssignmentExpression} WriteStatement a construct that writes to the reference it carries */
377
394
  /** @typedef {boolean[]} MembersOptionals */
378
395
  /** @typedef {Range[]} MemberRanges */
379
396
 
@@ -430,24 +447,455 @@ const joinRanges = (startRange, endRange) => {
430
447
  * Helper function used to generate a string representation of a
431
448
  * [member expression](https://github.com/estree/estree/blob/master/es5.md#memberexpression).
432
449
  * @param {string} object object to name
433
- * @param {Members} membersReversed reversed list of members
450
+ * @param {Members} members members in source order
434
451
  * @returns {string} member expression as a string
435
452
  * @example
436
453
  * ```js
437
- * const membersReversed = ["property1", "property2", "property3"]; // Members parsed from the AST
438
- * const name = objectAndMembersToName("myObject", membersReversed);
454
+ * const members = ["property1", "property2", "property3"]; // Members parsed from the AST
455
+ * const name = objectAndMembersToName("myObject", members);
439
456
  *
440
457
  * console.log(name); // "myObject.property1.property2.property3"
441
458
  * ```
442
459
  */
443
- const objectAndMembersToName = (object, membersReversed) => {
460
+ const objectAndMembersToName = (object, members) => {
444
461
  let name = object;
445
- for (let i = membersReversed.length - 1; i >= 0; i--) {
446
- name = `${name}.${membersReversed[i]}`;
462
+ for (let i = 0; i < members.length; i++) {
463
+ name = `${name}.${members[i]}`;
447
464
  }
448
465
  return name;
449
466
  };
450
467
 
468
+ /**
469
+ * The range of the object below each of the first `count` members of a chain,
470
+ * in source order: what `extractMemberExpressionChain` reports as memberRanges.
471
+ * @param {Expression | Super} expression a member expression
472
+ * @param {number} count how many members the chain has
473
+ * @returns {MemberRanges} the ranges
474
+ */
475
+ const memberRangesOf = (expression, count) => {
476
+ /** @type {MemberRanges} */
477
+ const ranges = [];
478
+ let expr = /** @type {MemberExpression} */ (expression);
479
+ for (let i = 0; i < count; i++) {
480
+ const object = expr.object;
481
+ ranges.push(/** @type {Range} */ (object.range));
482
+ expr = /** @type {MemberExpression} */ (object);
483
+ }
484
+ if (count > 1) ranges.reverse();
485
+ return ranges;
486
+ };
487
+
488
+ /**
489
+ * In simple logical cases, we can use valueAsExpression to assist us in evaluating the expression on
490
+ * either side of a [BinaryExpression](https://github.com/estree/estree/blob/master/es5.md#binaryexpression).
491
+ * This supports scenarios in webpack like conditionally `import()`'ing modules based on some simple evaluation:
492
+ *
493
+ * ```js
494
+ * if (1 === 3) {
495
+ * import("./moduleA"); // webpack will auto evaluate this and not import the modules
496
+ * }
497
+ * ```
498
+ *
499
+ * Additional scenarios include evaluation of strings inside of dynamic import statements:
500
+ *
501
+ * ```js
502
+ * const foo = "foo";
503
+ * const bar = "bar";
504
+ *
505
+ * import("./" + foo + bar); // webpack will auto evaluate this into import("./foobar")
506
+ * ```
507
+ * @param {boolean | number | bigint | string} value the value to convert to an expression
508
+ * @param {BinaryExpression | UnaryExpression} expr the expression being evaluated
509
+ * @param {boolean} sideEffects whether the expression has side effects
510
+ * @returns {BasicEvaluatedExpression | undefined} the evaluated expression
511
+ * @example
512
+ *
513
+ * ```js
514
+ * const binaryExpr = new BinaryExpression("+",
515
+ * { type: "Literal", value: 2 },
516
+ * { type: "Literal", value: 3 }
517
+ * );
518
+ *
519
+ * const leftValue = 2;
520
+ * const rightValue = 3;
521
+ *
522
+ * const leftExpr = valueAsExpression(leftValue, binaryExpr.left, false);
523
+ * const rightExpr = valueAsExpression(rightValue, binaryExpr.right, false);
524
+ * const result = new BasicEvaluatedExpression()
525
+ * .setNumber(leftExpr.number + rightExpr.number)
526
+ * .setRange(binaryExpr.range);
527
+ *
528
+ * console.log(result.number); // Output: 5
529
+ * ```
530
+ */
531
+ const valueAsExpression = (value, expr, sideEffects) => {
532
+ switch (typeof value) {
533
+ case "boolean":
534
+ return new BasicEvaluatedExpression()
535
+ .setBoolean(value)
536
+ .setSideEffects(sideEffects)
537
+ .setRange(/** @type {Range} */ (expr.range));
538
+ case "number":
539
+ return new BasicEvaluatedExpression()
540
+ .setNumber(value)
541
+ .setSideEffects(sideEffects)
542
+ .setRange(/** @type {Range} */ (expr.range));
543
+ case "bigint":
544
+ return new BasicEvaluatedExpression()
545
+ .setBigInt(value)
546
+ .setSideEffects(sideEffects)
547
+ .setRange(/** @type {Range} */ (expr.range));
548
+ case "string":
549
+ return new BasicEvaluatedExpression()
550
+ .setString(value)
551
+ .setSideEffects(sideEffects)
552
+ .setRange(/** @type {Range} */ (expr.range));
553
+ }
554
+ };
555
+
556
+ // The constant operators, shared so evaluating `a - b` allocates no handler.
557
+
558
+ /**
559
+ * @param {EXPECTED_ANY} l left operand
560
+ * @param {EXPECTED_ANY} r right operand
561
+ * @returns {EXPECTED_ANY} the result
562
+ */
563
+ const SUBTRACT = (l, r) => l - r;
564
+
565
+ /**
566
+ * @param {EXPECTED_ANY} l left operand
567
+ * @param {EXPECTED_ANY} r right operand
568
+ * @returns {EXPECTED_ANY} the result
569
+ */
570
+ const MULTIPLY = (l, r) => l * r;
571
+
572
+ /**
573
+ * @param {EXPECTED_ANY} l left operand
574
+ * @param {EXPECTED_ANY} r right operand
575
+ * @returns {EXPECTED_ANY} the result
576
+ */
577
+ const DIVIDE = (l, r) => l / r;
578
+
579
+ /**
580
+ * @param {EXPECTED_ANY} l left operand
581
+ * @param {EXPECTED_ANY} r right operand
582
+ * @returns {EXPECTED_ANY} the result
583
+ */
584
+ const POWER = (l, r) => l ** r;
585
+
586
+ /**
587
+ * @param {EXPECTED_ANY} l left operand
588
+ * @param {EXPECTED_ANY} r right operand
589
+ * @returns {EXPECTED_ANY} the result
590
+ */
591
+ const BITWISE_AND = (l, r) => l & r;
592
+
593
+ /**
594
+ * @param {EXPECTED_ANY} l left operand
595
+ * @param {EXPECTED_ANY} r right operand
596
+ * @returns {EXPECTED_ANY} the result
597
+ */
598
+ const BITWISE_OR = (l, r) => l | r;
599
+
600
+ /**
601
+ * @param {EXPECTED_ANY} l left operand
602
+ * @param {EXPECTED_ANY} r right operand
603
+ * @returns {EXPECTED_ANY} the result
604
+ */
605
+ const BITWISE_XOR = (l, r) => l ^ r;
606
+
607
+ /**
608
+ * @param {EXPECTED_ANY} l left operand
609
+ * @param {EXPECTED_ANY} r right operand
610
+ * @returns {EXPECTED_ANY} the result
611
+ */
612
+ const UNSIGNED_SHIFT_RIGHT = (l, r) => l >>> r;
613
+
614
+ /**
615
+ * @param {EXPECTED_ANY} l left operand
616
+ * @param {EXPECTED_ANY} r right operand
617
+ * @returns {EXPECTED_ANY} the result
618
+ */
619
+ const SHIFT_RIGHT = (l, r) => l >> r;
620
+
621
+ /**
622
+ * @param {EXPECTED_ANY} l left operand
623
+ * @param {EXPECTED_ANY} r right operand
624
+ * @returns {EXPECTED_ANY} the result
625
+ */
626
+ const SHIFT_LEFT = (l, r) => l << r;
627
+
628
+ /**
629
+ * @param {EXPECTED_ANY} l left operand
630
+ * @param {EXPECTED_ANY} r right operand
631
+ * @returns {EXPECTED_ANY} the result
632
+ */
633
+ const LESS_THAN = (l, r) => l < r;
634
+
635
+ /**
636
+ * @param {EXPECTED_ANY} l left operand
637
+ * @param {EXPECTED_ANY} r right operand
638
+ * @returns {EXPECTED_ANY} the result
639
+ */
640
+ const GREATER_THAN = (l, r) => l > r;
641
+
642
+ /**
643
+ * @param {EXPECTED_ANY} l left operand
644
+ * @param {EXPECTED_ANY} r right operand
645
+ * @returns {EXPECTED_ANY} the result
646
+ */
647
+ const LESS_THAN_OR_EQUAL = (l, r) => l <= r;
648
+
649
+ /**
650
+ * @param {EXPECTED_ANY} l left operand
651
+ * @param {EXPECTED_ANY} r right operand
652
+ * @returns {EXPECTED_ANY} the result
653
+ */
654
+ const GREATER_THAN_OR_EQUAL = (l, r) => l >= r;
655
+
656
+ /**
657
+ * @param {EXPECTED_ANY} v operand
658
+ * @returns {EXPECTED_ANY} the result
659
+ */
660
+ const BITWISE_NOT = (v) => ~v;
661
+
662
+ /**
663
+ * @param {EXPECTED_ANY} v operand
664
+ * @returns {EXPECTED_ANY} the result
665
+ */
666
+ // eslint-disable-next-line no-implicit-coercion
667
+ const TO_NUMBER = (v) => +v;
668
+
669
+ /**
670
+ * @param {EXPECTED_ANY} v operand
671
+ * @returns {EXPECTED_ANY} the result
672
+ */
673
+ const NEGATE = (v) => -v;
674
+
675
+ /**
676
+ * Evaluates a binary expression if and only if it is a const operation (e.g. 1 + 2, "a" + "b", etc.).
677
+ * @template T
678
+ * @param {JavascriptParser} parser the parser evaluating the expression
679
+ * @param {BinaryExpression} expr the binary expression
680
+ * @param {(leftOperand: T, rightOperand: T) => boolean | number | bigint | string} operandHandler the handler for the operation (e.g. (a, b) => a + b)
681
+ * @returns {BasicEvaluatedExpression | undefined} the evaluated expression
682
+ */
683
+ const handleConstOperation = (parser, expr, operandHandler) => {
684
+ const left = parser.evaluateExpression(expr.left);
685
+ if (!left.isCompileTimeValue()) return;
686
+
687
+ const right = parser.evaluateExpression(expr.right);
688
+ if (!right.isCompileTimeValue()) return;
689
+
690
+ const result = operandHandler(
691
+ /** @type {T} */ (left.asCompileTimeValue()),
692
+ /** @type {T} */ (right.asCompileTimeValue())
693
+ );
694
+ return valueAsExpression(
695
+ result,
696
+ expr,
697
+ left.couldHaveSideEffects() || right.couldHaveSideEffects()
698
+ );
699
+ };
700
+
701
+ /**
702
+ * Helper function to determine if two booleans are always different. This is used in `handleStrictEqualityComparison`
703
+ * to determine if an expressions boolean or nullish conversion is equal or not.
704
+ * @param {boolean} a first boolean to compare
705
+ * @param {boolean} b second boolean to compare
706
+ * @returns {boolean} true if the two booleans are always different, false otherwise
707
+ */
708
+ const isAlwaysDifferent = (a, b) =>
709
+ (a === true && b === false) || (a === false && b === true);
710
+
711
+ /**
712
+ * Handle template string compare.
713
+ * @param {BasicEvaluatedExpression} left left
714
+ * @param {BasicEvaluatedExpression} right right
715
+ * @param {BinaryExpression} expr the comparison, for its range
716
+ * @param {boolean} eql true for "===" and false for "!=="
717
+ * @returns {BasicEvaluatedExpression | undefined} result
718
+ */
719
+ const handleTemplateStringCompare = (left, right, expr, eql) => {
720
+ /**
721
+ * Returns value.
722
+ * @param {BasicEvaluatedExpression[]} parts parts
723
+ * @returns {string} value
724
+ */
725
+ const getPrefix = (parts) => {
726
+ let value = "";
727
+ for (const p of parts) {
728
+ const v = p.asString();
729
+ if (v !== undefined) value += v;
730
+ else break;
731
+ }
732
+ return value;
733
+ };
734
+ /**
735
+ * Returns value.
736
+ * @param {BasicEvaluatedExpression[]} parts parts
737
+ * @returns {string} value
738
+ */
739
+ const getSuffix = (parts) => {
740
+ let value = "";
741
+ for (let i = parts.length - 1; i >= 0; i--) {
742
+ const v = parts[i].asString();
743
+ if (v !== undefined) value = v + value;
744
+ else break;
745
+ }
746
+ return value;
747
+ };
748
+ const leftPrefix = getPrefix(
749
+ /** @type {BasicEvaluatedExpression[]} */ (left.parts)
750
+ );
751
+ const rightPrefix = getPrefix(
752
+ /** @type {BasicEvaluatedExpression[]} */ (right.parts)
753
+ );
754
+ const leftSuffix = getSuffix(
755
+ /** @type {BasicEvaluatedExpression[]} */ (left.parts)
756
+ );
757
+ const rightSuffix = getSuffix(
758
+ /** @type {BasicEvaluatedExpression[]} */ (right.parts)
759
+ );
760
+ const lenPrefix = Math.min(leftPrefix.length, rightPrefix.length);
761
+ const lenSuffix = Math.min(leftSuffix.length, rightSuffix.length);
762
+ const prefixMismatch =
763
+ lenPrefix > 0 &&
764
+ leftPrefix.slice(0, lenPrefix) !== rightPrefix.slice(0, lenPrefix);
765
+ const suffixMismatch =
766
+ lenSuffix > 0 &&
767
+ leftSuffix.slice(-lenSuffix) !== rightSuffix.slice(-lenSuffix);
768
+ if (prefixMismatch || suffixMismatch) {
769
+ return new BasicEvaluatedExpression()
770
+ .setRange(/** @type {Range} */ (expr.range))
771
+ .setBoolean(!eql)
772
+ .setSideEffects(
773
+ left.couldHaveSideEffects() || right.couldHaveSideEffects()
774
+ );
775
+ }
776
+ };
777
+
778
+ /**
779
+ * Helper function to handle BinaryExpressions using strict equality comparisons (e.g. "===" and "!==").
780
+ * @param {JavascriptParser} parser the parser evaluating the expression
781
+ * @param {BinaryExpression} expr the binary expression
782
+ * @param {boolean} eql true for "===" and false for "!=="
783
+ * @returns {BasicEvaluatedExpression | undefined} the evaluated expression
784
+ */
785
+ const handleStrictEqualityComparison = (parser, expr, eql) => {
786
+ const left = parser.evaluateExpression(expr.left);
787
+ const right = parser.evaluateExpression(expr.right);
788
+
789
+ const leftConst = left.isCompileTimeValue();
790
+ const rightConst = right.isCompileTimeValue();
791
+
792
+ if (leftConst && rightConst) {
793
+ return new BasicEvaluatedExpression()
794
+ .setRange(/** @type {Range} */ (expr.range))
795
+ .setBoolean(
796
+ eql === (left.asCompileTimeValue() === right.asCompileTimeValue())
797
+ )
798
+ .setSideEffects(
799
+ left.couldHaveSideEffects() || right.couldHaveSideEffects()
800
+ );
801
+ }
802
+
803
+ if (left.isArray() && right.isArray()) {
804
+ return new BasicEvaluatedExpression()
805
+ .setRange(/** @type {Range} */ (expr.range))
806
+ .setBoolean(!eql)
807
+ .setSideEffects(
808
+ left.couldHaveSideEffects() || right.couldHaveSideEffects()
809
+ );
810
+ }
811
+ if (left.isTemplateString() && right.isTemplateString()) {
812
+ return handleTemplateStringCompare(left, right, expr, eql);
813
+ }
814
+
815
+ const leftPrimitive = left.isPrimitiveType();
816
+ const rightPrimitive = right.isPrimitiveType();
817
+
818
+ if (
819
+ // Primitive !== Object or
820
+ // compile-time object types are never equal to something at runtime
821
+ (leftPrimitive === false && (leftConst || rightPrimitive === true)) ||
822
+ (rightPrimitive === false && (rightConst || leftPrimitive === true)) ||
823
+ // Different nullish or boolish status also means not equal
824
+ isAlwaysDifferent(
825
+ /** @type {boolean} */ (left.asBool()),
826
+ /** @type {boolean} */ (right.asBool())
827
+ ) ||
828
+ isAlwaysDifferent(
829
+ /** @type {boolean} */ (left.asNullish()),
830
+ /** @type {boolean} */ (right.asNullish())
831
+ )
832
+ ) {
833
+ return new BasicEvaluatedExpression()
834
+ .setRange(/** @type {Range} */ (expr.range))
835
+ .setBoolean(!eql)
836
+ .setSideEffects(
837
+ left.couldHaveSideEffects() || right.couldHaveSideEffects()
838
+ );
839
+ }
840
+ };
841
+
842
+ /**
843
+ * Helper function to handle BinaryExpressions using abstract equality comparisons (e.g. "==" and "!=").
844
+ * @param {JavascriptParser} parser the parser evaluating the expression
845
+ * @param {BinaryExpression} expr the binary expression
846
+ * @param {boolean} eql true for "==" and false for "!="
847
+ * @returns {BasicEvaluatedExpression | undefined} the evaluated expression
848
+ */
849
+ const handleAbstractEqualityComparison = (parser, expr, eql) => {
850
+ const left = parser.evaluateExpression(expr.left);
851
+ const right = parser.evaluateExpression(expr.right);
852
+
853
+ const leftConst = left.isCompileTimeValue();
854
+ const rightConst = right.isCompileTimeValue();
855
+
856
+ if (leftConst && rightConst) {
857
+ return new BasicEvaluatedExpression()
858
+ .setRange(/** @type {Range} */ (expr.range))
859
+ .setBoolean(
860
+ eql ===
861
+ // eslint-disable-next-line eqeqeq
862
+ (left.asCompileTimeValue() == right.asCompileTimeValue())
863
+ )
864
+ .setSideEffects(
865
+ left.couldHaveSideEffects() || right.couldHaveSideEffects()
866
+ );
867
+ }
868
+
869
+ if (left.isArray() && right.isArray()) {
870
+ return new BasicEvaluatedExpression()
871
+ .setRange(/** @type {Range} */ (expr.range))
872
+ .setBoolean(!eql)
873
+ .setSideEffects(
874
+ left.couldHaveSideEffects() || right.couldHaveSideEffects()
875
+ );
876
+ }
877
+ if (left.isTemplateString() && right.isTemplateString()) {
878
+ return handleTemplateStringCompare(left, right, expr, eql);
879
+ }
880
+ };
881
+
882
+ /**
883
+ * Evaluates a UnaryExpression if and only if it is a basic const operator (e.g. +a, -a, ~a).
884
+ * @template T
885
+ * @param {JavascriptParser} parser the parser evaluating the expression
886
+ * @param {UnaryExpression} expr the unary expression
887
+ * @param {(operand: T) => boolean | number | bigint | string} operandHandler handler for the operand
888
+ * @returns {BasicEvaluatedExpression | undefined} evaluated expression
889
+ */
890
+ const handleUnaryConstOperation = (parser, expr, operandHandler) => {
891
+ const argument = parser.evaluateExpression(expr.argument);
892
+ if (!argument.isCompileTimeValue()) return;
893
+ const result = operandHandler(
894
+ /** @type {T} */ (argument.asCompileTimeValue())
895
+ );
896
+ return valueAsExpression(result, expr, argument.couldHaveSideEffects());
897
+ };
898
+
451
899
  /**
452
900
  * Grabs the name of a given expression and returns it as a string or undefined. Has particular
453
901
  * handling for [Identifiers](https://github.com/estree/estree/blob/master/es5.md#identifier),
@@ -511,6 +959,183 @@ const evaluateAstCaches = new WeakMap();
511
959
  // The inner map is strong while the compilation lives, so cap it
512
960
  const EVALUATE_AST_CACHE_LIMIT = 4096;
513
961
 
962
+ /**
963
+ * Add one declaration, starting the list at the first — most scopes declare
964
+ * nothing, and the shared empty list spares them an array.
965
+ * @param {HoistedDeclarations} hoisted the list so far
966
+ * @param {Statement | ModuleDeclaration} declaration what to add
967
+ * @returns {HoistedDeclarations} the list holding it
968
+ */
969
+ const addHoistedDeclaration = (hoisted, declaration) => {
970
+ if (hoisted === EMPTY_HOISTED_DECLARATIONS) return [declaration];
971
+ hoisted.push(declaration);
972
+ return hoisted;
973
+ };
974
+
975
+ /**
976
+ * Collect the declarations that hoist to one scope, for an AST that did not
977
+ * come from webpack's parser and so carries no record of its own.
978
+ * @param {(Statement | ModuleDeclaration)[]} statements the scope's statements
979
+ * @param {HoistedDeclarations} hoisted the list so far
980
+ * @returns {HoistedDeclarations} the list holding what they declare
981
+ */
982
+ const collectHoistedDeclarations = (statements, hoisted) => {
983
+ for (let index = 0, len = statements.length; index < len; index++) {
984
+ hoisted = collectHoistedDeclaration(statements[index], hoisted);
985
+ }
986
+ return hoisted;
987
+ };
988
+
989
+ /**
990
+ * Collect from one statement, descending exactly as far as a `var` reaches:
991
+ * through blocks and loop bodies, never into another function.
992
+ * @param {Statement | ModuleDeclaration} statement the statement
993
+ * @param {HoistedDeclarations} hoisted the list so far
994
+ * @returns {HoistedDeclarations} the list holding what it declares
995
+ */
996
+ const collectHoistedDeclaration = (statement, hoisted) => {
997
+ switch (statement.type) {
998
+ case "VariableDeclaration":
999
+ if (statement.kind === "var") {
1000
+ hoisted = addHoistedDeclaration(hoisted, statement);
1001
+ }
1002
+ break;
1003
+ case "FunctionDeclaration":
1004
+ case "ClassDeclaration":
1005
+ hoisted = addHoistedDeclaration(hoisted, statement);
1006
+ break;
1007
+ case "BlockStatement":
1008
+ hoisted = collectHoistedDeclarations(statement.body, hoisted);
1009
+ break;
1010
+ case "IfStatement":
1011
+ hoisted = collectHoistedDeclaration(statement.consequent, hoisted);
1012
+ if (statement.alternate) {
1013
+ hoisted = collectHoistedDeclaration(statement.alternate, hoisted);
1014
+ }
1015
+ break;
1016
+ case "ForStatement":
1017
+ if (statement.init && statement.init.type === "VariableDeclaration") {
1018
+ hoisted = collectHoistedDeclaration(statement.init, hoisted);
1019
+ }
1020
+ hoisted = collectHoistedDeclaration(statement.body, hoisted);
1021
+ break;
1022
+ case "ForInStatement":
1023
+ case "ForOfStatement":
1024
+ if (statement.left.type === "VariableDeclaration") {
1025
+ hoisted = collectHoistedDeclaration(statement.left, hoisted);
1026
+ }
1027
+ hoisted = collectHoistedDeclaration(statement.body, hoisted);
1028
+ break;
1029
+ case "WhileStatement":
1030
+ case "DoWhileStatement":
1031
+ case "LabeledStatement":
1032
+ case "WithStatement":
1033
+ hoisted = collectHoistedDeclaration(statement.body, hoisted);
1034
+ break;
1035
+ case "TryStatement":
1036
+ hoisted = collectHoistedDeclaration(statement.block, hoisted);
1037
+ if (statement.handler) {
1038
+ hoisted = collectHoistedDeclaration(statement.handler.body, hoisted);
1039
+ }
1040
+ if (statement.finalizer) {
1041
+ hoisted = collectHoistedDeclaration(statement.finalizer, hoisted);
1042
+ }
1043
+ break;
1044
+ case "SwitchStatement": {
1045
+ const cases = statement.cases;
1046
+ for (let index = 0, len = cases.length; index < len; index++) {
1047
+ hoisted = collectHoistedDeclarations(cases[index].consequent, hoisted);
1048
+ }
1049
+ break;
1050
+ }
1051
+ }
1052
+ return hoisted;
1053
+ };
1054
+
1055
+ /**
1056
+ * What the program imports and re-exports: the parser's own record, or a scan
1057
+ * of the top-level statements for an AST some other parser built.
1058
+ * @param {Program} ast the program
1059
+ * @param {boolean} trusted whether the parser's own record may be read
1060
+ * @returns {ModuleDeclarations} the declarations, in source order
1061
+ */
1062
+ const moduleDeclarationsOf = (ast, trusted) => {
1063
+ if (trusted) {
1064
+ const recorded = /** @type {EXPECTED_ANY} */ (ast)[MODULE_DECLARATIONS];
1065
+ if (recorded !== undefined) return recorded;
1066
+ }
1067
+ /** @type {ModuleDeclarations} */
1068
+ let declarations = EMPTY_MODULE_DECLARATIONS;
1069
+ const body = ast.body;
1070
+ for (let index = 0, len = body.length; index < len; index++) {
1071
+ const statement = body[index];
1072
+ if (MODULE_STATEMENT_TYPES.has(statement.type)) {
1073
+ if (declarations === EMPTY_MODULE_DECLARATIONS) declarations = [];
1074
+ declarations.push(statement, index === 0 ? undefined : body[index - 1]);
1075
+ }
1076
+ }
1077
+ return declarations;
1078
+ };
1079
+
1080
+ /**
1081
+ * What hoists to the scope the node opens: the parser's own record, or a walk
1082
+ * of the body for an AST some other parser built.
1083
+ * @param {Program | BlockStatement} node the node owning the scope
1084
+ * @param {boolean} trusted whether the parser's own record may be read
1085
+ * @returns {HoistedDeclarations} the declarations, in source order
1086
+ */
1087
+ const hoistedDeclarationsOf = (node, trusted) => {
1088
+ if (trusted) {
1089
+ const recorded = /** @type {EXPECTED_ANY} */ (node)[HOISTED_DECLARATIONS];
1090
+ if (recorded !== undefined) return recorded;
1091
+ }
1092
+ return collectHoistedDeclarations(node.body, EMPTY_HOISTED_DECLARATIONS);
1093
+ };
1094
+
1095
+ /**
1096
+ * Which statements of a list declare into its block scope, scanned for an AST
1097
+ * some other parser built. The parser's own record is read instead when there
1098
+ * is one.
1099
+ * @param {(Statement | ModuleDeclaration)[]} statements the list
1100
+ * @returns {BlockDeclarations} their indices, in source order
1101
+ */
1102
+ const collectBlockDeclarations = (statements) => {
1103
+ let mask = 0;
1104
+ /** @type {number[] | undefined} */
1105
+ let overflow;
1106
+ for (let index = 0, len = statements.length; index < len; index++) {
1107
+ if (!declaresIntoBlock(statements[index])) continue;
1108
+ if (overflow !== undefined) overflow.push(index);
1109
+ else if (index <= MAX_MASKED_INDEX) mask |= 1 << index;
1110
+ else overflow = overflowFrom(mask, index);
1111
+ }
1112
+ return overflow === undefined ? mask : overflow;
1113
+ };
1114
+
1115
+ /**
1116
+ * Whether the source between two offsets is whitespace only.
1117
+ * @param {string} source the module source
1118
+ * @param {number} start where to start
1119
+ * @param {number} end where to stop
1120
+ * @returns {boolean} whether nothing but whitespace lies between them
1121
+ */
1122
+ const onlyWhitespaceBetween = (source, start, end) => {
1123
+ for (let index = start; index < end; index++) {
1124
+ const code = source.charCodeAt(index);
1125
+ // the classification `skipSpace` uses: anything else is a token, and a
1126
+ // token means the run of comments before the statement has ended
1127
+ if (
1128
+ code !== 32 &&
1129
+ code !== 160 &&
1130
+ !(code > 8 && code < 14) &&
1131
+ !(code >= 5760 && nonASCIIwhitespace.test(source[index]))
1132
+ ) {
1133
+ return false;
1134
+ }
1135
+ }
1136
+ return true;
1137
+ };
1138
+
514
1139
  const CLASS_NAME = "JavascriptParser";
515
1140
 
516
1141
  // Reserved words in strict mode, so unusable as binding names there (12.7.2).
@@ -573,17 +1198,33 @@ class JavascriptParser extends Parser {
573
1198
  isPure: new HookMap(
574
1199
  () => new SyncBailHook(["expression", "commentsStartPosition"])
575
1200
  ),
576
- /** @type {SyncBailHook<[Statement | ModuleDeclaration | MaybeNamedClassDeclaration | MaybeNamedFunctionDeclaration], boolean | void>} */
1201
+ /**
1202
+ * Every statement the hoisting pass reads, which a tap on this turns
1203
+ * back into a walk of them all. Tap `preStatementByType` instead.
1204
+ * @type {SyncBailHook<[Statement | ModuleDeclaration | MaybeNamedClassDeclaration | MaybeNamedFunctionDeclaration], boolean | void>}
1205
+ * @deprecated TODO webpack 6: remove, with the statement walk it turns on
1206
+ */
577
1207
  preStatement: new SyncBailHook(["statement"]),
578
1208
  /**
1209
+ * Each declaration the parser records: a variable, function or class
1210
+ * one. Any other type turns the pass back into a walk of every
1211
+ * statement, and goes away with that walk in webpack 6.
579
1212
  * @type {HookMap<SyncBailHook<[Statement | ModuleDeclaration | MaybeNamedClassDeclaration | MaybeNamedFunctionDeclaration], boolean | void>>}
580
1213
  * @since 5.109.0
581
1214
  */
582
1215
  preStatementByType: new HookMap(() => new SyncBailHook(["statement"])),
583
1216
 
584
- /** @type {SyncBailHook<[Statement | ModuleDeclaration | MaybeNamedClassDeclaration | MaybeNamedFunctionDeclaration], boolean | void>} */
1217
+ /**
1218
+ * Every statement the block pass reads, which a tap on this turns back
1219
+ * into a walk of them all. Tap `blockPreStatementByType` instead.
1220
+ * @type {SyncBailHook<[Statement | ModuleDeclaration | MaybeNamedClassDeclaration | MaybeNamedFunctionDeclaration], boolean | void>}
1221
+ * @deprecated TODO webpack 6: remove, with the statement walk it turns on
1222
+ */
585
1223
  blockPreStatement: new SyncBailHook(["declaration"]),
586
1224
  /**
1225
+ * Each statement the parser records for the block pass: a variable, class
1226
+ * or export declaration. Any other type turns the pass back into a walk
1227
+ * of every statement, and goes away with that walk in webpack 6.
587
1228
  * @type {HookMap<SyncBailHook<[Statement | ModuleDeclaration | MaybeNamedClassDeclaration | MaybeNamedFunctionDeclaration], boolean | void>>}
588
1229
  * @since 5.109.0
589
1230
  */
@@ -681,6 +1322,12 @@ class JavascriptParser extends Parser {
681
1322
  rename: new HookMap(() => new SyncBailHook(["initExpression"])),
682
1323
  /** @type {HookMap<SyncBailHook<[AssignmentExpression], boolean | void>>} */
683
1324
  assign: new HookMap(() => new SyncBailHook(["expression"])),
1325
+ /** @type {HookMap<SyncBailHook<[WriteStatement], boolean | void>>} */
1326
+ write: new HookMap(() => new SyncBailHook(["node"])),
1327
+ /** @type {HookMap<SyncBailHook<[WriteStatement, Members], boolean | void>>} */
1328
+ writeMemberChain: new HookMap(
1329
+ () => new SyncBailHook(["node", "members"])
1330
+ ),
684
1331
  /** @type {HookMap<SyncBailHook<[AssignmentExpression, Members], boolean | void>>} */
685
1332
  assignMemberChain: new HookMap(
686
1333
  () => new SyncBailHook(["expression", "members"])
@@ -790,6 +1437,37 @@ class JavascriptParser extends Parser {
790
1437
  this.statementPath = undefined;
791
1438
  /** @type {Statement | ModuleDeclaration | Expression | MaybeNamedFunctionDeclaration | MaybeNamedClassDeclaration | undefined} */
792
1439
  this.prevStatement = undefined;
1440
+ /** @type {boolean} */
1441
+ this._hasParserRecord = false;
1442
+ // BACKWARD-COMPAT START
1443
+
1444
+ // WHY: the hoisting pass reaches declarations alone, so a tap that
1445
+ // expects every statement would quietly stop seeing most of them. Such a
1446
+ // tap turns the pass back into the walk it used to be, which no build
1447
+ // webpack's own plugins drive ever asks for.
1448
+ /** @type {boolean} */
1449
+ this._needsFullPreWalk = false;
1450
+ this.hooks.preStatementByType.intercept({
1451
+ factory: (type, hook) => {
1452
+ if (!RECORDED_STATEMENT_TYPES.has(type)) {
1453
+ this._needsFullPreWalk = true;
1454
+ }
1455
+ return hook;
1456
+ }
1457
+ });
1458
+
1459
+ // The same for the block pass, over the types the parser records for it.
1460
+ /** @type {boolean} */
1461
+ this._needsFullBlockPreWalk = false;
1462
+ this.hooks.blockPreStatementByType.intercept({
1463
+ factory: (type, hook) => {
1464
+ if (!BLOCK_DECLARATION_TYPES.has(type)) {
1465
+ this._needsFullBlockPreWalk = true;
1466
+ }
1467
+ return hook;
1468
+ }
1469
+ });
1470
+ // BACKWARD-COMPAT END
793
1471
  /** @type {WeakMap<Expression, DestructuringAssignmentProperties> | undefined} */
794
1472
  this.destructuringAssignmentProperties = undefined;
795
1473
  /** @type {TagData | undefined} */
@@ -802,6 +1480,32 @@ class JavascriptParser extends Parser {
802
1480
  // Offset of each line's first character, built on first getLocation use.
803
1481
  /** @type {number[] | undefined} */
804
1482
  this._lineStarts = undefined;
1483
+ // The last member expression resolved, kept while the node and the scope
1484
+ // it resolved in stay the same: the walk and an evaluate of one node meet
1485
+ /** @type {Expression | Super | undefined} */
1486
+ this._memberExpression = undefined;
1487
+ /** @type {ScopeInfo | undefined} */
1488
+ this._memberScope = undefined;
1489
+ /** @type {number} */
1490
+ this._memberMask = 0;
1491
+ /** @type {0 | 1 | 2} */
1492
+ this._memberType = 0;
1493
+ /** @type {string} */
1494
+ this._memberName = "";
1495
+ /** @type {string | VariableInfo | undefined} */
1496
+ this._memberRootInfo = undefined;
1497
+ /** @type {Members} */
1498
+ this._members = EMPTY_ARRAY;
1499
+ /** @type {MembersOptionals} */
1500
+ this._membersOptionals = EMPTY_MEMBERS_OPTIONALS;
1501
+ /** @type {CallExpression | undefined} */
1502
+ this._memberCall = undefined;
1503
+ /** @type {string} */
1504
+ this._memberCalleeName = "";
1505
+ /** @type {CalleeMembers} */
1506
+ this._memberCalleeMembers = EMPTY_ARRAY;
1507
+ /** @type {CallExpressionInfo | ExpressionExpressionInfo | undefined} */
1508
+ this._memberInfo = undefined;
805
1509
  // True while only the parser's own taps sit on evaluate.for("Identifier");
806
1510
  // recomputed per parse, gates the defined-identifier fast paths.
807
1511
  this._evalIdentOwnTaps = false;
@@ -809,6 +1513,22 @@ class JavascriptParser extends Parser {
809
1513
  // Reused for enterPatterns on every scope entry to avoid per-scope closures.
810
1514
  /** @type {OnIdentString} */
811
1515
  this._defineVariable = (ident) => this.defineVariable(ident);
1516
+ // The expression an identifier evaluator is running for, read by the
1517
+ // shared `_evaluateDefinedIdentifier` instead of a closure per evaluation
1518
+ /** @type {Identifier | ThisExpression | MemberExpression | undefined} */
1519
+ this._evaluatingExpression = undefined;
1520
+ /** @type {(name?: string) => BasicEvaluatedExpression | null | undefined} */
1521
+ this._evaluateDefinedIdentifier = (name) => {
1522
+ const hook = this.hooks.evaluateDefinedIdentifier.get(
1523
+ /** @type {string} */ (name)
1524
+ );
1525
+ if (hook !== undefined) {
1526
+ return hook.call(
1527
+ /** @type {Identifier | ThisExpression | MemberExpression} */
1528
+ (this._evaluatingExpression)
1529
+ );
1530
+ }
1531
+ };
812
1532
  this._initializeEvaluating();
813
1533
  // keyed taps are free for every other member chain, and a shadowed
814
1534
  // `arguments` binding never dispatches the free-variable hooks
@@ -867,7 +1587,7 @@ class JavascriptParser extends Parser {
867
1587
  const callee = expr.callee;
868
1588
  if (callee.type !== "Identifier") return;
869
1589
  if (callee.name !== "RegExp") {
870
- return this.callHooksForName(
1590
+ return this._callHooksForName1(
871
1591
  this.hooks.evaluateNewExpression,
872
1592
  callee.name,
873
1593
  expr
@@ -979,278 +1699,10 @@ class JavascriptParser extends Parser {
979
1699
  }
980
1700
  });
981
1701
 
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
1702
  this.hooks.evaluate.for("BinaryExpression").tap(CLASS_NAME, (_expr) => {
1051
1703
  const expr = /** @type {BinaryExpression} */ (_expr);
1052
1704
 
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
- if (expr.operator === "+") {
1705
+ if (expr.operator === "+") {
1254
1706
  const left = this.evaluateExpression(expr.left);
1255
1707
  const right = this.evaluateExpression(expr.right);
1256
1708
  const res = new BasicEvaluatedExpression();
@@ -1416,41 +1868,41 @@ class JavascriptParser extends Parser {
1416
1868
  res.setRange(/** @type {Range} */ (expr.range));
1417
1869
  return res;
1418
1870
  } else if (expr.operator === "-") {
1419
- return handleConstOperation((l, r) => l - r);
1871
+ return handleConstOperation(this, expr, SUBTRACT);
1420
1872
  } else if (expr.operator === "*") {
1421
- return handleConstOperation((l, r) => l * r);
1873
+ return handleConstOperation(this, expr, MULTIPLY);
1422
1874
  } else if (expr.operator === "/") {
1423
- return handleConstOperation((l, r) => l / r);
1875
+ return handleConstOperation(this, expr, DIVIDE);
1424
1876
  } else if (expr.operator === "**") {
1425
- return handleConstOperation((l, r) => l ** r);
1877
+ return handleConstOperation(this, expr, POWER);
1426
1878
  } else if (expr.operator === "===") {
1427
- return handleStrictEqualityComparison(true);
1879
+ return handleStrictEqualityComparison(this, expr, true);
1428
1880
  } else if (expr.operator === "==") {
1429
- return handleAbstractEqualityComparison(true);
1881
+ return handleAbstractEqualityComparison(this, expr, true);
1430
1882
  } else if (expr.operator === "!==") {
1431
- return handleStrictEqualityComparison(false);
1883
+ return handleStrictEqualityComparison(this, expr, false);
1432
1884
  } else if (expr.operator === "!=") {
1433
- return handleAbstractEqualityComparison(false);
1885
+ return handleAbstractEqualityComparison(this, expr, false);
1434
1886
  } else if (expr.operator === "&") {
1435
- return handleConstOperation((l, r) => l & r);
1887
+ return handleConstOperation(this, expr, BITWISE_AND);
1436
1888
  } else if (expr.operator === "|") {
1437
- return handleConstOperation((l, r) => l | r);
1889
+ return handleConstOperation(this, expr, BITWISE_OR);
1438
1890
  } else if (expr.operator === "^") {
1439
- return handleConstOperation((l, r) => l ^ r);
1891
+ return handleConstOperation(this, expr, BITWISE_XOR);
1440
1892
  } else if (expr.operator === ">>>") {
1441
- return handleConstOperation((l, r) => l >>> r);
1893
+ return handleConstOperation(this, expr, UNSIGNED_SHIFT_RIGHT);
1442
1894
  } else if (expr.operator === ">>") {
1443
- return handleConstOperation((l, r) => l >> r);
1895
+ return handleConstOperation(this, expr, SHIFT_RIGHT);
1444
1896
  } else if (expr.operator === "<<") {
1445
- return handleConstOperation((l, r) => l << r);
1897
+ return handleConstOperation(this, expr, SHIFT_LEFT);
1446
1898
  } else if (expr.operator === "<") {
1447
- return handleConstOperation((l, r) => l < r);
1899
+ return handleConstOperation(this, expr, LESS_THAN);
1448
1900
  } else if (expr.operator === ">") {
1449
- return handleConstOperation((l, r) => l > r);
1901
+ return handleConstOperation(this, expr, GREATER_THAN);
1450
1902
  } else if (expr.operator === "<=") {
1451
- return handleConstOperation((l, r) => l <= r);
1903
+ return handleConstOperation(this, expr, LESS_THAN_OR_EQUAL);
1452
1904
  } else if (expr.operator === ">=") {
1453
- return handleConstOperation((l, r) => l >= r);
1905
+ return handleConstOperation(this, expr, GREATER_THAN_OR_EQUAL);
1454
1906
  } else if (expr.operator === "in") {
1455
1907
  // `x in y` always evaluates to a boolean, so it is never nullish
1456
1908
  const left = this.evaluateExpression(expr.left);
@@ -1466,25 +1918,10 @@ class JavascriptParser extends Parser {
1466
1918
  this.hooks.evaluate.for("UnaryExpression").tap(CLASS_NAME, (_expr) => {
1467
1919
  const expr = /** @type {UnaryExpression} */ (_expr);
1468
1920
 
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
1921
  if (expr.operator === "typeof") {
1485
1922
  switch (expr.argument.type) {
1486
1923
  case "Identifier": {
1487
- const res = this.callHooksForName(
1924
+ const res = this._callHooksForName1(
1488
1925
  this.hooks.evaluateTypeof,
1489
1926
  expr.argument.name,
1490
1927
  expr
@@ -1493,7 +1930,7 @@ class JavascriptParser extends Parser {
1493
1930
  break;
1494
1931
  }
1495
1932
  case "MetaProperty": {
1496
- const res = this.callHooksForName(
1933
+ const res = this._callHooksForName1(
1497
1934
  this.hooks.evaluateTypeof,
1498
1935
  /** @type {string} */
1499
1936
  (getRootName(expr.argument)),
@@ -1579,12 +2016,11 @@ class JavascriptParser extends Parser {
1579
2016
  .setSideEffects(argument.couldHaveSideEffects())
1580
2017
  .setRange(/** @type {Range} */ (expr.range));
1581
2018
  } else if (expr.operator === "~") {
1582
- return handleConstOperation((v) => ~v);
2019
+ return handleUnaryConstOperation(this, expr, BITWISE_NOT);
1583
2020
  } else if (expr.operator === "+") {
1584
- // eslint-disable-next-line no-implicit-coercion
1585
- return handleConstOperation((v) => +v);
2021
+ return handleUnaryConstOperation(this, expr, TO_NUMBER);
1586
2022
  } else if (expr.operator === "-") {
1587
- return handleConstOperation((v) => -v);
2023
+ return handleUnaryConstOperation(this, expr, NEGATE);
1588
2024
  }
1589
2025
  });
1590
2026
  this.hooks.evaluateTypeof
@@ -1623,18 +2059,18 @@ class JavascriptParser extends Parser {
1623
2059
  cachedExpression = expression;
1624
2060
  cachedInfo = info;
1625
2061
  if (info !== undefined) {
1626
- return this.callHooksForInfoWithFallback(
2062
+ // saved around the call: a hook may evaluate another identifier
2063
+ const outer = this._evaluatingExpression;
2064
+ this._evaluatingExpression = expression;
2065
+ const result = this.callHooksForInfoWithFallback(
1627
2066
  this.hooks.evaluateIdentifier,
1628
2067
  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
- },
2068
+ RETURN_UNDEFINED,
2069
+ this._evaluateDefinedIdentifier,
1636
2070
  expression
1637
2071
  );
2072
+ this._evaluatingExpression = outer;
2073
+ return result;
1638
2074
  }
1639
2075
  });
1640
2076
  this.hooks.evaluate
@@ -1699,7 +2135,7 @@ class JavascriptParser extends Parser {
1699
2135
  this.hooks.evaluate.for("MetaProperty").tap(CLASS_NAME, (expr) => {
1700
2136
  const metaProperty = /** @type {MetaProperty} */ (expr);
1701
2137
 
1702
- return this.callHooksForName(
2138
+ return this._callHooksForName1(
1703
2139
  this.hooks.evaluateIdentifier,
1704
2140
  /** @type {string} */
1705
2141
  (getRootName(metaProperty)),
@@ -1733,7 +2169,7 @@ class JavascriptParser extends Parser {
1733
2169
  return hook.call(expr, param);
1734
2170
  }
1735
2171
  } else if (expr.callee.type === "Identifier") {
1736
- return this.callHooksForName(
2172
+ return this._callHooksForName1(
1737
2173
  this.hooks.evaluateCallExpression,
1738
2174
  expr.callee.name,
1739
2175
  expr
@@ -2209,12 +2645,19 @@ class JavascriptParser extends Parser {
2209
2645
  }
2210
2646
 
2211
2647
  /**
2212
- * Module pre walking iterates the scope for import entries
2213
- * @param {(Statement | ModuleDeclaration)[]} statements statements
2648
+ * Report what the program imports and re-exports, before anything reads a
2649
+ * name. The list is what the parser recorded, in source order.
2650
+ * @param {ModuleDeclarations} declarations the program's module declarations
2214
2651
  */
2215
- modulePreWalkStatements(statements) {
2216
- for (let index = 0, len = statements.length; index < len; index++) {
2217
- const statement = statements[index];
2652
+ _modulePreWalkDeclarations(declarations) {
2653
+ for (let index = 0, len = declarations.length; index < len; index += 2) {
2654
+ const statement =
2655
+ /** @type {ModuleDeclaration} */
2656
+ (declarations[index]);
2657
+ // what precedes it decides whether removing it joins two statements
2658
+ this.prevStatement =
2659
+ /** @type {Statement | undefined} */
2660
+ (declarations[index + 1]);
2218
2661
  /** @type {StatementPath} */
2219
2662
  (this.statementPath).push(statement);
2220
2663
  switch (statement.type) {
@@ -2235,19 +2678,212 @@ class JavascriptParser extends Parser {
2235
2678
  }
2236
2679
 
2237
2680
  /**
2238
- * Pre walking iterates the scope for variable declarations
2681
+ * Declare what the scope the node opens hoists, before anything reads a
2682
+ * name in it.
2683
+ * @param {Program | BlockStatement} node the node owning the scope
2684
+ */
2685
+ _preWalkScope(node) {
2686
+ // BACKWARD-COMPAT START
2687
+ if (this._needsFullPreWalk || this.hooks.preStatement.taps.length !== 0) {
2688
+ if (node.type === "Program") this.preWalkStatements(node.body);
2689
+ else this.preWalkStatement(node);
2690
+ return;
2691
+ }
2692
+ // BACKWARD-COMPAT END
2693
+ this._preWalkHoistedDeclarations(
2694
+ hoistedDeclarationsOf(node, this._hasParserRecord)
2695
+ );
2696
+ }
2697
+
2698
+ /**
2699
+ * Declare what hoists to the scope being entered, from what the parser
2700
+ * recorded while reading it. `prevStatement` is not available here.
2701
+ * @param {HoistedDeclarations} hoisted what the scope declares
2702
+ */
2703
+ _preWalkHoistedDeclarations(hoisted) {
2704
+ for (let index = 0, len = hoisted.length; index < len; index++) {
2705
+ // WHY: the statements between two declarations are not read here, so
2706
+ // the one before this declaration is unknown rather than the previous
2707
+ // declaration. A tap needing it taps `preStatement`, which walks.
2708
+ this.prevStatement = undefined;
2709
+ this.preWalkStatement(hoisted[index]);
2710
+ }
2711
+ }
2712
+
2713
+ // BACKWARD-COMPAT START
2714
+ /**
2715
+ * Walk every statement of a scope to declare what hoists to it. Only a tap
2716
+ * on the deprecated `preStatement` hook asks for this.
2239
2717
  * @param {(Statement | ModuleDeclaration)[]} statements statements
2718
+ * @deprecated TODO webpack 6: remove, with the `preStatement` hook
2240
2719
  */
2241
2720
  preWalkStatements(statements) {
2721
+ for (let index = 0, len = statements.length; index < len; index++) {
2722
+ this.preWalkStatement(statements[index]);
2723
+ }
2724
+ }
2725
+
2726
+ /**
2727
+ * Pre walk block statement.
2728
+ * @param {BlockStatement} statement block statement
2729
+ * @deprecated TODO webpack 6: remove, with the `preStatement` hook
2730
+ */
2731
+ preWalkBlockStatement(statement) {
2732
+ this.preWalkStatements(statement.body);
2733
+ }
2734
+
2735
+ /**
2736
+ * Pre walk if statement.
2737
+ * @param {IfStatement} statement if statement
2738
+ * @deprecated TODO webpack 6: remove, with the `preStatement` hook
2739
+ */
2740
+ preWalkIfStatement(statement) {
2741
+ this.preWalkStatement(statement.consequent);
2742
+ if (statement.alternate) {
2743
+ this.preWalkStatement(statement.alternate);
2744
+ }
2745
+ }
2746
+
2747
+ /**
2748
+ * Pre walk labeled statement.
2749
+ * @param {LabeledStatement} statement labeled statement
2750
+ * @deprecated TODO webpack 6: remove, with the `preStatement` hook
2751
+ */
2752
+ preWalkLabeledStatement(statement) {
2753
+ this.preWalkStatement(statement.body);
2754
+ }
2755
+
2756
+ /**
2757
+ * Pre walk with statement.
2758
+ * @param {WithStatement} statement with statement
2759
+ * @deprecated TODO webpack 6: remove, with the `preStatement` hook
2760
+ */
2761
+ preWalkWithStatement(statement) {
2762
+ this.preWalkStatement(statement.body);
2763
+ }
2764
+
2765
+ /**
2766
+ * Pre walk switch statement.
2767
+ * @param {SwitchStatement} statement switch statement
2768
+ * @deprecated TODO webpack 6: remove, with the `preStatement` hook
2769
+ */
2770
+ preWalkSwitchStatement(statement) {
2771
+ this.preWalkSwitchCases(statement.cases);
2772
+ }
2773
+
2774
+ /**
2775
+ * Pre walk switch cases.
2776
+ * @param {SwitchCase[]} switchCases switch cases
2777
+ * @deprecated TODO webpack 6: remove, with the `preStatement` hook
2778
+ */
2779
+ preWalkSwitchCases(switchCases) {
2780
+ for (let index = 0, len = switchCases.length; index < len; index++) {
2781
+ this.preWalkStatements(switchCases[index].consequent);
2782
+ }
2783
+ }
2784
+
2785
+ /**
2786
+ * Pre walk try statement.
2787
+ * @param {TryStatement} statement try statement
2788
+ * @deprecated TODO webpack 6: remove, with the `preStatement` hook
2789
+ */
2790
+ preWalkTryStatement(statement) {
2791
+ this.preWalkStatement(statement.block);
2792
+ if (statement.handler) this.preWalkCatchClause(statement.handler);
2793
+ if (statement.finalizer) this.preWalkStatement(statement.finalizer);
2794
+ }
2795
+
2796
+ /**
2797
+ * Pre walk catch clause.
2798
+ * @param {CatchClause} catchClause catch clause
2799
+ * @deprecated TODO webpack 6: remove, with the `preStatement` hook
2800
+ */
2801
+ preWalkCatchClause(catchClause) {
2802
+ this.preWalkStatement(catchClause.body);
2803
+ }
2804
+
2805
+ /**
2806
+ * Pre walk while statement.
2807
+ * @param {WhileStatement} statement while statement
2808
+ * @deprecated TODO webpack 6: remove, with the `preStatement` hook
2809
+ */
2810
+ preWalkWhileStatement(statement) {
2811
+ this.preWalkStatement(statement.body);
2812
+ }
2813
+
2814
+ /**
2815
+ * Pre walk do while statement.
2816
+ * @param {DoWhileStatement} statement do while statement
2817
+ * @deprecated TODO webpack 6: remove, with the `preStatement` hook
2818
+ */
2819
+ preWalkDoWhileStatement(statement) {
2820
+ this.preWalkStatement(statement.body);
2821
+ }
2822
+
2823
+ /**
2824
+ * Pre walk for statement.
2825
+ * @param {ForStatement} statement for statement
2826
+ * @deprecated TODO webpack 6: remove, with the `preStatement` hook
2827
+ */
2828
+ preWalkForStatement(statement) {
2829
+ if (statement.init && statement.init.type === "VariableDeclaration") {
2830
+ this.preWalkStatement(statement.init);
2831
+ }
2832
+ this.preWalkStatement(statement.body);
2833
+ }
2834
+
2835
+ /**
2836
+ * Pre walk for in statement.
2837
+ * @param {ForInStatement} statement for in statement
2838
+ * @deprecated TODO webpack 6: remove, with the `preStatement` hook
2839
+ */
2840
+ preWalkForInStatement(statement) {
2841
+ if (statement.left.type === "VariableDeclaration") {
2842
+ this.preWalkStatement(statement.left);
2843
+ }
2844
+ this.preWalkStatement(statement.body);
2845
+ }
2846
+
2847
+ /**
2848
+ * Pre walk for of statement. `for await` is reported by the walk, not here.
2849
+ * @param {ForOfStatement} statement for of statement
2850
+ * @deprecated TODO webpack 6: remove, with the `preStatement` hook
2851
+ */
2852
+ preWalkForOfStatement(statement) {
2853
+ if (statement.left.type === "VariableDeclaration") {
2854
+ this.preWalkStatement(statement.left);
2855
+ }
2856
+ this.preWalkStatement(statement.body);
2857
+ }
2858
+
2859
+ /**
2860
+ * Scan every top-level statement for the program's module declarations.
2861
+ * The parser records them now, so nothing inside webpack scans for them.
2862
+ * @deprecated TODO webpack 6: remove
2863
+ * @param {(Statement | ModuleDeclaration)[]} statements statements
2864
+ */
2865
+ modulePreWalkStatements(statements) {
2866
+ /** @type {ModuleDeclarations} */
2867
+ const declarations = [];
2242
2868
  for (let index = 0, len = statements.length; index < len; index++) {
2243
2869
  const statement = statements[index];
2244
- this.preWalkStatement(statement);
2870
+ if (MODULE_STATEMENT_TYPES.has(statement.type)) {
2871
+ declarations.push(
2872
+ statement,
2873
+ index === 0 ? undefined : statements[index - 1]
2874
+ );
2875
+ }
2245
2876
  }
2877
+ this._modulePreWalkDeclarations(declarations);
2246
2878
  }
2879
+ // BACKWARD-COMPAT END
2247
2880
 
2881
+ // BACKWARD-COMPAT START
2248
2882
  /**
2249
- * Block pre walking iterates the scope for block variable declarations
2883
+ * Walk every statement of a list to declare what it binds. Only a tap on the
2884
+ * deprecated `blockPreStatement` hook asks for this.
2250
2885
  * @param {(Statement | ModuleDeclaration)[]} statements statements
2886
+ * @deprecated TODO webpack 6: remove, with the `blockPreStatement` hook
2251
2887
  */
2252
2888
  blockPreWalkStatements(statements) {
2253
2889
  for (let index = 0, len = statements.length; index < len; index++) {
@@ -2255,6 +2891,68 @@ class JavascriptParser extends Parser {
2255
2891
  this.blockPreWalkStatement(statement);
2256
2892
  }
2257
2893
  }
2894
+ // BACKWARD-COMPAT END
2895
+
2896
+ /**
2897
+ * Declare what the statement list binds into its own block scope, before any
2898
+ * of it is walked. The list is what the parser recorded while reading it.
2899
+ * @param {Program | BlockStatement | StaticBlock | SwitchCase} node the node owning the list
2900
+ * @param {(Statement | ModuleDeclaration)[]} statements the list it holds
2901
+ */
2902
+ _blockPreWalkScope(node, statements) {
2903
+ // BACKWARD-COMPAT START
2904
+ if (
2905
+ this._needsFullBlockPreWalk ||
2906
+ this.hooks.blockPreStatement.taps.length !== 0
2907
+ ) {
2908
+ this.blockPreWalkStatements(statements);
2909
+ return;
2910
+ }
2911
+ // BACKWARD-COMPAT END
2912
+ /** @type {BlockDeclarations | undefined} */
2913
+ let recorded;
2914
+ if (this._hasParserRecord) {
2915
+ recorded = /** @type {EXPECTED_ANY} */ (node)[BLOCK_DECLARATIONS];
2916
+ // 72.8% of the lists a build reads declare nothing, and saying so is
2917
+ // the whole pass for them
2918
+ if (recorded === 0) return;
2919
+ }
2920
+ if (recorded === undefined) {
2921
+ recorded = collectBlockDeclarations(statements);
2922
+ if (recorded === 0) return;
2923
+ }
2924
+ this._blockPreWalkDeclarations(statements, recorded);
2925
+ }
2926
+
2927
+ /**
2928
+ * Declare what the recorded statements of the list bind, in source order.
2929
+ * @param {(Statement | ModuleDeclaration)[]} statements the whole list
2930
+ * @param {BlockDeclarations} declarations which of them declare
2931
+ */
2932
+ _blockPreWalkDeclarations(statements, declarations) {
2933
+ if (typeof declarations === "number") {
2934
+ for (let bits = declarations; bits !== 0; bits &= bits - 1) {
2935
+ this._blockPreWalkAt(statements, 31 - Math.clz32(bits & -bits));
2936
+ }
2937
+ return;
2938
+ }
2939
+ for (let index = 0, len = declarations.length; index < len; index++) {
2940
+ this._blockPreWalkAt(statements, declarations[index]);
2941
+ }
2942
+ }
2943
+
2944
+ /**
2945
+ * Declare what one statement of the list binds, with the statement written
2946
+ * before it, so a tap reading `prevStatement` sees what the walk would show.
2947
+ * @param {(Statement | ModuleDeclaration)[]} statements the whole list
2948
+ * @param {number} index which statement declares
2949
+ */
2950
+ _blockPreWalkAt(statements, index) {
2951
+ // the first statement of the list has nothing before it, and leaves
2952
+ // whatever the caller set standing, as walking the whole list did
2953
+ if (index !== 0) this.prevStatement = statements[index - 1];
2954
+ this.blockPreWalkStatement(statements[index]);
2955
+ }
2258
2956
 
2259
2957
  /**
2260
2958
  * Walking iterates the statements and expressions and processes them
@@ -2304,21 +3002,22 @@ class JavascriptParser extends Parser {
2304
3002
  (this.statementPath).pop();
2305
3003
  return;
2306
3004
  }
2307
- // cases ordered by measured statement frequency; the commonest statements
2308
- // (expressions, returns) intentionally match nothing here
3005
+ // a class declaration reaches here for the typed hook alone: defining it
3006
+ // is the block pass's. The rest are only walked into by the deprecated
3007
+ // statement walk, which a tap expecting every statement turns back on.
2309
3008
  switch (statement.type) {
2310
3009
  case "VariableDeclaration":
2311
3010
  this.preWalkVariableDeclaration(statement);
2312
3011
  break;
3012
+ case "FunctionDeclaration":
3013
+ this.preWalkFunctionDeclaration(statement);
3014
+ break;
2313
3015
  case "IfStatement":
2314
3016
  this.preWalkIfStatement(statement);
2315
3017
  break;
2316
3018
  case "BlockStatement":
2317
3019
  this.preWalkBlockStatement(statement);
2318
3020
  break;
2319
- case "FunctionDeclaration":
2320
- this.preWalkFunctionDeclaration(statement);
2321
- break;
2322
3021
  case "ForStatement":
2323
3022
  this.preWalkForStatement(statement);
2324
3023
  break;
@@ -2491,26 +3190,20 @@ class JavascriptParser extends Parser {
2491
3190
  }
2492
3191
 
2493
3192
  // Real Statements
2494
- /**
2495
- * Pre walk block statement.
2496
- * @param {BlockStatement} statement block statement
2497
- */
2498
- preWalkBlockStatement(statement) {
2499
- this.preWalkStatements(statement.body);
2500
- }
2501
3193
 
2502
3194
  /**
2503
3195
  * Walk block statement.
2504
3196
  * @param {BlockStatement | StaticBlock} statement block statement
2505
3197
  */
2506
3198
  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);
3199
+ const oldScope = this._enterBlockScope();
3200
+ const body = statement.body;
3201
+ const prev = this.prevStatement;
3202
+ this._blockPreWalkScope(statement, body);
3203
+ this.prevStatement = prev;
3204
+ this.walkStatements(body);
3205
+
3206
+ this._exitBlockScope(oldScope, true);
2514
3207
  }
2515
3208
 
2516
3209
  /**
@@ -2521,17 +3214,6 @@ class JavascriptParser extends Parser {
2521
3214
  this.walkExpression(statement.expression);
2522
3215
  }
2523
3216
 
2524
- /**
2525
- * Pre walk if statement.
2526
- * @param {IfStatement} statement if statement
2527
- */
2528
- preWalkIfStatement(statement) {
2529
- this.preWalkStatement(statement.consequent);
2530
- if (statement.alternate) {
2531
- this.preWalkStatement(statement.alternate);
2532
- }
2533
- }
2534
-
2535
3217
  /**
2536
3218
  * Walks a conditional branch with its guard frame (if any) pushed onto the
2537
3219
  * parser-state guard stack for the duration of the branch body.
@@ -2562,18 +3244,27 @@ class JavascriptParser extends Parser {
2562
3244
  if (result === undefined) {
2563
3245
  const guard = this.hooks.collectGuards.call(statement.test);
2564
3246
  this.walkExpression(statement.test);
2565
- this.walkGuardedBranch(guard ? guard.consequent : undefined, () =>
2566
- this.walkNestedStatement(statement.consequent)
2567
- );
3247
+ // allocate the branch closure only when a guard frame needs pushing
3248
+ if (guard && guard.consequent) {
3249
+ this.walkGuardedBranch(guard.consequent, () =>
3250
+ this.walkNestedStatement(statement.consequent)
3251
+ );
3252
+ } else {
3253
+ this.walkNestedStatement(statement.consequent);
3254
+ }
2568
3255
 
2569
3256
  const consequentTerminated = this.scope.terminated;
2570
3257
  this.scope.terminated = undefined;
2571
3258
 
2572
3259
  if (statement.alternate) {
2573
3260
  const alternate = statement.alternate;
2574
- this.walkGuardedBranch(guard ? guard.alternate : undefined, () =>
2575
- this.walkNestedStatement(alternate)
2576
- );
3261
+ if (guard && guard.alternate) {
3262
+ this.walkGuardedBranch(guard.alternate, () =>
3263
+ this.walkNestedStatement(alternate)
3264
+ );
3265
+ } else {
3266
+ this.walkNestedStatement(alternate);
3267
+ }
2577
3268
  }
2578
3269
 
2579
3270
  const alternateTerminated = this.scope.terminated;
@@ -2589,14 +3280,6 @@ class JavascriptParser extends Parser {
2589
3280
  }
2590
3281
  }
2591
3282
 
2592
- /**
2593
- * Pre walk labeled statement.
2594
- * @param {LabeledStatement} statement with statement
2595
- */
2596
- preWalkLabeledStatement(statement) {
2597
- this.preWalkStatement(statement.body);
2598
- }
2599
-
2600
3283
  /**
2601
3284
  * Walk labeled statement.
2602
3285
  * @param {LabeledStatement} statement with statement
@@ -2607,17 +3290,10 @@ class JavascriptParser extends Parser {
2607
3290
  const result = hook.call(statement);
2608
3291
  if (result === true) return;
2609
3292
  }
2610
- this.inBlockScope(() => {
2611
- this.walkNestedStatement(statement.body);
2612
- });
2613
- }
3293
+ const oldScope = this._enterBlockScope();
3294
+ this.walkNestedStatement(statement.body);
2614
3295
 
2615
- /**
2616
- * Pre walk with statement.
2617
- * @param {WithStatement} statement with statement
2618
- */
2619
- preWalkWithStatement(statement) {
2620
- this.preWalkStatement(statement.body);
3296
+ this._exitBlockScope(oldScope, false);
2621
3297
  }
2622
3298
 
2623
3299
  /**
@@ -2631,18 +3307,11 @@ class JavascriptParser extends Parser {
2631
3307
  statement
2632
3308
  );
2633
3309
  }
2634
- this.inBlockScope(() => {
2635
- this.walkExpression(statement.object);
2636
- this.walkNestedStatement(statement.body);
2637
- });
2638
- }
3310
+ const oldScope = this._enterBlockScope();
3311
+ this.walkExpression(statement.object);
3312
+ this.walkNestedStatement(statement.body);
2639
3313
 
2640
- /**
2641
- * Pre walk switch statement.
2642
- * @param {SwitchStatement} statement switch statement
2643
- */
2644
- preWalkSwitchStatement(statement) {
2645
- this.preWalkSwitchCases(statement.cases);
3314
+ this._exitBlockScope(oldScope, false);
2646
3315
  }
2647
3316
 
2648
3317
  /**
@@ -2686,16 +3355,6 @@ class JavascriptParser extends Parser {
2686
3355
  this.walkTerminatingStatement(statement);
2687
3356
  }
2688
3357
 
2689
- /**
2690
- * Pre walk try statement.
2691
- * @param {TryStatement} statement try statement
2692
- */
2693
- preWalkTryStatement(statement) {
2694
- this.preWalkStatement(statement.block);
2695
- if (statement.handler) this.preWalkCatchClause(statement.handler);
2696
- if (statement.finalizer) this.preWalkStatement(statement.finalizer);
2697
- }
2698
-
2699
3358
  /**
2700
3359
  * Walk try statement.
2701
3360
  * @param {TryStatement} statement try statement
@@ -2734,53 +3393,28 @@ class JavascriptParser extends Parser {
2734
3393
  }
2735
3394
  }
2736
3395
 
2737
- /**
2738
- * Pre walk while statement.
2739
- * @param {WhileStatement} statement while statement
2740
- */
2741
- preWalkWhileStatement(statement) {
2742
- this.preWalkStatement(statement.body);
2743
- }
2744
-
2745
3396
  /**
2746
3397
  * Walk while statement.
2747
3398
  * @param {WhileStatement} statement while statement
2748
3399
  */
2749
3400
  walkWhileStatement(statement) {
2750
- this.inBlockScope(() => {
2751
- this.walkExpression(statement.test);
2752
- this.walkNestedStatement(statement.body);
2753
- });
2754
- }
3401
+ const oldScope = this._enterBlockScope();
3402
+ this.walkExpression(statement.test);
3403
+ this.walkNestedStatement(statement.body);
2755
3404
 
2756
- /**
2757
- * Pre walk do while statement.
2758
- * @param {DoWhileStatement} statement do while statement
2759
- */
2760
- preWalkDoWhileStatement(statement) {
2761
- this.preWalkStatement(statement.body);
2762
- }
2763
-
2764
- /**
2765
- * Walk do while statement.
2766
- * @param {DoWhileStatement} statement do while statement
2767
- */
2768
- walkDoWhileStatement(statement) {
2769
- this.inBlockScope(() => {
2770
- this.walkNestedStatement(statement.body);
2771
- this.walkExpression(statement.test);
2772
- });
3405
+ this._exitBlockScope(oldScope, false);
2773
3406
  }
2774
3407
 
2775
- /**
2776
- * Pre walk for statement.
2777
- * @param {ForStatement} statement for statement
2778
- */
2779
- preWalkForStatement(statement) {
2780
- if (statement.init && statement.init.type === "VariableDeclaration") {
2781
- this.preWalkStatement(statement.init);
2782
- }
2783
- this.preWalkStatement(statement.body);
3408
+ /**
3409
+ * Walk do while statement.
3410
+ * @param {DoWhileStatement} statement do while statement
3411
+ */
3412
+ walkDoWhileStatement(statement) {
3413
+ const oldScope = this._enterBlockScope();
3414
+ this.walkNestedStatement(statement.body);
3415
+ this.walkExpression(statement.test);
3416
+
3417
+ this._exitBlockScope(oldScope, false);
2784
3418
  }
2785
3419
 
2786
3420
  /**
@@ -2788,46 +3422,36 @@ class JavascriptParser extends Parser {
2788
3422
  * @param {ForStatement} statement for statement
2789
3423
  */
2790
3424
  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);
2806
- }
2807
-
2808
- const body = statement.body;
2809
-
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);
3425
+ const oldScope = this._enterBlockScope();
3426
+ if (statement.init) {
3427
+ if (statement.init.type === "VariableDeclaration") {
3428
+ this.blockPreWalkVariableDeclaration(statement.init);
3429
+ this.prevStatement = undefined;
3430
+ this.walkStatement(statement.init);
2816
3431
  } else {
2817
- this.walkNestedStatement(body);
3432
+ this.walkExpression(statement.init);
2818
3433
  }
2819
- });
2820
- }
3434
+ }
3435
+ if (statement.test) {
3436
+ this.walkExpression(statement.test);
3437
+ }
3438
+ if (statement.update) {
3439
+ this.walkExpression(statement.update);
3440
+ }
2821
3441
 
2822
- /**
2823
- * Pre walk for in statement.
2824
- * @param {ForInStatement} statement for statement
2825
- */
2826
- preWalkForInStatement(statement) {
2827
- if (statement.left.type === "VariableDeclaration") {
2828
- this.preWalkVariableDeclaration(statement.left);
3442
+ const body = statement.body;
3443
+
3444
+ if (body.type === "BlockStatement") {
3445
+ // no need to add additional scope
3446
+ const prev = this.prevStatement;
3447
+ this._blockPreWalkScope(body, body.body);
3448
+ this.prevStatement = prev;
3449
+ this.walkStatements(body.body);
3450
+ } else {
3451
+ this.walkNestedStatement(body);
2829
3452
  }
2830
- this.preWalkStatement(statement.body);
3453
+
3454
+ this._exitBlockScope(oldScope, false);
2831
3455
  }
2832
3456
 
2833
3457
  /**
@@ -2835,71 +3459,62 @@ class JavascriptParser extends Parser {
2835
3459
  * @param {ForInStatement} statement for statement
2836
3460
  */
2837
3461
  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
- }
3462
+ const oldScope = this._enterBlockScope();
3463
+ if (statement.left.type === "VariableDeclaration") {
3464
+ this.blockPreWalkVariableDeclaration(statement.left);
3465
+ this.walkVariableDeclaration(statement.left);
3466
+ } else if (!this._walkWriteTarget(statement.left, statement)) {
3467
+ this.walkPattern(statement.left);
3468
+ }
2845
3469
 
2846
- this.walkExpression(statement.right);
3470
+ this.walkExpression(statement.right);
2847
3471
 
2848
- const body = statement.body;
3472
+ const body = statement.body;
2849
3473
 
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
- });
3474
+ if (body.type === "BlockStatement") {
3475
+ // no need to add additional scope
3476
+ const prev = this.prevStatement;
3477
+ this._blockPreWalkScope(body, body.body);
3478
+ this.prevStatement = prev;
3479
+ this.walkStatements(body.body);
3480
+ } else {
3481
+ this.walkNestedStatement(body);
3482
+ }
3483
+
3484
+ this._exitBlockScope(oldScope, false);
2860
3485
  }
2861
3486
 
2862
3487
  /**
2863
- * Pre walk for of statement.
2864
- * @param {ForOfStatement} statement statement
3488
+ * Walk for of statement.
3489
+ * @param {ForOfStatement} statement for statement
2865
3490
  */
2866
- preWalkForOfStatement(statement) {
3491
+ walkForOfStatement(statement) {
2867
3492
  if (statement.await && this.scope.topLevelScope === true) {
2868
3493
  this.hooks.topLevelAwait.call(statement);
2869
3494
  }
3495
+ const oldScope = this._enterBlockScope();
2870
3496
  if (statement.left.type === "VariableDeclaration") {
2871
- this.preWalkVariableDeclaration(statement.left);
3497
+ this.blockPreWalkVariableDeclaration(statement.left);
3498
+ this.walkVariableDeclaration(statement.left);
3499
+ } else if (!this._walkWriteTarget(statement.left, statement)) {
3500
+ this.walkPattern(statement.left);
2872
3501
  }
2873
- this.preWalkStatement(statement.body);
2874
- }
2875
3502
 
2876
- /**
2877
- * Walk for of statement.
2878
- * @param {ForOfStatement} statement for statement
2879
- */
2880
- 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
- }
3503
+ this.walkExpression(statement.right);
2888
3504
 
2889
- this.walkExpression(statement.right);
3505
+ const body = statement.body;
2890
3506
 
2891
- const body = statement.body;
3507
+ if (body.type === "BlockStatement") {
3508
+ // no need to add additional scope
3509
+ const prev = this.prevStatement;
3510
+ this._blockPreWalkScope(body, body.body);
3511
+ this.prevStatement = prev;
3512
+ this.walkStatements(body.body);
3513
+ } else {
3514
+ this.walkNestedStatement(body);
3515
+ }
2892
3516
 
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
- });
3517
+ this._exitBlockScope(oldScope, false);
2903
3518
  }
2904
3519
 
2905
3520
  /**
@@ -2925,19 +3540,20 @@ class JavascriptParser extends Parser {
2925
3540
  if (this._strictInModuleOutput) {
2926
3541
  this._checkStrictModeParams(statement.params);
2927
3542
  }
2928
- this.inFunctionScope(true, statement.params, () => {
2929
- for (const param of statement.params) {
2930
- this.walkPattern(param);
2931
- }
3543
+ const oldScope = this._enterFunctionScope(true, statement.params);
3544
+ for (const param of statement.params) {
3545
+ this.walkPattern(param);
3546
+ }
2932
3547
 
2933
- this.detectMode(statement.body.body);
3548
+ this.detectMode(statement.body.body);
2934
3549
 
2935
- const prev = this.prevStatement;
3550
+ const prev = this.prevStatement;
2936
3551
 
2937
- this.preWalkStatement(statement.body);
2938
- this.prevStatement = prev;
2939
- this.walkStatement(statement.body);
2940
- });
3552
+ this._preWalkScope(statement.body);
3553
+ this.prevStatement = prev;
3554
+ this.walkStatement(statement.body);
3555
+
3556
+ this.scope = oldScope;
2941
3557
  this.scope.topLevelScope = wasTopLevel;
2942
3558
  }
2943
3559
 
@@ -3303,7 +3919,9 @@ class JavascriptParser extends Parser {
3303
3919
  /** @type {OnIdent | undefined} */
3304
3920
  let onIdent;
3305
3921
  const checkStrictBindings = this._strictInModuleOutput;
3306
- for (const declarator of statement.declarations) {
3922
+ const declarations = statement.declarations;
3923
+ for (let i = 0, len = declarations.length; i < len; i++) {
3924
+ const declarator = declarations[i];
3307
3925
  if (declarator.type !== "VariableDeclarator") continue;
3308
3926
  this.preWalkVariableDeclarator(declarator);
3309
3927
  if (this.hooks.preDeclarator.call(declarator, statement)) continue;
@@ -3312,7 +3930,7 @@ class JavascriptParser extends Parser {
3312
3930
  if (checkStrictBindings) this._checkStrictModeBinding(id.name, id);
3313
3931
  // fast path: plain `const x =` skips the enterPattern dispatch and
3314
3932
  // the per-declaration onIdent closure (only patterns need it)
3315
- if (!this.callHooksForName(this.hooks.pattern, id.name, id)) {
3933
+ if (!this._callHooksForName1(this.hooks.pattern, id.name, id)) {
3316
3934
  this._defineVariableForDeclaration(id.name, id, hookMap);
3317
3935
  }
3318
3936
  } else {
@@ -3452,7 +4070,9 @@ class JavascriptParser extends Parser {
3452
4070
  * @param {VariableDeclaration} statement variable declaration
3453
4071
  */
3454
4072
  walkVariableDeclaration(statement) {
3455
- for (const declarator of statement.declarations) {
4073
+ const declarations = statement.declarations;
4074
+ for (let i = 0, len = declarations.length; i < len; i++) {
4075
+ const declarator = declarations[i];
3456
4076
  switch (declarator.type) {
3457
4077
  case "VariableDeclarator": {
3458
4078
  const renameIdentifier =
@@ -3507,64 +4127,40 @@ class JavascriptParser extends Parser {
3507
4127
  this.walkClass(statement);
3508
4128
  }
3509
4129
 
3510
- /**
3511
- * Pre walk switch cases.
3512
- * @param {SwitchCase[]} switchCases switch statement
3513
- */
3514
- preWalkSwitchCases(switchCases) {
3515
- for (let index = 0, len = switchCases.length; index < len; index++) {
3516
- const switchCase = switchCases[index];
3517
- this.preWalkStatements(switchCase.consequent);
3518
- }
3519
- }
3520
-
3521
4130
  /**
3522
4131
  * Processes the provided switch case.
3523
4132
  * @param {SwitchCase[]} switchCases switch statement
3524
4133
  */
3525
4134
  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
- }
4135
+ const oldScope = this._enterBlockScope();
4136
+ const len = switchCases.length;
4137
+
4138
+ // pre-walk every case first: a `const` declared in a later case is already
4139
+ // in scope for an earlier case that reads it (and fails at runtime)
4140
+ for (let index = 0; index < len; index++) {
4141
+ const switchCase = switchCases[index];
4142
+
4143
+ if (switchCase.consequent.length > 0) {
4144
+ const prev = this.prevStatement;
4145
+ this._blockPreWalkScope(switchCase, switchCase.consequent);
4146
+ this.prevStatement = prev;
3545
4147
  }
4148
+ }
3546
4149
 
3547
- for (let index = 0; index < len; index++) {
3548
- const switchCase = switchCases[index];
4150
+ for (let index = 0; index < len; index++) {
4151
+ const switchCase = switchCases[index];
3549
4152
 
3550
- if (switchCase.test) {
3551
- this.walkExpression(switchCase.test);
3552
- }
4153
+ if (switchCase.test) {
4154
+ this.walkExpression(switchCase.test);
4155
+ }
3553
4156
 
3554
- if (switchCase.consequent.length > 0) {
3555
- this.walkStatements(switchCase.consequent);
3556
- this.scope.terminated = undefined;
3557
- }
4157
+ if (switchCase.consequent.length > 0) {
4158
+ this.walkStatements(switchCase.consequent);
4159
+ this.scope.terminated = undefined;
3558
4160
  }
3559
- });
3560
- }
4161
+ }
3561
4162
 
3562
- /**
3563
- * Pre walk catch clause.
3564
- * @param {CatchClause} catchClause catch clause
3565
- */
3566
- preWalkCatchClause(catchClause) {
3567
- this.preWalkStatement(catchClause.body);
4163
+ this._exitBlockScope(oldScope, false);
3568
4164
  }
3569
4165
 
3570
4166
  /**
@@ -3572,22 +4168,23 @@ class JavascriptParser extends Parser {
3572
4168
  * @param {CatchClause} catchClause catch clause
3573
4169
  */
3574
4170
  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);
4171
+ const oldScope = this._enterBlockScope();
4172
+ // Error binding is optional in catch clause since ECMAScript 2019
4173
+ if (catchClause.param !== null) {
4174
+ this.enterPattern(catchClause.param, (ident, identifier) => {
4175
+ if (this._strictInModuleOutput) {
4176
+ this._checkStrictModeBinding(ident, identifier);
4177
+ }
4178
+ this.defineVariable(ident);
4179
+ });
4180
+ this.walkPattern(catchClause.param);
4181
+ }
4182
+ const prev = this.prevStatement;
4183
+ this.blockPreWalkStatement(catchClause.body);
4184
+ this.prevStatement = prev;
4185
+ this.walkStatement(catchClause.body);
4186
+
4187
+ this._exitBlockScope(oldScope, true);
3591
4188
  }
3592
4189
 
3593
4190
  /**
@@ -3885,19 +4482,20 @@ class JavascriptParser extends Parser {
3885
4482
  this._checkStrictModeBinding(expression.id.name, expression.id);
3886
4483
  }
3887
4484
  }
3888
- this.inFunctionScope(true, scopeParams, () => {
3889
- for (const param of expression.params) {
3890
- this.walkPattern(param);
3891
- }
4485
+ const oldScope = this._enterFunctionScope(true, scopeParams);
4486
+ for (const param of expression.params) {
4487
+ this.walkPattern(param);
4488
+ }
3892
4489
 
3893
- this.detectMode(expression.body.body);
4490
+ this.detectMode(expression.body.body);
3894
4491
 
3895
- const prev = this.prevStatement;
4492
+ const prev = this.prevStatement;
3896
4493
 
3897
- this.preWalkStatement(expression.body);
3898
- this.prevStatement = prev;
3899
- this.walkStatement(expression.body);
3900
- });
4494
+ this._preWalkScope(expression.body);
4495
+ this.prevStatement = prev;
4496
+ this.walkStatement(expression.body);
4497
+
4498
+ this.scope = oldScope;
3901
4499
  this.scope.topLevelScope = wasTopLevel;
3902
4500
  }
3903
4501
 
@@ -3911,20 +4509,21 @@ class JavascriptParser extends Parser {
3911
4509
  if (this._strictInModuleOutput) {
3912
4510
  this._checkStrictModeParams(expression.params);
3913
4511
  }
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
- });
4512
+ const oldScope = this._enterFunctionScope(false, expression.params);
4513
+ for (const param of expression.params) {
4514
+ this.walkPattern(param);
4515
+ }
4516
+ if (expression.body.type === "BlockStatement") {
4517
+ this.detectMode(expression.body.body);
4518
+ const prev = this.prevStatement;
4519
+ this._preWalkScope(expression.body);
4520
+ this.prevStatement = prev;
4521
+ this.walkStatement(expression.body);
4522
+ } else {
4523
+ this.walkExpression(expression.body);
4524
+ }
4525
+
4526
+ this.scope = oldScope;
3928
4527
  this.scope.topLevelScope = wasTopLevel;
3929
4528
  }
3930
4529
 
@@ -3978,9 +4577,98 @@ class JavascriptParser extends Parser {
3978
4577
  ) {
3979
4578
  this._checkStrictModeAssignTarget(expression.argument.name, expression);
3980
4579
  }
4580
+ if (this._walkWriteTarget(expression.argument, expression)) return;
3981
4581
  this.walkExpression(expression.argument);
3982
4582
  }
3983
4583
 
4584
+ /**
4585
+ * Offers a write that is not an assignment's left side — an update's target
4586
+ * or a loop's binding — to the taps that care, `assign` carrying only an
4587
+ * AssignmentExpression.
4588
+ * @param {Expression | Pattern} target the written reference
4589
+ * @param {WriteStatement} node the construct performing the write
4590
+ * @returns {boolean} true when a tap rendered the write itself
4591
+ */
4592
+ _walkWriteTarget(target, node) {
4593
+ if (target.type === "Identifier") {
4594
+ return Boolean(
4595
+ this._callHooksForName1(this.hooks.write, target.name, node)
4596
+ );
4597
+ }
4598
+ if (target.type.endsWith("Pattern")) {
4599
+ this.enterPattern(/** @type {Pattern} */ (target), (name) => {
4600
+ this._callHooksForName1(this.hooks.write, name, node);
4601
+ });
4602
+ this._walkPatternWriteMembers(target, node);
4603
+ return false;
4604
+ }
4605
+ return Boolean(
4606
+ target.type === "MemberExpression" &&
4607
+ this._callWriteMemberChain(target, node)
4608
+ );
4609
+ }
4610
+
4611
+ /**
4612
+ * Offers the member expressions a destructuring target writes to the
4613
+ * `writeMemberChain` taps, which `enterPattern` passes over because they
4614
+ * bind no name.
4615
+ * @param {Pattern | Expression | null} pattern the destructuring target
4616
+ * @param {WriteStatement} node the construct performing the write
4617
+ * @returns {void}
4618
+ */
4619
+ _walkPatternWriteMembers(pattern, node) {
4620
+ if (!pattern) return;
4621
+ switch (pattern.type) {
4622
+ case "MemberExpression":
4623
+ this._callWriteMemberChain(pattern, node);
4624
+ break;
4625
+ case "ObjectPattern":
4626
+ for (const property of pattern.properties) {
4627
+ this._walkPatternWriteMembers(
4628
+ property.type === "RestElement"
4629
+ ? property.argument
4630
+ : /** @type {Pattern} */ (property.value),
4631
+ node
4632
+ );
4633
+ }
4634
+ break;
4635
+ case "ArrayPattern":
4636
+ for (const element of pattern.elements) {
4637
+ this._walkPatternWriteMembers(element, node);
4638
+ }
4639
+ break;
4640
+ case "AssignmentPattern":
4641
+ this._walkPatternWriteMembers(pattern.left, node);
4642
+ break;
4643
+ case "RestElement":
4644
+ this._walkPatternWriteMembers(pattern.argument, node);
4645
+ break;
4646
+ }
4647
+ }
4648
+
4649
+ /**
4650
+ * Offers a written member expression to the `writeMemberChain` taps.
4651
+ * @param {MemberExpression} target the written member expression
4652
+ * @param {WriteStatement} node the construct performing the write
4653
+ * @returns {boolean} true when a tap rendered the write itself
4654
+ */
4655
+ _callWriteMemberChain(target, node) {
4656
+ return Boolean(
4657
+ this._resolveMemberExpression(target, ALLOWED_MEMBER_TYPES_EXPRESSION) ===
4658
+ 1 &&
4659
+ this._hasHooksForInfo(
4660
+ this.hooks.writeMemberChain,
4661
+ /** @type {string | VariableInfo} */ (this._memberRootInfo)
4662
+ ) &&
4663
+ this.callHooksForInfo(
4664
+ this.hooks.writeMemberChain,
4665
+ /** @type {string | VariableInfo} */ (this._memberRootInfo),
4666
+ node,
4667
+ this._members
4668
+ )
4669
+ );
4670
+ }
4671
+
3984
4672
  /**
3985
4673
  * Reports `arguments.callee` / `arguments.caller` — both throw a TypeError
3986
4674
  * once the module runs in strict-mode ESM output.
@@ -4185,7 +4873,7 @@ class JavascriptParser extends Parser {
4185
4873
  const renameIdentifier = this.getRenameIdentifier(expression.right);
4186
4874
  if (
4187
4875
  renameIdentifier &&
4188
- this.callHooksForInfo(
4876
+ this._callHooksForInfo1(
4189
4877
  this.hooks.canRename,
4190
4878
  renameIdentifier,
4191
4879
  expression.right
@@ -4193,7 +4881,7 @@ class JavascriptParser extends Parser {
4193
4881
  ) {
4194
4882
  // renaming "a = b;"
4195
4883
  if (
4196
- !this.callHooksForInfo(
4884
+ !this._callHooksForInfo1(
4197
4885
  this.hooks.rename,
4198
4886
  renameIdentifier,
4199
4887
  expression.right
@@ -4209,34 +4897,43 @@ class JavascriptParser extends Parser {
4209
4897
  return;
4210
4898
  }
4211
4899
  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
- });
4900
+ // inlined enterPattern → enterIdentifier for the Identifier left side:
4901
+ // same hook sequence without the per-assignment closure
4902
+ const leftName = expression.left.name;
4903
+ if (
4904
+ !this._callHooksForName1(
4905
+ this.hooks.pattern,
4906
+ leftName,
4907
+ expression.left
4908
+ ) &&
4909
+ !this._callHooksForName1(this.hooks.assign, leftName, expression)
4910
+ ) {
4911
+ this.walkExpression(expression.left);
4912
+ }
4220
4913
  } else if (expression.left.type.endsWith("Pattern")) {
4221
4914
  this.walkExpression(expression.right);
4222
4915
  this.enterPattern(expression.left, (name, _decl) => {
4223
- if (!this.callHooksForName(this.hooks.assign, name, expression)) {
4916
+ if (!this._callHooksForName1(this.hooks.assign, name, expression)) {
4224
4917
  this.defineVariable(name);
4225
4918
  }
4226
4919
  });
4920
+ this._walkPatternWriteMembers(expression.left, expression);
4227
4921
  this.walkPattern(expression.left);
4228
4922
  } else if (expression.left.type === "MemberExpression") {
4229
- const exprName = this.getMemberExpressionInfo(
4230
- expression.left,
4231
- ALLOWED_MEMBER_TYPES_EXPRESSION
4232
- );
4233
4923
  if (
4234
- exprName &&
4924
+ this._resolveMemberExpression(
4925
+ expression.left,
4926
+ ALLOWED_MEMBER_TYPES_EXPRESSION
4927
+ ) === 1 &&
4928
+ this._hasHooksForInfo(
4929
+ this.hooks.assignMemberChain,
4930
+ /** @type {string | VariableInfo} */ (this._memberRootInfo)
4931
+ ) &&
4235
4932
  this.callHooksForInfo(
4236
4933
  this.hooks.assignMemberChain,
4237
- exprName.rootInfo,
4934
+ /** @type {string | VariableInfo} */ (this._memberRootInfo),
4238
4935
  expression,
4239
- exprName.getMembers()
4936
+ this._members
4240
4937
  )
4241
4938
  ) {
4242
4939
  return;
@@ -4261,15 +4958,24 @@ class JavascriptParser extends Parser {
4261
4958
  if (result === undefined) {
4262
4959
  const guard = this.hooks.collectGuards.call(expression.test);
4263
4960
  this.walkExpression(expression.test);
4264
- this.walkGuardedBranch(guard ? guard.consequent : undefined, () =>
4265
- this.walkExpression(expression.consequent)
4266
- );
4961
+ // allocate the branch closure only when a guard frame needs pushing
4962
+ if (guard && guard.consequent) {
4963
+ this.walkGuardedBranch(guard.consequent, () =>
4964
+ this.walkExpression(expression.consequent)
4965
+ );
4966
+ } else {
4967
+ this.walkExpression(expression.consequent);
4968
+ }
4267
4969
 
4268
4970
  if (expression.alternate) {
4269
4971
  const alternate = expression.alternate;
4270
- this.walkGuardedBranch(guard ? guard.alternate : undefined, () =>
4271
- this.walkExpression(alternate)
4272
- );
4972
+ if (guard && guard.alternate) {
4973
+ this.walkGuardedBranch(guard.alternate, () =>
4974
+ this.walkExpression(alternate)
4975
+ );
4976
+ } else {
4977
+ this.walkExpression(alternate);
4978
+ }
4273
4979
  }
4274
4980
  } else if (result) {
4275
4981
  this.walkExpression(expression.consequent);
@@ -4375,13 +5081,13 @@ class JavascriptParser extends Parser {
4375
5081
  const renameIdentifier = this.getRenameIdentifier(argOrThis);
4376
5082
  if (
4377
5083
  renameIdentifier &&
4378
- this.callHooksForInfo(
5084
+ this._callHooksForInfo1(
4379
5085
  this.hooks.canRename,
4380
5086
  renameIdentifier,
4381
5087
  /** @type {Expression} */
4382
5088
  (argOrThis)
4383
5089
  ) &&
4384
- !this.callHooksForInfo(
5090
+ !this._callHooksForInfo1(
4385
5091
  this.hooks.rename,
4386
5092
  renameIdentifier,
4387
5093
  /** @type {Expression} */
@@ -4422,26 +5128,27 @@ class JavascriptParser extends Parser {
4422
5128
  scopeParams.push(functionExpression.id.name);
4423
5129
  }
4424
5130
 
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
- });
5131
+ const oldScope = this._enterFunctionScope(true, scopeParams);
5132
+ if (renameThis && !arrow) {
5133
+ this.setVariable("this", renameThis);
5134
+ }
5135
+ for (let i = 0; i < varInfoForArgs.length; i++) {
5136
+ const varInfo = varInfoForArgs[i];
5137
+ if (!varInfo) continue;
5138
+ if (!params[i] || params[i].type !== "Identifier") continue;
5139
+ this.setVariable(/** @type {Identifier} */ (params[i]).name, varInfo);
5140
+ }
5141
+ if (functionExpression.body.type === "BlockStatement") {
5142
+ this.detectMode(functionExpression.body.body);
5143
+ const prev = this.prevStatement;
5144
+ this._preWalkScope(functionExpression.body);
5145
+ this.prevStatement = prev;
5146
+ this.walkStatement(functionExpression.body);
5147
+ } else {
5148
+ this.walkExpression(functionExpression.body);
5149
+ }
5150
+
5151
+ this.scope = oldScope;
4445
5152
  this.scope.topLevelScope = wasTopLevel;
4446
5153
  }
4447
5154
 
@@ -4553,22 +5260,26 @@ class JavascriptParser extends Parser {
4553
5260
  }
4554
5261
  const callee = this.evaluateExpression(expression.callee);
4555
5262
  if (callee.isIdentifier()) {
4556
- const members =
4557
- /** @type {NonNullable<BasicEvaluatedExpression["getMembers"]>} */
4558
- (callee.getMembers)();
4559
- const result1 = this.callHooksForInfo(
4560
- this.hooks.callMemberChain,
5263
+ const rootInfo =
4561
5264
  /** @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(
5265
+ (callee.rootInfo);
5266
+ if (this._hasHooksForInfo(this.hooks.callMemberChain, rootInfo)) {
5267
+ const members =
5268
+ /** @type {NonNullable<BasicEvaluatedExpression["getMembers"]>} */
5269
+ (callee.getMembers)();
5270
+ const result1 = this.callHooksForInfo(
5271
+ this.hooks.callMemberChain,
5272
+ rootInfo,
5273
+ expression,
5274
+ members,
5275
+ callee.getMembersOptionals
5276
+ ? callee.getMembersOptionals()
5277
+ : members.map(RETURN_FALSE),
5278
+ callee.getMemberRanges ? callee.getMemberRanges() : []
5279
+ );
5280
+ if (result1 === true) return;
5281
+ }
5282
+ const result2 = this._callHooksForInfo1(
4572
5283
  this.hooks.call,
4573
5284
  /** @type {NonNullable<BasicEvaluatedExpression["identifier"]>} */
4574
5285
  (callee.identifier),
@@ -4593,69 +5304,113 @@ class JavascriptParser extends Parser {
4593
5304
  }
4594
5305
 
4595
5306
  /**
4596
- * Walk member expression.
4597
- * @param {MemberExpression} expression member expression
5307
+ * Walk member expression.
5308
+ * @param {MemberExpression} expression member expression
5309
+ */
5310
+ walkMemberExpression(expression) {
5311
+ const type = this._resolveMemberExpression(
5312
+ expression,
5313
+ ALLOWED_MEMBER_TYPES_ALL
5314
+ );
5315
+ if (type === 1) {
5316
+ // copied out first: a hook may resolve another chain and reuse the slots
5317
+ const name = this._memberName;
5318
+ const rootInfo = /** @type {string | VariableInfo} */ (
5319
+ this._memberRootInfo
5320
+ );
5321
+ const members = this._members;
5322
+ const membersOptionals = this._membersOptionals;
5323
+ const result1 = this._callHooksForInfo1(
5324
+ this.hooks.expression,
5325
+ name,
5326
+ expression
5327
+ );
5328
+ if (result1 === true) return;
5329
+ if (this._hasHooksForInfo(this.hooks.expressionMemberChain, rootInfo)) {
5330
+ const result2 = this.callHooksForInfo(
5331
+ this.hooks.expressionMemberChain,
5332
+ rootInfo,
5333
+ expression,
5334
+ members,
5335
+ membersOptionals,
5336
+ memberRangesOf(expression, members.length)
5337
+ );
5338
+ if (result2 === true) return;
5339
+ }
5340
+ this._walkMemberChain(
5341
+ expression,
5342
+ expression,
5343
+ name,
5344
+ rootInfo,
5345
+ members,
5346
+ members.length
5347
+ );
5348
+ return;
5349
+ }
5350
+ if (type === 2) {
5351
+ const call = /** @type {CallExpression} */ (this._memberCall);
5352
+ const result = this.callHooksForInfo(
5353
+ this.hooks.memberChainOfCallMemberChain,
5354
+ /** @type {string | VariableInfo} */ (this._memberRootInfo),
5355
+ expression,
5356
+ this._memberCalleeMembers,
5357
+ call,
5358
+ this._members,
5359
+ memberRangesOf(expression, this._members.length)
5360
+ );
5361
+ if (result === true) return;
5362
+ // Fast skip over the member chain as we already called memberChainOfCallMemberChain
5363
+ // and call computed property are literals anyway
5364
+ this.walkExpression(call);
5365
+ return;
5366
+ }
5367
+ this.walkExpression(expression.object);
5368
+ if (expression.computed === true) this.walkExpression(expression.property);
5369
+ }
5370
+
5371
+ /**
5372
+ * The walk `walkMemberExpressionWithExpressionName` does over a resolved
5373
+ * chain, reading `members` up to `end` instead of popping a copy of it.
5374
+ * @param {MemberExpression} outer the whole chain, for the unhandled hook
5375
+ * @param {MemberExpression} expression the chain from here inwards
5376
+ * @param {string} name name of `expression`
5377
+ * @param {string | VariableInfo} rootInfo root info
5378
+ * @param {Members} members members of the whole chain, in source order
5379
+ * @param {number} end how many of `members` are inside `expression`
5380
+ * @returns {void}
4598
5381
  */
4599
- walkMemberExpression(expression) {
4600
- const exprInfo = this.getMemberExpressionInfo(
4601
- expression,
4602
- ALLOWED_MEMBER_TYPES_ALL
4603
- );
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
- }
4656
- }
5382
+ _walkMemberChain(outer, expression, name, rootInfo, members, end) {
5383
+ if (expression.object.type === "MemberExpression") {
5384
+ const property = members[end - 1];
5385
+ name = name.slice(0, -property.length - 1);
5386
+ const result = this._callHooksForInfo1(
5387
+ this.hooks.expression,
5388
+ name,
5389
+ expression.object
5390
+ );
5391
+ if (result === true) return;
5392
+ this._walkMemberChain(
5393
+ outer,
5394
+ expression.object,
5395
+ name,
5396
+ rootInfo,
5397
+ members,
5398
+ end - 1
5399
+ );
5400
+ } else if (
5401
+ !this._hasHooksForInfo(
5402
+ this.hooks.unhandledExpressionMemberChain,
5403
+ rootInfo
5404
+ ) ||
5405
+ !this.callHooksForInfo(
5406
+ this.hooks.unhandledExpressionMemberChain,
5407
+ rootInfo,
5408
+ outer,
5409
+ members
5410
+ )
5411
+ ) {
5412
+ this.walkExpression(expression.object);
4657
5413
  }
4658
- this.walkExpression(expression.object);
4659
5414
  if (expression.computed === true) this.walkExpression(expression.property);
4660
5415
  }
4661
5416
 
@@ -4676,15 +5431,13 @@ class JavascriptParser extends Parser {
4676
5431
  onUnhandled
4677
5432
  ) {
4678
5433
  if (expression.object.type === "MemberExpression") {
4679
- // optimize the case where expression.object is a MemberExpression too.
4680
- // we can keep info here when calling walkMemberExpression directly
4681
- // Read the property from `members` (already extracted by
4682
- // extractMemberExpressionChain) since the AST node may be a
4683
- // TemplateLiteral, which has neither .name nor .value.
5434
+ // A nested MemberExpression keeps its info by calling walkMemberExpression
5435
+ // directly. The property comes from `members` rather than the node, which
5436
+ // may be a TemplateLiteral and carry neither `.name` nor `.value`.
4684
5437
  const property = members[members.length - 1];
4685
5438
  name = name.slice(0, -property.length - 1);
4686
5439
  members.pop();
4687
- const result = this.callHooksForInfo(
5440
+ const result = this._callHooksForInfo1(
4688
5441
  this.hooks.expression,
4689
5442
  name,
4690
5443
  expression.object
@@ -4708,7 +5461,7 @@ class JavascriptParser extends Parser {
4708
5461
  * @param {ThisExpression} expression this expression
4709
5462
  */
4710
5463
  walkThisExpression(expression) {
4711
- this.callHooksForName(this.hooks.expression, "this", expression);
5464
+ this._callHooksForName1(this.hooks.expression, "this", expression);
4712
5465
  }
4713
5466
 
4714
5467
  /**
@@ -4716,7 +5469,7 @@ class JavascriptParser extends Parser {
4716
5469
  * @param {Identifier} expression identifier
4717
5470
  */
4718
5471
  walkIdentifier(expression) {
4719
- this.callHooksForName(this.hooks.expression, expression.name, expression);
5472
+ this._callHooksForName1(this.hooks.expression, expression.name, expression);
4720
5473
  }
4721
5474
 
4722
5475
  /**
@@ -4882,6 +5635,27 @@ class JavascriptParser extends Parser {
4882
5635
  }
4883
5636
  }
4884
5637
 
5638
+ /**
5639
+ * Whether `_callHooksForInfo` would call any hook for this info, so a
5640
+ * caller can skip building the arguments for a name nothing taps.
5641
+ * @template T
5642
+ * @template R
5643
+ * @param {HookMap<SyncBailHook<T, R>>} hookMap hooks that would be called
5644
+ * @param {ExportedVariableInfo} info variable info
5645
+ * @returns {boolean} true when a hook is registered for the info
5646
+ */
5647
+ _hasHooksForInfo(hookMap, info) {
5648
+ if (typeof info === "string") return hookMap.get(info) !== undefined;
5649
+ if (!(info instanceof VariableInfo)) return false;
5650
+ let tagInfo = info.tagInfo;
5651
+ while (tagInfo !== undefined) {
5652
+ if (hookMap.get(tagInfo.tag) !== undefined) return true;
5653
+ tagInfo = tagInfo.next;
5654
+ }
5655
+ if (!info.isFree() && !info.isTagged()) return false;
5656
+ return hookMap.get(/** @type {string} */ (info.name)) !== undefined;
5657
+ }
5658
+
4885
5659
  /**
4886
5660
  * Call hooks for name with fallback.
4887
5661
  * @template T
@@ -4903,6 +5677,69 @@ class JavascriptParser extends Parser {
4903
5677
  );
4904
5678
  }
4905
5679
 
5680
+ /**
5681
+ * Single-argument twin of `callHooksForName` (must stay in sync with
5682
+ * `_callHooksForInfo`): every internal dispatch passes exactly one hook
5683
+ * argument, so this skips the rest-array collection and spread call the
5684
+ * generic path pays per identifier.
5685
+ * @template T
5686
+ * @template R
5687
+ * @param {HookMap<SyncBailHook<T, R>>} hookMap hooks that should be called
5688
+ * @param {string} name key in map
5689
+ * @param {AsArray<T>[0]} arg the single hook argument
5690
+ * @returns {R | undefined} result of hook
5691
+ */
5692
+ _callHooksForName1(hookMap, name, arg) {
5693
+ return this._callHooksForInfo1(hookMap, this.getVariableInfo(name), arg);
5694
+ }
5695
+
5696
+ /**
5697
+ * Single-argument twin of `callHooksForInfo` (must stay in sync with
5698
+ * `_callHooksForInfo`), for the hot dispatch sites that pass one argument
5699
+ * and no fallback/defined callbacks.
5700
+ * @template T
5701
+ * @template R
5702
+ * @param {HookMap<SyncBailHook<T, R>>} hookMap hooks that should be called
5703
+ * @param {ExportedVariableInfo} info variable info
5704
+ * @param {AsArray<T>[0]} arg the single hook argument
5705
+ * @returns {R | undefined} result of hook
5706
+ */
5707
+ _callHooksForInfo1(hookMap, info, arg) {
5708
+ /** @type {string} */
5709
+ let name;
5710
+ if (typeof info === "string") {
5711
+ name = info;
5712
+ } else {
5713
+ if (!(info instanceof VariableInfo)) {
5714
+ return;
5715
+ }
5716
+ let tagInfo = info.tagInfo;
5717
+ while (tagInfo !== undefined) {
5718
+ const hook =
5719
+ /** @type {SyncBailHook<[AsArray<T>[0]], R> | undefined} */
5720
+ (hookMap.get(tagInfo.tag));
5721
+ if (hook !== undefined) {
5722
+ this.currentTagData = tagInfo.data;
5723
+ const result = hook.call(arg);
5724
+ this.currentTagData = undefined;
5725
+ if (result !== undefined) return result;
5726
+ }
5727
+ tagInfo = tagInfo.next;
5728
+ }
5729
+ if (!info.isFree() && !info.isTagged()) {
5730
+ return;
5731
+ }
5732
+ name = /** @type {string} */ (info.name);
5733
+ }
5734
+ const hook =
5735
+ /** @type {SyncBailHook<[AsArray<T>[0]], R> | undefined} */
5736
+ (hookMap.get(name));
5737
+ if (hook !== undefined) {
5738
+ const result = hook.call(arg);
5739
+ if (result !== undefined) return result;
5740
+ }
5741
+ }
5742
+
4906
5743
  /**
4907
5744
  * Processes the provided param.
4908
5745
  * @deprecated
@@ -4971,26 +5808,8 @@ class JavascriptParser extends Parser {
4971
5808
  * @returns {void}
4972
5809
  */
4973
5810
  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
-
5811
+ const oldScope = this._enterFunctionScope(hasThis, params);
4992
5812
  fn();
4993
-
4994
5813
  this.scope = oldScope;
4995
5814
  }
4996
5815
 
@@ -5001,6 +5820,18 @@ class JavascriptParser extends Parser {
5001
5820
  * @returns {void}
5002
5821
  */
5003
5822
  inBlockScope(fn, inExecutedPath = false) {
5823
+ const oldScope = this._enterBlockScope();
5824
+ fn();
5825
+ this._exitBlockScope(oldScope, inExecutedPath);
5826
+ }
5827
+
5828
+ /**
5829
+ * Enters a fresh block scope: the closure-free core of `inBlockScope`, used
5830
+ * directly by the internal walkers so each block does not allocate a
5831
+ * closure and context. Pass the returned scope to `_exitBlockScope`.
5832
+ * @returns {ScopeInfo} the previous scope
5833
+ */
5834
+ _enterBlockScope() {
5004
5835
  const oldScope = this.scope;
5005
5836
  this.scope = {
5006
5837
  topLevelScope: oldScope.topLevelScope,
@@ -5012,18 +5843,50 @@ class JavascriptParser extends Parser {
5012
5843
  terminated: oldScope.terminated,
5013
5844
  definitions: oldScope.definitions.createChild()
5014
5845
  };
5846
+ return oldScope;
5847
+ }
5015
5848
 
5016
- fn();
5017
-
5849
+ /**
5850
+ * Leaves a block scope entered with `_enterBlockScope`.
5851
+ * @param {ScopeInfo} oldScope scope returned by `_enterBlockScope`
5852
+ * @param {boolean} inExecutedPath whether termination propagates to the outer scope
5853
+ * @returns {void}
5854
+ */
5855
+ _exitBlockScope(oldScope, inExecutedPath) {
5018
5856
  const terminated = this.scope.terminated;
5019
-
5020
5857
  if (inExecutedPath && terminated) {
5021
5858
  oldScope.terminated = terminated;
5022
5859
  }
5023
-
5024
5860
  this.scope = oldScope;
5025
5861
  }
5026
5862
 
5863
+ /**
5864
+ * Enters a fresh function scope: the closure-free core of `inFunctionScope`,
5865
+ * used directly by the internal walkers. Restore `this.scope` to the
5866
+ * returned scope when leaving.
5867
+ * @param {boolean} hasThis true, when this is defined
5868
+ * @param {(Pattern | string)[]} params scope params
5869
+ * @returns {ScopeInfo} the previous scope
5870
+ */
5871
+ _enterFunctionScope(hasThis, params) {
5872
+ const oldScope = this.scope;
5873
+ this.scope = {
5874
+ topLevelScope: oldScope.topLevelScope,
5875
+ inTry: false,
5876
+ inShorthand: false,
5877
+ inTaggedTemplateTag: false,
5878
+ isStrict: oldScope.isStrict,
5879
+ isAsmJs: oldScope.isAsmJs,
5880
+ terminated: undefined,
5881
+ definitions: oldScope.definitions.createChild()
5882
+ };
5883
+ if (hasThis) {
5884
+ this.undefineVariable("this");
5885
+ }
5886
+ this.enterPatterns(params, this._defineVariable);
5887
+ return oldScope;
5888
+ }
5889
+
5027
5890
  /**
5028
5891
  * Processes the provided statement.
5029
5892
  * @param {(Directive | Statement | ModuleDeclaration)[]} statements statements
@@ -5103,7 +5966,7 @@ class JavascriptParser extends Parser {
5103
5966
  * @param {OnIdent} onIdent callback
5104
5967
  */
5105
5968
  enterIdentifier(pattern, onIdent) {
5106
- if (!this.callHooksForName(this.hooks.pattern, pattern.name, pattern)) {
5969
+ if (!this._callHooksForName1(this.hooks.pattern, pattern.name, pattern)) {
5107
5970
  onIdent(pattern.name, pattern);
5108
5971
  }
5109
5972
  }
@@ -5351,6 +6214,13 @@ class JavascriptParser extends Parser {
5351
6214
  const oldStrictInModuleOutput = this._strictInModuleOutput;
5352
6215
  const oldSource = this._source;
5353
6216
  const oldLineStarts = this._lineStarts;
6217
+ const oldHasParserRecord = this._hasParserRecord;
6218
+ // WHY: the parser's record of each scope is only the truth for a tree it
6219
+ // just built. A loader's AST, and whatever `module.parser.javascript.parse`
6220
+ // returns, may have been edited after it was read — or built by something
6221
+ // else entirely — so both are collected from instead of trusted.
6222
+ this._hasParserRecord =
6223
+ typeof source !== "object" && typeof this.options.parse !== "function";
5354
6224
  // a preparsed AST carries no text, but the build passes the module
5355
6225
  // source in the state — offsets still map for it
5356
6226
  this._source =
@@ -5398,16 +6268,19 @@ class JavascriptParser extends Parser {
5398
6268
  if (this._strictInModuleOutput && this.scope.isStrict) {
5399
6269
  this._strictInModuleOutput = false;
5400
6270
  }
5401
- this.modulePreWalkStatements(ast.body);
6271
+ this._modulePreWalkDeclarations(
6272
+ moduleDeclarationsOf(ast, this._hasParserRecord)
6273
+ );
5402
6274
  this.prevStatement = undefined;
5403
- this.preWalkStatements(ast.body);
6275
+ this._preWalkScope(ast);
5404
6276
  this.prevStatement = undefined;
5405
- this.blockPreWalkStatements(ast.body);
6277
+ this._blockPreWalkScope(ast, ast.body);
5406
6278
  this.prevStatement = undefined;
5407
6279
  this.walkStatements(ast.body);
5408
6280
  this.destructuringAssignmentProperties = undefined;
5409
6281
  }
5410
6282
  this.hooks.finish.call(ast, comments);
6283
+ this._releaseMemberExpression();
5411
6284
  this.scope = oldScope;
5412
6285
  this.state = oldState;
5413
6286
  this.comments = oldComments;
@@ -5418,6 +6291,7 @@ class JavascriptParser extends Parser {
5418
6291
  // release — these retain the whole source string and its line table
5419
6292
  this._source = oldSource;
5420
6293
  this._lineStarts = oldLineStarts;
6294
+ this._hasParserRecord = oldHasParserRecord;
5421
6295
  return state;
5422
6296
  }
5423
6297
 
@@ -5452,6 +6326,9 @@ class JavascriptParser extends Parser {
5452
6326
  `(${source})`,
5453
6327
  {
5454
6328
  sourceType: this.sourceType,
6329
+ // ranges alone take the lazy path, which serves `range` from a
6330
+ // getter and tracks no locations — as the module parse does
6331
+ ranges: true,
5455
6332
  importPhases
5456
6333
  },
5457
6334
  this.options.parse
@@ -5533,10 +6410,9 @@ class JavascriptParser extends Parser {
5533
6410
  });
5534
6411
  }
5535
6412
  case "TemplateLiteral":
5536
- // Thread `commentsStartPos` through the interpolations so a
5537
- // /*#__PURE__*/ comment that sits inside `${ ... }` (or before
5538
- // the first interpolation) is part of the scanned range when
5539
- // the inner expression's purity is evaluated.
6413
+ // Thread `commentsStartPos` through the interpolations, so a
6414
+ // `/*#__PURE__*/` inside `${ … }`, or before the first one, is within
6415
+ // the range scanned when the inner expression's purity is judged.
5540
6416
  return expr.expressions.every((e) => {
5541
6417
  const pureFlag = this.isPure(e, commentsStartPos);
5542
6418
  commentsStartPos = /** @type {Range} */ (e.range)[1];
@@ -5590,17 +6466,13 @@ class JavascriptParser extends Parser {
5590
6466
  return this.isPure(expr.expression, commentsStartPos);
5591
6467
 
5592
6468
  case "UnaryExpression":
5593
- // Safe unary operators — produce their result without invoking
5594
- // user code on the operand:
5595
- // - `typeof` returns a type tag and never throws, even for
5596
- // undeclared identifiers; no coercion.
5597
- // - `void` evaluates the operand and discards it, returning
5598
- // `undefined`; pure iff the operand is pure.
5599
- // - `!` coerces via ToBoolean, which is defined to not call
5600
- // any user code (objects → true, etc.).
5601
- // Other operators (`+`, `-`, `~`, `delete`) fall through to
5602
- // the generic evaluator which can still recognize literal
5603
- // cases (e.g. `-1`, `+5`).
6469
+ // WHY: these three produce their result without invoking user code on
6470
+ // the operand. `typeof` returns a type tag and never throws, even for an
6471
+ // undeclared identifier, and coerces nothing; `void` evaluates the
6472
+ // operand and discards it, so it is pure exactly when the operand is; and
6473
+ // `!` coerces through ToBoolean, which is defined to call no user code.
6474
+ // `+`, `-`, `~` and `delete` fall through to the generic evaluator, which
6475
+ // still recognizes literal cases such as `-1` and `+5`.
5604
6476
  if (
5605
6477
  expr.operator === "typeof" ||
5606
6478
  expr.operator === "void" ||
@@ -5614,12 +6486,12 @@ class JavascriptParser extends Parser {
5614
6486
  return true;
5615
6487
 
5616
6488
  case "BinaryExpression":
5617
- // Strict (in)equality compares without coercion and never invokes
5618
- // user code on its operands, so the result is pure iff both sides
5619
- // are pure. All other binary operators may invoke `valueOf` /
5620
- // `toString` / `[Symbol.hasInstance]` / Proxy traps and fall through
5621
- // to the generic evaluator, which can still recognize the cases
5622
- // where both sides evaluate to known primitive literals.
6489
+ // WHY: strict (in)equality compares without coercion and never invokes user
6490
+ // code on its operands, so the result is pure where both sides are. Every
6491
+ // other binary operator may invoke `valueOf`, `toString`,
6492
+ // `[Symbol.hasInstance]` or a Proxy trap, and falls through to the generic
6493
+ // evaluator, which still recognizes the cases where both sides evaluate
6494
+ // to known primitive literals.
5623
6495
  if (expr.operator === "===" || expr.operator === "!==") {
5624
6496
  return (
5625
6497
  this.isPure(expr.left, commentsStartPos) &&
@@ -5753,7 +6625,15 @@ class JavascriptParser extends Parser {
5753
6625
  };
5754
6626
  }
5755
6627
  }
5756
- return /** @type {DependencyLocation} */ (node.loc);
6628
+ const loc = node.loc;
6629
+ if (!loc) {
6630
+ return /** @type {DependencyLocation} */ (/** @type {unknown} */ (loc));
6631
+ }
6632
+ // a plain copy: a foreign parser's location class must not reach the cache
6633
+ return {
6634
+ start: { line: loc.start.line, column: loc.start.column },
6635
+ end: { line: loc.end.line, column: loc.end.column }
6636
+ };
5757
6637
  }
5758
6638
 
5759
6639
  /**
@@ -5768,6 +6648,46 @@ class JavascriptParser extends Parser {
5768
6648
  );
5769
6649
  }
5770
6650
 
6651
+ /**
6652
+ * Where the comments written for `statement` begin: the run of comments
6653
+ * before it that whitespace alone separates from it and from each other.
6654
+ * @param {StatementPathItem} statement the statement they belong to
6655
+ * @returns {number} the offset the run starts at, or the statement's own
6656
+ */
6657
+ getAttachedCommentsStart(statement) {
6658
+ let position = /** @type {Range} */ (statement.range)[0];
6659
+ // what precedes `export function f() {}` is written for the export, and
6660
+ // the export keyword is the token the run would otherwise stop at
6661
+ const path = this.statementPath;
6662
+ if (path !== undefined && path.length > 1) {
6663
+ const outer = path[path.length - 2];
6664
+ if (
6665
+ (outer.type === "ExportNamedDeclaration" ||
6666
+ outer.type === "ExportDefaultDeclaration") &&
6667
+ outer.declaration === statement
6668
+ ) {
6669
+ position = /** @type {Range} */ (outer.range)[0];
6670
+ }
6671
+ }
6672
+ const comments = this.comments;
6673
+ if (comments === undefined) return position;
6674
+ const source = this._source;
6675
+ for (let index = comments.length - 1; index >= 0; index--) {
6676
+ const range = /** @type {Range} */ (comments[index].range);
6677
+ if (range[1] > position) continue;
6678
+ // with no source to read, nothing can be ruled out, so the run is
6679
+ // taken to reach as far as the comments themselves do
6680
+ if (
6681
+ source !== undefined &&
6682
+ !onlyWhitespaceBetween(source, range[1], position)
6683
+ ) {
6684
+ break;
6685
+ }
6686
+ position = range[0];
6687
+ }
6688
+ return position;
6689
+ }
6690
+
5771
6691
  /**
5772
6692
  * Reports whether `identifier` may occur as a word in the source text of
5773
6693
  * `range`. A conservative pre-filter for expensive AST scans: `false` means
@@ -5995,6 +6915,7 @@ class JavascriptParser extends Parser {
5995
6915
  });
5996
6916
  }
5997
6917
  this.scope.definitions.set(name, newInfo);
6918
+ this._memberExpression = undefined;
5998
6919
  }
5999
6920
 
6000
6921
  /**
@@ -6011,6 +6932,7 @@ class JavascriptParser extends Parser {
6011
6932
  return;
6012
6933
  }
6013
6934
  this.scope.definitions.set(name, this.scope);
6935
+ this._memberExpression = undefined;
6014
6936
  }
6015
6937
 
6016
6938
  /**
@@ -6019,6 +6941,7 @@ class JavascriptParser extends Parser {
6019
6941
  */
6020
6942
  undefineVariable(name) {
6021
6943
  this.scope.definitions.delete(name);
6944
+ this._memberExpression = undefined;
6022
6945
  }
6023
6946
 
6024
6947
  /**
@@ -6071,6 +6994,7 @@ class JavascriptParser extends Parser {
6071
6994
  * @returns {void}
6072
6995
  */
6073
6996
  setVariable(name, variableInfo) {
6997
+ this._memberExpression = undefined;
6074
6998
  if (typeof variableInfo === "string") {
6075
6999
  if (variableInfo === name) {
6076
7000
  this.scope.definitions.delete(name);
@@ -6242,69 +7166,219 @@ class JavascriptParser extends Parser {
6242
7166
  /** @typedef {{ type: "expression", rootInfo: string | VariableInfo, name: string, getMembers: () => Members, getMembersOptionals: () => MembersOptionals, getMemberRanges: () => MemberRanges }} ExpressionExpressionInfo */
6243
7167
 
6244
7168
  /**
6245
- * Gets member expression info.
7169
+ * Resolves a member expression into the `_member*` slots, once per node and
7170
+ * scope. Returns 0 when rejected, 1 for an `expression`, 2 for a `call`.
6246
7171
  * @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
7172
+ * @param {number} allowedTypes which types may be returned, presented in bit mask
7173
+ * @returns {0 | 1 | 2} the kind resolved
6249
7174
  */
6250
- getMemberExpressionInfo(expression, allowedTypes) {
7175
+ _resolveMemberExpression(expression, allowedTypes) {
7176
+ if (
7177
+ expression === this._memberExpression &&
7178
+ this._memberScope === this.scope
7179
+ ) {
7180
+ const type = this._memberType;
7181
+ if (type !== 0) {
7182
+ // the root kind is a fact of the node; the mask only says whether to report it
7183
+ const bit =
7184
+ type === 2
7185
+ ? ALLOWED_MEMBER_TYPES_CALL_EXPRESSION
7186
+ : ALLOWED_MEMBER_TYPES_EXPRESSION;
7187
+ return (allowedTypes & bit) === 0 ? 0 : type;
7188
+ }
7189
+ if ((allowedTypes & ~this._memberMask) === 0) return 0;
7190
+ }
7191
+ this._memberExpression = expression;
7192
+ this._memberScope = this.scope;
7193
+ this._memberMask = allowedTypes;
7194
+ this._memberType = 0;
7195
+ this._memberInfo = undefined;
6251
7196
  // Resolve the root first (no allocation); most calls reject here and so
6252
- // never build the member arrays via extractMemberExpressionChain.
7197
+ // never build the member arrays
6253
7198
  const object = this.getMemberExpressionRoot(expression);
6254
7199
  switch (object.type) {
6255
7200
  case "CallExpression": {
6256
- if ((allowedTypes & ALLOWED_MEMBER_TYPES_CALL_EXPRESSION) === 0) return;
7201
+ if ((allowedTypes & ALLOWED_MEMBER_TYPES_CALL_EXPRESSION) === 0) {
7202
+ return 0;
7203
+ }
6257
7204
  const calleeExpr = object.callee;
6258
7205
  const callee =
6259
7206
  calleeExpr.type === "MemberExpression"
6260
7207
  ? this.getMemberExpressionRoot(calleeExpr)
6261
7208
  : calleeExpr;
6262
7209
  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);
7210
+ if (!rootName) return this._rejectMemberExpression();
7211
+ const resolvedRoot = this._resolveMemberRoot(rootName);
7212
+ if (resolvedRoot === undefined) return this._rejectMemberExpression();
7213
+ /** @type {CalleeMembers} */
7214
+ let rootMembers = EMPTY_ARRAY;
7215
+ if (calleeExpr.type === "MemberExpression") {
7216
+ this._extractMembers(calleeExpr);
7217
+ rootMembers = this._members;
7218
+ }
7219
+ this._extractMembers(expression);
6273
7220
  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
- };
7221
+ this._memberType = 2;
7222
+ this._memberCall = object;
7223
+ this._memberCalleeName = calleeName;
7224
+ this._memberCalleeMembers = rootMembers;
7225
+ this._memberName = objectAndMembersToName(
7226
+ `${calleeName}()`,
7227
+ this._members
7228
+ );
7229
+ return 2;
6285
7230
  }
6286
7231
  case "Identifier":
6287
7232
  case "MetaProperty":
6288
7233
  case "ThisExpression": {
6289
- if ((allowedTypes & ALLOWED_MEMBER_TYPES_EXPRESSION) === 0) return;
7234
+ if ((allowedTypes & ALLOWED_MEMBER_TYPES_EXPRESSION) === 0) return 0;
6290
7235
  const rootName = getRootName(object);
6291
- if (!rootName) return;
7236
+ if (!rootName) return this._rejectMemberExpression();
7237
+ const resolvedRoot = this._resolveMemberRoot(rootName);
7238
+ if (resolvedRoot === undefined) return this._rejectMemberExpression();
7239
+ this._extractMembers(expression);
7240
+ this._memberType = 1;
7241
+ this._memberName = objectAndMembersToName(resolvedRoot, this._members);
7242
+ return 1;
7243
+ }
7244
+ }
7245
+ return this._rejectMemberExpression();
7246
+ }
6292
7247
 
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
- };
7248
+ /**
7249
+ * Records that the node resolves to nothing under any mask.
7250
+ * @returns {0} rejected
7251
+ */
7252
+ _rejectMemberExpression() {
7253
+ this._memberMask = ALLOWED_MEMBER_TYPES_ALL;
7254
+ return 0;
7255
+ }
7256
+
7257
+ /**
7258
+ * `getNameInfoFromVariable` for the resolver: stores the info in
7259
+ * `_memberRootInfo` and returns the name, or undefined when rejected.
7260
+ * @param {string} rootName name of the root variable
7261
+ * @returns {string | undefined} the resolved root name
7262
+ */
7263
+ _resolveMemberRoot(rootName) {
7264
+ const info = this.getVariableInfo(rootName);
7265
+ if (info instanceof VariableInfo && info.name) {
7266
+ if (!info.isFree() && !info.isTagged()) return;
7267
+ this._memberRootInfo = info;
7268
+ return info.name;
7269
+ }
7270
+ if (typeof info !== "string") return;
7271
+ this._memberRootInfo = info;
7272
+ return info;
7273
+ }
7274
+
7275
+ /**
7276
+ * Fills `_members` and `_membersOptionals` for the chain, in source order.
7277
+ * @param {Expression | Super} expression a member expression
7278
+ * @returns {void}
7279
+ */
7280
+ _extractMembers(expression) {
7281
+ /** @type {Node} */
7282
+ let expr = expression;
7283
+ /** @type {Members} */
7284
+ const members = [];
7285
+ /** @type {MembersOptionals} */
7286
+ const membersOptionals = [];
7287
+ while (expr.type === "MemberExpression") {
7288
+ if (expr.computed) {
7289
+ const prop = expr.property;
7290
+ if (prop.type === "Literal") {
7291
+ members.push(`${prop.value}`);
7292
+ } else if (
7293
+ prop.type === "TemplateLiteral" &&
7294
+ prop.expressions.length === 0 &&
7295
+ typeof prop.quasis[0].value.cooked === "string"
7296
+ ) {
7297
+ members.push(prop.quasis[0].value.cooked);
7298
+ } else {
7299
+ break;
7300
+ }
7301
+ } else {
7302
+ if (expr.property.type !== "Identifier") break;
7303
+ members.push(expr.property.name);
6306
7304
  }
7305
+ membersOptionals.push(expr.optional);
7306
+ expr = expr.object;
7307
+ }
7308
+ if (members.length > 1) {
7309
+ members.reverse();
7310
+ membersOptionals.reverse();
7311
+ }
7312
+ this._members = members;
7313
+ this._membersOptionals = membersOptionals;
7314
+ }
7315
+
7316
+ /**
7317
+ * Drops the resolved member expression, so no AST outlives its parse.
7318
+ * @returns {void}
7319
+ */
7320
+ _releaseMemberExpression() {
7321
+ this._memberExpression = undefined;
7322
+ this._memberScope = undefined;
7323
+ this._memberType = 0;
7324
+ this._memberRootInfo = undefined;
7325
+ this._members = EMPTY_ARRAY;
7326
+ this._membersOptionals = EMPTY_MEMBERS_OPTIONALS;
7327
+ this._memberCall = undefined;
7328
+ this._memberCalleeMembers = EMPTY_ARRAY;
7329
+ this._memberInfo = undefined;
7330
+ }
7331
+
7332
+ /**
7333
+ * Gets member expression info.
7334
+ * @param {Expression | Super} expression a member expression
7335
+ * @param {number} allowedTypes which types should be returned, presented in bit mask
7336
+ * @returns {CallExpressionInfo | ExpressionExpressionInfo | undefined} expression info
7337
+ */
7338
+ getMemberExpressionInfo(expression, allowedTypes) {
7339
+ const type = this._resolveMemberExpression(expression, allowedTypes);
7340
+ if (type === 0) return;
7341
+ const cached = this._memberInfo;
7342
+ if (cached !== undefined) return cached;
7343
+ const members = this._members;
7344
+ const membersOptionals = this._membersOptionals;
7345
+ /** @type {MemberRanges | undefined} */
7346
+ let memberRanges;
7347
+ const getMembers = () => members;
7348
+ const getMembersOptionals = () => membersOptionals;
7349
+ const getMemberRanges = () =>
7350
+ memberRanges ||
7351
+ (memberRanges = memberRangesOf(expression, members.length));
7352
+ const rootInfo = /** @type {string | VariableInfo} */ (
7353
+ this._memberRootInfo
7354
+ );
7355
+ /** @type {CallExpressionInfo | ExpressionExpressionInfo} */
7356
+ let info;
7357
+ if (type === 2) {
7358
+ const calleeMembers = this._memberCalleeMembers;
7359
+ info = {
7360
+ type: "call",
7361
+ call: /** @type {CallExpression} */ (this._memberCall),
7362
+ calleeName: this._memberCalleeName,
7363
+ rootInfo,
7364
+ getCalleeMembers: () => calleeMembers,
7365
+ name: this._memberName,
7366
+ getMembers,
7367
+ getMembersOptionals,
7368
+ getMemberRanges
7369
+ };
7370
+ } else {
7371
+ info = {
7372
+ type: "expression",
7373
+ name: this._memberName,
7374
+ rootInfo,
7375
+ getMembers,
7376
+ getMembersOptionals,
7377
+ getMemberRanges
7378
+ };
6307
7379
  }
7380
+ this._memberInfo = info;
7381
+ return info;
6308
7382
  }
6309
7383
 
6310
7384
  /**
@@ -6373,7 +7447,10 @@ class JavascriptParser extends Parser {
6373
7447
  // syntax so `auto` avoids a second full parse in the common case
6374
7448
  parserOptions.moduleFallback = type === "auto";
6375
7449
  }
6376
- const wantRanges = parserOptions.ranges === true;
7450
+ // `locations` are acorn's to track, so the lazy path serves the callers
7451
+ // that ask for ranges alone — which is every one inside webpack
7452
+ const wantLazyNodes =
7453
+ parserOptions.ranges === true && parserOptions.locations !== true;
6377
7454
  /**
6378
7455
  * Returns parse result.
6379
7456
  * @param {string} code source code
@@ -6388,34 +7465,27 @@ class JavascriptParser extends Parser {
6388
7465
  /** @type {Comment[]} */
6389
7466
  const comments = [];
6390
7467
 
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) {
7468
+ // Ranges alone (the main parse path and the concatenation re-parse) are
7469
+ // served on demand; hoisted, since the auto-fallback re-enters here.
7470
+ options.lazyNodes = wantLazyNodes;
7471
+ if (wantLazyNodes) {
6397
7472
  // the lazy plugin replaces acorn's native tracking — disabling it
6398
7473
  // here lets WebpackParser skip its defensive options copy
6399
- options.locations = false;
6400
7474
  options.ranges = false;
6401
7475
  }
6402
7476
 
6403
7477
  if (options.comments) {
6404
- if (wantRanges) {
7478
+ if (wantLazyNodes) {
6405
7479
  // the parser plugin collects the comments itself, deferring the
6406
7480
  // text slice until some hook actually reads `value`
6407
7481
  options.lazyComments = comments;
6408
7482
  } else {
6409
- /** @type {AcornOptions} */
6410
- (options).onComment =
6411
- /** @type {import("acorn").Comment[]} */
6412
- (/** @type {unknown} */ (comments));
7483
+ /** @type {ParserOptions} */
7484
+ (options).onComment = comments;
6413
7485
  }
6414
7486
  }
6415
7487
 
6416
- const ast =
6417
- /** @type {Program} */
6418
- (parser.parse(code, /** @type {AcornOptions} */ (options)));
7488
+ const ast = /** @type {Program} */ (parser.parse(code, options));
6419
7489
 
6420
7490
  return { ast, comments };
6421
7491
  };
@@ -6451,7 +7521,7 @@ class JavascriptParser extends Parser {
6451
7521
  // release per-parse state retained on the (possibly reused) options
6452
7522
  // object — the comments array retains source text slices
6453
7523
  parserOptions.lazyComments = undefined;
6454
- /** @type {AcornOptions} */
7524
+ /** @type {ParserOptions} */
6455
7525
  (parserOptions).onComment = undefined;
6456
7526
 
6457
7527
  if (threw) {
@@ -6463,7 +7533,7 @@ class JavascriptParser extends Parser {
6463
7533
 
6464
7534
  /**
6465
7535
  * Returns parser.
6466
- * @param {((BaseParser: AcornParser) => AcornParser)[]} plugins parser plugin
7536
+ * @param {ParserPlugin[]} plugins parser plugin
6467
7537
  * @returns {typeof JavascriptParser} parser
6468
7538
  */
6469
7539
  static extend(...plugins) {