@depup/webpack 5.109.0-depup.0 → 5.110.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 (641) hide show
  1. package/README.md +7 -7
  2. package/bin/webpack.js +12 -0
  3. package/changes.json +10 -10
  4. package/lib/APIPlugin.js +123 -17
  5. package/lib/AsyncDependenciesBlock.js +10 -7
  6. package/lib/AutomaticPrefetchPlugin.js +1 -1
  7. package/lib/BannerPlugin.js +10 -6
  8. package/lib/CacheFacade.js +6 -6
  9. package/lib/Chunk.js +14 -11
  10. package/lib/ChunkGraph.js +43 -32
  11. package/lib/ChunkGroup.js +7 -7
  12. package/lib/ChunkTemplate.js +35 -44
  13. package/lib/CircularModulesPlugin.js +205 -28
  14. package/lib/CleanPlugin.js +15 -16
  15. package/lib/CodeGenerationResults.js +11 -8
  16. package/lib/CompatibilityPlugin.js +38 -7
  17. package/lib/Compilation.js +709 -414
  18. package/lib/Compiler.js +69 -42
  19. package/lib/ConcatenationScope.js +117 -8
  20. package/lib/ConditionalInitFragment.js +3 -3
  21. package/lib/ConstPlugin.js +13 -11
  22. package/lib/ContextExclusionPlugin.js +1 -1
  23. package/lib/ContextModule.js +146 -54
  24. package/lib/ContextModuleFactory.js +46 -14
  25. package/lib/ContextReplacementPlugin.js +12 -14
  26. package/lib/DefinePlugin.js +168 -57
  27. package/lib/DependenciesBlock.js +9 -6
  28. package/lib/Dependency.js +36 -16
  29. package/lib/DependencyTemplate.js +11 -12
  30. package/lib/DependencyTemplates.js +3 -3
  31. package/lib/DotenvPlugin.js +50 -13
  32. package/lib/DynamicEntryPlugin.js +8 -4
  33. package/lib/EntryOptionPlugin.js +8 -4
  34. package/lib/EntryPlugin.js +2 -2
  35. package/lib/Entrypoint.js +6 -2
  36. package/lib/EnvironmentPlugin.js +2 -2
  37. package/lib/EvalDevToolModulePlugin.js +8 -4
  38. package/lib/EvalSourceMapDevToolPlugin.js +15 -8
  39. package/lib/ExportsInfo.js +123 -19
  40. package/lib/ExportsInfoApiPlugin.js +2 -4
  41. package/lib/ExternalModule.js +242 -84
  42. package/lib/ExternalModuleFactoryPlugin.js +97 -38
  43. package/lib/ExternalsPlugin.js +10 -6
  44. package/lib/FileSystemInfo.js +116 -42
  45. package/lib/FlagAllModulesAsUsedPlugin.js +2 -3
  46. package/lib/FlagDependencyExportsPlugin.js +76 -19
  47. package/lib/FlagDependencyUsagePlugin.js +63 -57
  48. package/lib/FlagEntryExportAsUsedPlugin.js +1 -1
  49. package/lib/Generator.js +36 -15
  50. package/lib/HotModuleReplacementPlugin.js +16 -44
  51. package/lib/IgnorePlugin.js +8 -4
  52. package/lib/IgnoreWarningsPlugin.js +2 -2
  53. package/lib/InitFragment.js +28 -36
  54. package/lib/JavascriptMetaInfoPlugin.js +35 -4
  55. package/lib/LazyBarrel.js +3 -4
  56. package/lib/LibraryTemplatePlugin.js +10 -6
  57. package/lib/LoaderOptionsPlugin.js +7 -3
  58. package/lib/LoaderTargetPlugin.js +1 -1
  59. package/lib/MainTemplate.js +49 -42
  60. package/lib/ManifestPlugin.js +12 -11
  61. package/lib/Module.js +62 -33
  62. package/lib/ModuleFactory.js +3 -3
  63. package/lib/ModuleFilenameHelpers.js +22 -6
  64. package/lib/ModuleGraph.js +195 -21
  65. package/lib/ModuleGraphConnection.js +6 -13
  66. package/lib/ModuleInfoHeaderPlugin.js +8 -10
  67. package/lib/ModuleTemplate.js +62 -63
  68. package/lib/MultiCompiler.js +130 -64
  69. package/lib/MultiStats.js +40 -9
  70. package/lib/MultiWatching.js +3 -3
  71. package/lib/NoEmitOnErrorsPlugin.js +1 -1
  72. package/lib/NodeStuffPlugin.js +29 -19
  73. package/lib/NormalModule.js +221 -156
  74. package/lib/NormalModuleFactory.js +563 -112
  75. package/lib/NormalModuleReplacementPlugin.js +5 -8
  76. package/lib/NullFactory.js +6 -2
  77. package/lib/OptionsApply.js +7 -3
  78. package/lib/Parser.js +7 -3
  79. package/lib/PlatformPlugin.js +2 -2
  80. package/lib/PrefetchPlugin.js +1 -1
  81. package/lib/ProgressPlugin.js +16 -13
  82. package/lib/ProvidePlugin.js +3 -5
  83. package/lib/RawModule.js +25 -17
  84. package/lib/RecordIdsPlugin.js +3 -3
  85. package/lib/RequestShortener.js +1 -1
  86. package/lib/ResolverFactory.js +2 -2
  87. package/lib/RuntimeGlobals.js +22 -10
  88. package/lib/RuntimeModule.js +29 -19
  89. package/lib/RuntimePlugin.js +346 -256
  90. package/lib/RuntimeTemplate.js +2792 -352
  91. package/lib/SelfModuleFactory.js +7 -3
  92. package/lib/SourceMapDevToolModuleOptionsPlugin.js +6 -2
  93. package/lib/SourceMapDevToolPlugin.js +30 -20
  94. package/lib/Stats.js +3 -4
  95. package/lib/Template.js +55 -25
  96. package/lib/TemplatedPathPlugin.js +96 -14
  97. package/lib/UseStrictPlugin.js +4 -6
  98. package/lib/WarnCaseSensitiveModulesPlugin.js +4 -4
  99. package/lib/WarnDeprecatedOptionPlugin.js +1 -1
  100. package/lib/WarnNoModeSetPlugin.js +1 -1
  101. package/lib/WatchIgnorePlugin.js +8 -6
  102. package/lib/Watching.js +8 -8
  103. package/lib/WebpackIsIncludedPlugin.js +2 -4
  104. package/lib/WebpackOptionsApply.js +308 -18
  105. package/lib/WebpackOptionsDefaulter.js +6 -2
  106. package/lib/asset/AssetBytesGenerator.js +12 -10
  107. package/lib/asset/AssetBytesParser.js +2 -4
  108. package/lib/asset/AssetGenerator.js +188 -95
  109. package/lib/asset/AssetModule.js +7 -3
  110. package/lib/asset/AssetModulesPlugin.js +110 -38
  111. package/lib/asset/AssetParser.js +9 -7
  112. package/lib/asset/AssetSourceGenerator.js +60 -13
  113. package/lib/asset/AssetSourceParser.js +2 -4
  114. package/lib/asset/RawDataUrlModule.js +24 -16
  115. package/lib/asset/WebManifestGenerator.js +8 -10
  116. package/lib/asset/WebManifestParser.js +24 -10
  117. package/lib/async-modules/AsyncModuleHelpers.js +2 -2
  118. package/lib/async-modules/AwaitDependenciesInitFragment.js +2 -2
  119. package/lib/async-modules/InferAsyncModulesPlugin.js +2 -2
  120. package/lib/async-modules/isGeneratorLowered.js +4 -4
  121. package/lib/buildChunkGraph.js +36 -27
  122. package/lib/bun/BunTargetPlugin.js +1 -1
  123. package/lib/cache/AddBuildDependenciesPlugin.js +1 -1
  124. package/lib/cache/AddManagedPathsPlugin.js +1 -1
  125. package/lib/cache/IdleFileCachePlugin.js +2 -2
  126. package/lib/cache/MemoryCachePlugin.js +18 -10
  127. package/lib/cache/MemoryWithGcCachePlugin.js +34 -22
  128. package/lib/cache/PackFileCacheStrategy.js +289 -28
  129. package/lib/cache/ResolverCachePlugin.js +38 -18
  130. package/lib/cache/getLazyHashedEtag.js +1 -2
  131. package/lib/cache/mergeEtags.js +1 -1
  132. package/lib/cli.js +1 -3
  133. package/lib/config/browserslistTargetHandler.js +41 -5
  134. package/lib/config/defaults.js +282 -81
  135. package/lib/config/defineConfig.js +10 -2
  136. package/lib/config/getClaimedAssetTypes.js +65 -0
  137. package/lib/config/normalization.js +36 -16
  138. package/lib/config/target.js +46 -3
  139. package/lib/container/ContainerEntryDependency.js +1 -1
  140. package/lib/container/ContainerEntryModule.js +24 -15
  141. package/lib/container/ContainerEntryModuleFactory.js +7 -3
  142. package/lib/container/ContainerPlugin.js +9 -10
  143. package/lib/container/ContainerReferencePlugin.js +6 -2
  144. package/lib/container/FallbackDependency.js +1 -1
  145. package/lib/container/FallbackModule.js +24 -17
  146. package/lib/container/FallbackModuleFactory.js +7 -3
  147. package/lib/container/HoistContainerReferencesPlugin.js +53 -66
  148. package/lib/container/ModuleFederationPlugin.js +7 -18
  149. package/lib/container/RemoteModule.js +27 -19
  150. package/lib/container/RemoteRuntimeModule.js +10 -8
  151. package/lib/container/moduleFederationHooks.js +32 -0
  152. package/lib/css/CssGenerator.js +178 -44
  153. package/lib/css/CssInjectStyleRuntimeModule.js +7 -5
  154. package/lib/css/CssLoadingRuntimeModule.js +82 -36
  155. package/lib/css/CssModule.js +10 -7
  156. package/lib/css/CssModulesPlugin.js +197 -106
  157. package/lib/css/CssParser.js +1248 -618
  158. package/lib/css/CssServerStylesRuntimeModule.js +54 -0
  159. package/lib/css/cssMinify.js +168 -0
  160. package/lib/css/data.js +5324 -0
  161. package/lib/css/syntax.js +10396 -952
  162. package/lib/debug/ProfilingPlugin.js +14 -9
  163. package/lib/deno/DenoTargetPlugin.js +1 -1
  164. package/lib/dependencies/AMDDefineDependency.js +112 -87
  165. package/lib/dependencies/AMDDefineDependencyParserPlugin.js +30 -17
  166. package/lib/dependencies/AMDPlugin.js +9 -7
  167. package/lib/dependencies/AMDRequireArrayDependency.js +5 -5
  168. package/lib/dependencies/AMDRequireContextDependency.js +2 -2
  169. package/lib/dependencies/AMDRequireDependenciesBlock.js +1 -1
  170. package/lib/dependencies/AMDRequireDependenciesBlockParserPlugin.js +25 -14
  171. package/lib/dependencies/AMDRequireDependency.js +8 -7
  172. package/lib/dependencies/AMDRequireItemDependency.js +1 -1
  173. package/lib/dependencies/AMDRuntimeModules.js +21 -1
  174. package/lib/dependencies/BuildTimeConstDependency.js +102 -0
  175. package/lib/dependencies/CachedConstDependency.js +11 -8
  176. package/lib/dependencies/CommonJsDependencyHelpers.js +67 -46
  177. package/lib/dependencies/CommonJsExportRequireDependency.js +140 -45
  178. package/lib/dependencies/CommonJsExportsDependency.js +53 -49
  179. package/lib/dependencies/CommonJsExportsParserPlugin.js +207 -25
  180. package/lib/dependencies/CommonJsFullRequireDependency.js +140 -26
  181. package/lib/dependencies/CommonJsImportsParserPlugin.js +100 -48
  182. package/lib/dependencies/CommonJsObjectExportDependency.js +188 -0
  183. package/lib/dependencies/CommonJsPlugin.js +35 -6
  184. package/lib/dependencies/CommonJsRequireContextDependency.js +5 -6
  185. package/lib/dependencies/CommonJsRequireDependency.js +156 -27
  186. package/lib/dependencies/CommonJsSelfReferenceDependency.js +63 -38
  187. package/lib/dependencies/ConstDependency.js +8 -9
  188. package/lib/dependencies/ContextDependency.js +12 -8
  189. package/lib/dependencies/ContextDependencyHelpers.js +14 -9
  190. package/lib/dependencies/ContextDependencyTemplateAsId.js +4 -4
  191. package/lib/dependencies/ContextDependencyTemplateAsRequireCall.js +4 -4
  192. package/lib/dependencies/ContextElementDependency.js +31 -12
  193. package/lib/dependencies/CreateRequireParserPlugin.js +12 -7
  194. package/lib/dependencies/CreateScriptUrlDependency.js +4 -4
  195. package/lib/dependencies/CssIcssExportDependency.js +56 -28
  196. package/lib/dependencies/CssIcssImportDependency.js +17 -13
  197. package/lib/dependencies/CssIcssSymbolDependency.js +9 -10
  198. package/lib/dependencies/CssImportDependency.js +18 -9
  199. package/lib/dependencies/CssUrlDependency.js +17 -13
  200. package/lib/dependencies/DllEntryDependency.js +1 -1
  201. package/lib/dependencies/DynamicExports.js +2 -2
  202. package/lib/dependencies/ExportBindingInitFragment.js +36 -9
  203. package/lib/dependencies/ExportsInfoDependency.js +7 -8
  204. package/lib/dependencies/ExternalModuleDependency.js +9 -5
  205. package/lib/dependencies/ExternalModuleInitFragment.js +2 -2
  206. package/lib/dependencies/ExternalModuleInitFragmentDependency.js +8 -5
  207. package/lib/dependencies/HarmonyAcceptDependency.js +5 -5
  208. package/lib/dependencies/HarmonyCompatibilityDependency.js +52 -17
  209. package/lib/dependencies/HarmonyDetectionParserPlugin.js +11 -7
  210. package/lib/dependencies/HarmonyEvaluatedImportSpecifierDependency.js +15 -11
  211. package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +78 -12
  212. package/lib/dependencies/HarmonyExportExpressionDependency.js +23 -18
  213. package/lib/dependencies/HarmonyExportHeaderDependency.js +7 -7
  214. package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +159 -71
  215. package/lib/dependencies/HarmonyExportInitFragment.js +18 -6
  216. package/lib/dependencies/HarmonyExportSpecifierDependency.js +51 -14
  217. package/lib/dependencies/HarmonyExports.js +7 -4
  218. package/lib/dependencies/HarmonyImportDependency.js +28 -20
  219. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +31 -24
  220. package/lib/dependencies/HarmonyImportGuard.js +21 -30
  221. package/lib/dependencies/HarmonyImportSideEffectDependency.js +38 -9
  222. package/lib/dependencies/HarmonyImportSpecifierDependency.js +106 -55
  223. package/lib/dependencies/HarmonyModulesPlugin.js +7 -3
  224. package/lib/dependencies/HarmonyTopLevelThisParserPlugin.js +1 -3
  225. package/lib/dependencies/HtmlEntryDependency.js +37 -29
  226. package/lib/dependencies/HtmlInlineHtmlDependency.js +7 -8
  227. package/lib/dependencies/HtmlInlineScriptDependency.js +11 -8
  228. package/lib/dependencies/HtmlInlineStyleDependency.js +7 -10
  229. package/lib/dependencies/HtmlSourceDependency.js +22 -14
  230. package/lib/dependencies/ImportContextDependency.js +2 -2
  231. package/lib/dependencies/ImportDependency.js +24 -20
  232. package/lib/dependencies/ImportEagerDependency.js +6 -9
  233. package/lib/dependencies/ImportMetaContextDependency.js +2 -2
  234. package/lib/dependencies/ImportMetaContextDependencyParserPlugin.js +14 -13
  235. package/lib/dependencies/ImportMetaContextPlugin.js +7 -3
  236. package/lib/dependencies/ImportMetaGlobDependency.js +2 -2
  237. package/lib/dependencies/ImportMetaGlobDependencyParserPlugin.js +2 -3
  238. package/lib/dependencies/ImportMetaGlobHelpers.js +19 -22
  239. package/lib/dependencies/ImportMetaHotAcceptDependency.js +1 -1
  240. package/lib/dependencies/ImportMetaHotDeclineDependency.js +1 -1
  241. package/lib/dependencies/ImportMetaPlugin.js +154 -71
  242. package/lib/dependencies/ImportMetaResolveDependency.js +3 -4
  243. package/lib/dependencies/ImportParserPlugin.js +49 -35
  244. package/lib/dependencies/ImportPhase.js +12 -5
  245. package/lib/dependencies/ImportPlugin.js +7 -3
  246. package/lib/dependencies/ImportWeakDependency.js +6 -9
  247. package/lib/dependencies/JsonExportsDependency.js +11 -7
  248. package/lib/dependencies/LoaderDependency.js +2 -2
  249. package/lib/dependencies/LoaderImportDependency.js +2 -2
  250. package/lib/dependencies/LoaderPlugin.js +11 -7
  251. package/lib/dependencies/LocalModuleDependency.js +5 -5
  252. package/lib/dependencies/LocalModulesHelpers.js +1 -1
  253. package/lib/dependencies/ModuleDecoratorDependency.js +6 -7
  254. package/lib/dependencies/ModuleDependency.js +9 -6
  255. package/lib/dependencies/ModuleDependencyTemplateAsId.js +4 -4
  256. package/lib/dependencies/ModuleDependencyTemplateAsRequireId.js +3 -3
  257. package/lib/dependencies/ModuleHotAcceptDependency.js +1 -1
  258. package/lib/dependencies/ModuleHotDeclineDependency.js +1 -1
  259. package/lib/dependencies/ModuleInitFragmentDependency.js +24 -10
  260. package/lib/dependencies/NullDependency.js +3 -3
  261. package/lib/dependencies/ProvidedDependency.js +14 -10
  262. package/lib/dependencies/PureExpressionDependency.js +9 -10
  263. package/lib/dependencies/RequireContextDependency.js +2 -2
  264. package/lib/dependencies/RequireContextDependencyParserPlugin.js +2 -4
  265. package/lib/dependencies/RequireContextPlugin.js +8 -4
  266. package/lib/dependencies/RequireEnsureDependenciesBlock.js +2 -2
  267. package/lib/dependencies/RequireEnsureDependenciesBlockParserPlugin.js +5 -8
  268. package/lib/dependencies/RequireEnsureDependency.js +8 -7
  269. package/lib/dependencies/RequireEnsurePlugin.js +7 -3
  270. package/lib/dependencies/RequireHeaderDependency.js +51 -12
  271. package/lib/dependencies/RequireIncludeDependency.js +6 -6
  272. package/lib/dependencies/RequireIncludeDependencyParserPlugin.js +2 -3
  273. package/lib/dependencies/RequireIncludePlugin.js +7 -3
  274. package/lib/dependencies/RequireJsStuffPlugin.js +7 -3
  275. package/lib/dependencies/RequireResolveContextDependency.js +2 -2
  276. package/lib/dependencies/RequireResolveDependency.js +4 -4
  277. package/lib/dependencies/RequireResolveHeaderDependency.js +4 -4
  278. package/lib/dependencies/RuntimeRequirementsDependency.js +5 -6
  279. package/lib/dependencies/StaticExportsDependency.js +2 -2
  280. package/lib/dependencies/SystemPlugin.js +10 -6
  281. package/lib/dependencies/SystemRuntimeModule.js +10 -0
  282. package/lib/dependencies/TopLevelAwaitDependency.js +11 -7
  283. package/lib/dependencies/URLContextDependency.js +2 -2
  284. package/lib/dependencies/URLDependency.js +110 -64
  285. package/lib/dependencies/URLPlugin.js +7 -3
  286. package/lib/dependencies/UnsupportedDependency.js +4 -4
  287. package/lib/dependencies/WebAssemblyExportImportedDependency.js +3 -4
  288. package/lib/dependencies/WebAssemblyImportDependency.js +5 -5
  289. package/lib/dependencies/WebpackIsIncludedDependency.js +6 -6
  290. package/lib/dependencies/WorkerAndWorkletPlugin.js +46 -28
  291. package/lib/dependencies/WorkerDependency.js +28 -22
  292. package/lib/dependencies/WorkletDependency.js +15 -14
  293. package/lib/dependencies/getFunctionExpression.js +8 -4
  294. package/lib/dependencies/processExportInfo.js +5 -5
  295. package/lib/dll/DelegatedModule.js +34 -22
  296. package/lib/dll/DelegatedModuleFactoryPlugin.js +15 -7
  297. package/lib/dll/DelegatedPlugin.js +2 -2
  298. package/lib/dll/DllEntryPlugin.js +2 -2
  299. package/lib/dll/DllModule.js +22 -15
  300. package/lib/dll/DllModuleFactory.js +7 -3
  301. package/lib/dll/DllPlugin.js +7 -5
  302. package/lib/dll/DllReferencePlugin.js +10 -7
  303. package/lib/dll/LibManifestPlugin.js +5 -5
  304. package/lib/electron/ElectronTargetPlugin.js +53 -49
  305. package/lib/errors/AsyncChunkWaterfallWarning.js +36 -0
  306. package/lib/errors/AsyncDependencyToInitialChunkError.js +2 -2
  307. package/lib/errors/BroadContextsWarning.js +43 -0
  308. package/lib/errors/BuildCycleError.js +1 -1
  309. package/lib/errors/CacheEffectivenessWarning.js +55 -0
  310. package/lib/errors/ChunkRenderError.js +1 -1
  311. package/lib/errors/CircularDependenciesWarning.js +38 -0
  312. package/lib/errors/CodeGenerationError.js +1 -1
  313. package/lib/errors/CommentCompilationWarning.js +1 -1
  314. package/lib/errors/ConflictingResourceHintsWarning.js +35 -0
  315. package/lib/errors/DuplicateModulesWarning.js +45 -0
  316. package/lib/errors/DynamicExportsWarning.js +30 -0
  317. package/lib/errors/EmbeddedSourceMapsWarning.js +25 -0
  318. package/lib/errors/EntrypointOverlapWarning.js +45 -0
  319. package/lib/errors/EnvironmentNotSupportAsyncWarning.js +2 -2
  320. package/lib/errors/EvalUsageWarning.js +28 -0
  321. package/lib/errors/HookWebpackError.js +5 -5
  322. package/lib/errors/HotspotsWarning.js +57 -0
  323. package/lib/errors/IgnoreErrorModuleFactory.js +7 -3
  324. package/lib/errors/InlinedAssetsWarning.js +36 -0
  325. package/lib/errors/InvalidDependenciesModuleWarning.js +1 -1
  326. package/lib/errors/LargeModulesWarning.js +45 -0
  327. package/lib/errors/LegacyJavascriptWarning.js +48 -0
  328. package/lib/errors/MissingSideEffectsWarning.js +36 -0
  329. package/lib/errors/MixedExportsWarning.js +41 -0
  330. package/lib/errors/ModuleDependencyError.js +13 -16
  331. package/lib/errors/ModuleDependencyWarning.js +5 -15
  332. package/lib/errors/ModuleHashingError.js +1 -1
  333. package/lib/errors/ModuleNotFoundError.js +2 -2
  334. package/lib/errors/ModuleParseError.js +1 -2
  335. package/lib/errors/ModuleRestoreError.js +1 -1
  336. package/lib/errors/ModuleStoreError.js +1 -1
  337. package/lib/errors/NodeStuffInWebError.js +1 -1
  338. package/lib/errors/OsDependentRuleWarning.js +31 -0
  339. package/lib/errors/PureAnnotationsWarning.js +36 -0
  340. package/lib/errors/RedundantDynamicImportWarning.js +31 -0
  341. package/lib/errors/ScopeHoistingBailoutsWarning.js +41 -0
  342. package/lib/errors/SplitChunksCappedWarning.js +41 -0
  343. package/lib/errors/TinyChunksWarning.js +40 -0
  344. package/lib/errors/TopLevelThisWarning.js +34 -0
  345. package/lib/errors/UnsplitVendorsWarning.js +42 -0
  346. package/lib/errors/UnsupportedFeatureWarning.js +1 -1
  347. package/lib/errors/UnusedAliasesWarning.js +29 -0
  348. package/lib/errors/UnusedDefinesWarning.js +29 -0
  349. package/lib/errors/UnusedExternalsWarning.js +29 -0
  350. package/lib/errors/UnusedReexportsWarning.js +42 -0
  351. package/lib/errors/UnusedRuleWarning.js +29 -0
  352. package/lib/errors/WebpackError.js +9 -5
  353. package/lib/errors/deriveStackFromNestedError.js +90 -0
  354. package/lib/esm/ExportWebpackRequireRuntimeModule.js +2 -0
  355. package/lib/esm/ModuleChunkFormatPlugin.js +37 -19
  356. package/lib/esm/ModuleChunkLoadingPlugin.js +89 -51
  357. package/lib/esm/ModuleChunkLoadingRuntimeModule.js +170 -71
  358. package/lib/hmr/HotModuleReplacement.runtime.js +24 -5
  359. package/lib/hmr/HotModuleReplacementRuntimeModule.js +1 -1
  360. package/lib/hmr/LazyCompilationPlugin.js +45 -24
  361. package/lib/hmr/lazyCompilationBackend.js +28 -10
  362. package/lib/hmr/parserHooks.js +56 -0
  363. package/lib/html/HtmlGenerator.js +299 -79
  364. package/lib/html/HtmlModule.js +7 -3
  365. package/lib/html/HtmlModulesPlugin.js +312 -174
  366. package/lib/html/HtmlParser.js +31 -86
  367. package/lib/html/data.js +1928 -0
  368. package/lib/html/htmlMinify.js +288 -0
  369. package/lib/html/syntax.js +7819 -3331
  370. package/lib/ids/ChunkModuleIdRangePlugin.js +3 -3
  371. package/lib/ids/DeterministicChunkIdsPlugin.js +1 -1
  372. package/lib/ids/DeterministicModuleIdsPlugin.js +2 -2
  373. package/lib/ids/HashedModuleIdsPlugin.js +6 -2
  374. package/lib/ids/IdHelpers.js +31 -18
  375. package/lib/ids/NamedChunkIdsPlugin.js +1 -1
  376. package/lib/ids/NamedModuleIdsPlugin.js +1 -1
  377. package/lib/ids/NaturalChunkIdsPlugin.js +2 -2
  378. package/lib/ids/NaturalModuleIdsPlugin.js +1 -1
  379. package/lib/ids/OccurrenceChunkIdsPlugin.js +7 -3
  380. package/lib/ids/OccurrenceModuleIdsPlugin.js +7 -3
  381. package/lib/ids/SyncModuleIdsPlugin.js +4 -4
  382. package/lib/index.js +10 -0
  383. package/lib/javascript/ArrayPushCallbackChunkFormatPlugin.js +3 -3
  384. package/lib/javascript/BasicEvaluatedExpression.js +18 -14
  385. package/lib/javascript/ChunkFormatHelpers.js +6 -6
  386. package/lib/javascript/ChunkHelpers.js +1 -1
  387. package/lib/javascript/CommonJsChunkFormatPlugin.js +11 -5
  388. package/lib/javascript/EnableChunkLoadingPlugin.js +2 -2
  389. package/lib/javascript/JavascriptGenerator.js +65 -165
  390. package/lib/javascript/JavascriptModule.js +9 -3
  391. package/lib/javascript/JavascriptModulesPlugin.js +181 -162
  392. package/lib/javascript/JavascriptParser.js +291 -112
  393. package/lib/javascript/JavascriptParserHelpers.js +10 -7
  394. package/lib/javascript/ScopeAnalyzer.js +1094 -0
  395. package/lib/javascript/StartupHelpers.js +55 -9
  396. package/lib/javascript/syntax.js +3403 -300
  397. package/lib/json/JsonData.js +8 -4
  398. package/lib/json/JsonGenerator.js +38 -16
  399. package/lib/json/JsonModule.js +7 -3
  400. package/lib/json/JsonModulesPlugin.js +1 -1
  401. package/lib/json/JsonParser.js +5 -6
  402. package/lib/library/AbstractLibraryPlugin.js +20 -14
  403. package/lib/library/AmdLibraryPlugin.js +11 -7
  404. package/lib/library/AssignLibraryPlugin.js +19 -13
  405. package/lib/library/EnableLibraryPlugin.js +2 -2
  406. package/lib/library/ExportPropertyLibraryPlugin.js +15 -8
  407. package/lib/library/JsonpLibraryPlugin.js +11 -7
  408. package/lib/library/ModuleLibraryPlugin.js +256 -64
  409. package/lib/library/SystemLibraryPlugin.js +12 -8
  410. package/lib/library/UmdLibraryPlugin.js +62 -25
  411. package/lib/loaders/LoaderRunner.js +14 -1
  412. package/lib/loaders/loadLoader.js +8 -4
  413. package/lib/logging/createConsoleLogger.js +7 -4
  414. package/lib/node/CommonJsChunkLoadingPlugin.js +36 -22
  415. package/lib/node/NodeEnvironmentPlugin.js +17 -3
  416. package/lib/node/NodeSourcePlugin.js +1 -1
  417. package/lib/node/NodeTargetPlugin.js +4 -3
  418. package/lib/node/NodeTemplatePlugin.js +1 -1
  419. package/lib/node/NodeWatchFileSystem.js +70 -9
  420. package/lib/node/ReadFileChunkLoadingRuntimeModule.js +35 -38
  421. package/lib/node/ReadFileCompileAsyncWasmPlugin.js +81 -20
  422. package/lib/node/ReadFileCompileWasmPlugin.js +37 -17
  423. package/lib/node/RequireChunkLoadingRuntimeModule.js +35 -38
  424. package/lib/node/nodeConsole.js +7 -3
  425. package/lib/optimize/AggressiveMergingPlugin.js +21 -17
  426. package/lib/optimize/AggressiveSplittingPlugin.js +9 -6
  427. package/lib/optimize/ConcatenatedModule.js +996 -477
  428. package/lib/optimize/ConstExportsPlugin.js +11 -7
  429. package/lib/optimize/EnsureChunkConditionsPlugin.js +3 -3
  430. package/lib/optimize/FlagIncludedChunksPlugin.js +3 -4
  431. package/lib/optimize/InlineExports.js +9 -5
  432. package/lib/optimize/InnerGraph.js +28 -12
  433. package/lib/optimize/InnerGraphPlugin.js +23 -18
  434. package/lib/optimize/LimitChunkCountPlugin.js +7 -3
  435. package/lib/optimize/MangleExportsPlugin.js +7 -8
  436. package/lib/optimize/MergeDuplicateChunksPlugin.js +7 -3
  437. package/lib/optimize/MinChunkSizePlugin.js +7 -3
  438. package/lib/optimize/ModuleConcatenationPlugin.js +124 -59
  439. package/lib/optimize/RealContentHashPlugin.js +20 -13
  440. package/lib/optimize/RemoveEmptyChunksPlugin.js +2 -2
  441. package/lib/optimize/RemoveParentModulesPlugin.js +4 -4
  442. package/lib/optimize/RuntimeChunkPlugin.js +2 -2
  443. package/lib/optimize/SideEffectsFlagPlugin.js +262 -39
  444. package/lib/optimize/SplitChunksPlugin.js +232 -68
  445. package/lib/performance/AssetsOverSizeLimitWarning.js +10 -2
  446. package/lib/performance/AsyncChunkWaterfallsPlugin.js +136 -0
  447. package/lib/performance/BroadContextsPlugin.js +142 -0
  448. package/lib/performance/CacheEffectivenessPlugin.js +125 -0
  449. package/lib/performance/ConflictingResourceHintsPlugin.js +99 -0
  450. package/lib/performance/DuplicateModulesPlugin.js +93 -0
  451. package/lib/performance/DuplicatePackagesPlugin.js +119 -0
  452. package/lib/performance/DuplicatePackagesWarning.js +84 -0
  453. package/lib/performance/DynamicExportsPlugin.js +120 -0
  454. package/lib/performance/EmbeddedSourceMapsPlugin.js +71 -0
  455. package/lib/performance/EntrypointOverlapPlugin.js +122 -0
  456. package/lib/performance/EntrypointsOverSizeLimitWarning.js +1 -1
  457. package/lib/performance/EvalUsagePlugin.js +115 -0
  458. package/lib/performance/HotspotsPlugin.js +296 -0
  459. package/lib/performance/InlinedAssetsPlugin.js +99 -0
  460. package/lib/performance/LargeModulesPlugin.js +126 -0
  461. package/lib/performance/LegacyJavascriptPlugin.js +139 -0
  462. package/lib/performance/MissingSideEffectsPlugin.js +138 -0
  463. package/lib/performance/MixedExportsPlugin.js +136 -0
  464. package/lib/performance/OsDependentRuleWarningPlugin.js +178 -0
  465. package/lib/performance/PureAnnotationsPlugin.js +219 -0
  466. package/lib/performance/RedundantDynamicImportsPlugin.js +225 -0
  467. package/lib/performance/RuntimeInLargeChunkWarning.js +27 -0
  468. package/lib/performance/ScopeHoistingBailoutsPlugin.js +164 -0
  469. package/lib/performance/SizeLimitsPlugin.js +139 -7
  470. package/lib/performance/SplitChunksCappedPlugin.js +99 -0
  471. package/lib/performance/TinyChunksPlugin.js +128 -0
  472. package/lib/performance/TopLevelThisPlugin.js +208 -0
  473. package/lib/performance/UnsplitVendorsPlugin.js +112 -0
  474. package/lib/performance/UnusedAliasesPlugin.js +253 -0
  475. package/lib/performance/UnusedDefinesPlugin.js +87 -0
  476. package/lib/performance/UnusedExternalsPlugin.js +110 -0
  477. package/lib/performance/UnusedReexportsPlugin.js +159 -0
  478. package/lib/performance/UnusedRuleWarningPlugin.js +105 -0
  479. package/lib/performance/getModuleSize.js +22 -0
  480. package/lib/performance/getSourceModules.js +22 -0
  481. package/lib/performance/hasOnlyUnusedExports.js +40 -0
  482. package/lib/prefetch/ChunkPrefetchFunctionRuntimeModule.js +24 -10
  483. package/lib/prefetch/ChunkPrefetchPreloadPlugin.js +63 -46
  484. package/lib/prefetch/ChunkPrefetchStartupRuntimeModule.js +2 -3
  485. package/lib/prefetch/ChunkPrefetchTriggerRuntimeModule.js +2 -2
  486. package/lib/prefetch/ChunkPreloadTriggerRuntimeModule.js +2 -2
  487. package/lib/prefetch/ResourceHintPlugin.js +377 -136
  488. package/lib/prefetch/ResourceHintRuntimeModule.js +1 -21
  489. package/lib/prefetch/StartupAssetHintRuntimeModule.js +10 -133
  490. package/lib/prefetch/parseResourceHintOptions.js +31 -4
  491. package/lib/rules/BasicEffectRulePlugin.js +2 -2
  492. package/lib/rules/BasicMatcherRulePlugin.js +8 -4
  493. package/lib/rules/GlobMatcherRulePlugin.js +68 -0
  494. package/lib/rules/ObjectMatcherRulePlugin.js +12 -5
  495. package/lib/rules/RuleSetCompiler.js +146 -12
  496. package/lib/rules/UseEffectRulePlugin.js +18 -11
  497. package/lib/runtime/AsyncModuleGeneratorRuntimeModule.js +11 -1
  498. package/lib/runtime/AsyncModuleRuntimeModule.js +1 -1
  499. package/lib/runtime/AutoPublicPathRuntimeModule.js +15 -20
  500. package/lib/runtime/BaseUriRuntimeModule.js +33 -7
  501. package/lib/runtime/ChunkNameRuntimeModule.js +10 -0
  502. package/lib/runtime/CompatGetDefaultExportRuntimeModule.js +11 -1
  503. package/lib/runtime/CompatRuntimeModule.js +5 -3
  504. package/lib/runtime/ConcatenationWrapRuntimeModule.js +90 -0
  505. package/lib/runtime/ConstructRequireRuntimeModule.js +52 -0
  506. package/lib/runtime/CreateFakeNamespaceObjectRuntimeModule.js +8 -6
  507. package/lib/runtime/CreateScriptRuntimeModule.js +11 -1
  508. package/lib/runtime/CreateScriptUrlRuntimeModule.js +11 -1
  509. package/lib/runtime/DefinePropertyGettersRuntimeModule.js +56 -38
  510. package/lib/runtime/EnsureChunkRuntimeModule.js +18 -2
  511. package/lib/runtime/GetChunkFilenameRuntimeModule.js +62 -37
  512. package/lib/runtime/GetFullHashRuntimeModule.js +12 -2
  513. package/lib/runtime/GetMainFilenameRuntimeModule.js +12 -2
  514. package/lib/runtime/GetTrustedTypesPolicyRuntimeModule.js +2 -2
  515. package/lib/runtime/GetWorkletBootstrapRuntimeModule.js +16 -3
  516. package/lib/runtime/GlobalRuntimeModule.js +18 -0
  517. package/lib/runtime/HasOwnPropertyRuntimeModule.js +12 -2
  518. package/lib/runtime/LoadScriptRuntimeModule.js +2 -2
  519. package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +23 -6
  520. package/lib/runtime/MakeNamespaceObjectRuntimeModule.js +22 -6
  521. package/lib/runtime/NonceRuntimeModule.js +10 -0
  522. package/lib/runtime/OnChunksLoadedRuntimeModule.js +5 -3
  523. package/lib/runtime/PublicPathRuntimeModule.js +12 -2
  524. package/lib/runtime/RelativeUrlRuntimeModule.js +11 -1
  525. package/lib/runtime/RuntimeIdRuntimeModule.js +12 -2
  526. package/lib/runtime/SetAnonymousDefaultNameRuntimeModule.js +11 -1
  527. package/lib/runtime/StartupChunkDependenciesPlugin.js +18 -9
  528. package/lib/runtime/StartupChunkDependenciesRuntimeModule.js +3 -3
  529. package/lib/runtime/StartupEntrypointRuntimeModule.js +21 -4
  530. package/lib/runtime/SystemContextRuntimeModule.js +10 -0
  531. package/lib/runtime/ToBinaryRuntimeModule.js +1 -1
  532. package/lib/runtime/WorkerRuntimeModule.js +11 -1
  533. package/lib/runtime/baseUri.js +63 -0
  534. package/lib/schemes/DataUriPlugin.js +1 -1
  535. package/lib/schemes/FileUriPlugin.js +1 -1
  536. package/lib/schemes/HttpUriPlugin.js +24 -15
  537. package/lib/schemes/VirtualUrlPlugin.js +13 -7
  538. package/lib/serialization/AggregateErrorSerializer.js +1 -1
  539. package/lib/serialization/BinaryMiddleware.js +274 -942
  540. package/lib/serialization/ErrorObjectSerializer.js +1 -1
  541. package/lib/serialization/FileMiddleware.js +305 -66
  542. package/lib/serialization/ObjectMiddleware.js +139 -114
  543. package/lib/serialization/PlainObjectSerializer.js +19 -16
  544. package/lib/sharing/ConsumeSharedModule.js +30 -21
  545. package/lib/sharing/ConsumeSharedPlugin.js +18 -10
  546. package/lib/sharing/ConsumeSharedRuntimeModule.js +6 -9
  547. package/lib/sharing/ProvideSharedModule.js +25 -16
  548. package/lib/sharing/ProvideSharedModuleFactory.js +7 -3
  549. package/lib/sharing/ProvideSharedPlugin.js +8 -4
  550. package/lib/sharing/SharePlugin.js +17 -5
  551. package/lib/sharing/ShareRuntimeModule.js +4 -4
  552. package/lib/sharing/resolveMatchedConfigs.js +9 -6
  553. package/lib/sharing/utils.js +1 -3
  554. package/lib/stats/DefaultStatsFactoryPlugin.js +253 -226
  555. package/lib/stats/DefaultStatsPresetPlugin.js +17 -8
  556. package/lib/stats/DefaultStatsPrinterPlugin.js +60 -192
  557. package/lib/stats/StatsFactory.js +56 -35
  558. package/lib/stats/StatsPrinter.js +16 -12
  559. package/lib/typescript/TypeScriptPlugin.js +12 -14
  560. package/lib/url/URLParserPlugin.js +26 -13
  561. package/lib/util/AsyncQueue.js +4 -3
  562. package/lib/util/Hash.js +5 -1
  563. package/lib/util/LazySet.js +35 -22
  564. package/lib/util/LocConverter.js +36 -13
  565. package/lib/util/SourceProcessor.js +1050 -24
  566. package/lib/util/StringXor.js +1 -1
  567. package/lib/util/chainedImports.js +9 -5
  568. package/lib/util/cleverMerge.js +1 -1
  569. package/lib/util/comparators.js +45 -14
  570. package/lib/util/concatenate.js +203 -77
  571. package/lib/util/conventions.js +5 -1
  572. package/lib/util/createHash.js +2 -2
  573. package/lib/util/createHooksRegistry.js +8 -10
  574. package/lib/util/dataURL.js +225 -1
  575. package/lib/util/extractSourceMap.js +4 -3
  576. package/lib/util/findCaseMismatch.js +139 -0
  577. package/lib/util/formatLocation.js +1 -2
  578. package/lib/util/fs.js +22 -5
  579. package/lib/util/globUtils.js +298 -24
  580. package/lib/util/hash/BatchedHash.js +5 -1
  581. package/lib/util/hash/BulkUpdateHash.js +5 -1
  582. package/lib/util/hash/DebugHash.js +5 -1
  583. package/lib/util/hash/hash-digest.js +6 -2
  584. package/lib/util/identifier.js +25 -1
  585. package/lib/util/implicitTypeLoaderFallback.js +1 -1
  586. package/lib/util/internalSerializables.js +6 -0
  587. package/lib/util/lazyModule.js +56 -0
  588. package/lib/util/makeSerializable.js +7 -3
  589. package/lib/util/matchAlias.js +25 -0
  590. package/lib/util/mimeTypes.js +11 -5
  591. package/lib/util/nonNumericOnlyHash.js +4 -3
  592. package/lib/util/parseJson.js +5 -3
  593. package/lib/util/preloadModuleType.js +32 -0
  594. package/lib/util/registerExternalSerializer.js +7 -5
  595. package/lib/util/runtime.js +2 -2
  596. package/lib/util/semver.js +1 -1
  597. package/lib/util/serialization.js +28 -28
  598. package/lib/util/source.js +2 -2
  599. package/lib/util/traverseDestructuringAssignmentProperties.js +6 -2
  600. package/lib/wasm/EnableWasmLoadingPlugin.js +10 -8
  601. package/lib/wasm/wasmModuleFilename.js +54 -0
  602. package/lib/wasm-async/AsyncWasmCompileRuntimeModule.js +41 -21
  603. package/lib/wasm-async/AsyncWasmLoadingRuntimeModule.js +43 -21
  604. package/lib/wasm-async/AsyncWasmModule.js +3 -3
  605. package/lib/wasm-async/AsyncWebAssemblyGenerator.js +4 -5
  606. package/lib/wasm-async/AsyncWebAssemblyJavascriptGenerator.js +57 -17
  607. package/lib/wasm-async/AsyncWebAssemblyModulesPlugin.js +34 -18
  608. package/lib/wasm-async/AsyncWebAssemblyParser.js +9 -8
  609. package/lib/wasm-async/UniversalCompileAsyncWasmPlugin.js +34 -8
  610. package/lib/wasm-sync/SyncWasmModule.js +2 -2
  611. package/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js +18 -13
  612. package/lib/wasm-sync/WasmFinalizeExportsPlugin.js +4 -5
  613. package/lib/wasm-sync/WebAssemblyGenerator.js +18 -17
  614. package/lib/wasm-sync/WebAssemblyInInitialChunkError.js +4 -4
  615. package/lib/wasm-sync/WebAssemblyJavascriptGenerator.js +7 -7
  616. package/lib/wasm-sync/WebAssemblyModulesPlugin.js +24 -11
  617. package/lib/wasm-sync/WebAssemblyParser.js +4 -6
  618. package/lib/wasm-sync/WebAssemblyUtils.js +2 -2
  619. package/lib/web/FetchCompileAsyncWasmPlugin.js +101 -19
  620. package/lib/web/FetchCompileWasmPlugin.js +46 -16
  621. package/lib/web/JsonpChunkLoadingPlugin.js +47 -23
  622. package/lib/web/JsonpChunkLoadingRuntimeModule.js +50 -20
  623. package/lib/web/JsonpTemplatePlugin.js +2 -2
  624. package/lib/webpack.js +22 -11
  625. package/lib/webworker/ImportScriptsChunkLoadingPlugin.js +60 -26
  626. package/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js +30 -32
  627. package/lib/webworker/WebWorkerTemplatePlugin.js +1 -1
  628. package/module.d.ts +1 -0
  629. package/package.json +37 -30
  630. package/schemas/WebpackOptions.check.js +1 -1
  631. package/schemas/WebpackOptions.json +780 -91
  632. package/schemas/plugins/HtmlParserOptions.check.js +1 -1
  633. package/schemas/plugins/container/ContainerPlugin.check.js +1 -1
  634. package/schemas/plugins/container/ContainerPlugin.json +10 -0
  635. package/schemas/plugins/container/ModuleFederationPlugin.check.js +1 -1
  636. package/schemas/plugins/container/ModuleFederationPlugin.json +10 -0
  637. package/schemas/plugins/css/CssAutoOrModuleParserOptions.check.js +1 -1
  638. package/schemas/plugins/css/CssModuleParserOptions.check.js +1 -1
  639. package/schemas/plugins/css/CssParserOptions.check.js +1 -1
  640. package/types.d.ts +4013 -738
  641. package/lib/runtime/CommonJsWrapRuntimeModule.js +0 -37
@@ -36,24 +36,69 @@ const keywordTypes =
36
36
  (/** @type {unknown} */ (require("acorn"))).keywordTypes
37
37
  );
38
38
 
39
- /** @typedef {import("acorn").Options} Options */
40
- /** @typedef {import("acorn").Position} Position */
41
- /** @typedef {import("acorn").Node} Node */
42
- /** @typedef {import("acorn").Identifier} Identifier */
43
- /** @typedef {import("acorn").ImportAttribute} ImportAttribute */
44
- /** @typedef {import("acorn").ImportDefaultSpecifier} ImportDefaultSpecifier */
45
- /** @typedef {import("acorn").ImportExpression} ImportExpression */
46
- /** @typedef {import("acorn").Expression} Expression */
39
+ // acorn exports these Unicode/character helpers at runtime but leaves them out
40
+ // of its public types; the ported cold-path readers below reuse them so their
41
+ // classification stays byte-identical to acorn's tokenizer.
42
+ const {
43
+ isIdentifierChar,
44
+ isIdentifierStart,
45
+ isNewLine,
46
+ lineBreak,
47
+ nonASCIIwhitespace
48
+ } =
49
+ /** @type {{ isIdentifierStart: (code: number, astral?: boolean) => boolean, isIdentifierChar: (code: number, astral?: boolean) => boolean, isNewLine: (code: number) => boolean, lineBreak: RegExp, nonASCIIwhitespace: RegExp }} */
50
+ (/** @type {unknown} */ (require("acorn")));
51
+
52
+ /**
53
+ * @param {number} code code point
54
+ * @returns {string} the string for a single code point
55
+ */
56
+ const codePointToString = (code) => String.fromCodePoint(code);
57
+
58
+ /**
59
+ * acorn's `stringToNumber`: legacy octal parses in base 8, everything else via
60
+ * `parseFloat` after dropping `_` separators.
61
+ * @param {string} str numeric literal text (may contain `_` separators)
62
+ * @param {boolean} isLegacyOctal whether it is a legacy octal literal
63
+ * @returns {number} numeric value
64
+ */
65
+ const stringToNumber = (str, isLegacyOctal) =>
66
+ isLegacyOctal
67
+ ? Number.parseInt(str, 8)
68
+ : Number.parseFloat(str.replace(/_/g, ""));
69
+
70
+ /**
71
+ * acorn's `stringToBigInt`, minus the pre-BigInt fallback (every supported
72
+ * Node has `BigInt`).
73
+ * @param {string} str numeric literal text (may contain `_` separators)
74
+ * @returns {bigint} bigint value
75
+ */
76
+ const stringToBigInt = (str) => BigInt(str.replace(/_/g, ""));
77
+
78
+ /**
79
+ * @import {
80
+ * Options,
81
+ * Position,
82
+ * Node,
83
+ * Identifier,
84
+ * ImportAttribute,
85
+ * ImportDefaultSpecifier,
86
+ * ImportExpression,
87
+ * Expression,
88
+ * TokenType
89
+ * } from "acorn"
90
+ */
47
91
  /** @typedef {import("acorn").ImportSpecifier | import("acorn").ImportDefaultSpecifier | import("acorn").ImportNamespaceSpecifier} AnyImportSpecifier */
48
- /** @typedef {import("acorn").TokenType} TokenType */
49
- /** @typedef {TokenType & { beforeExpr: boolean, isAssign?: boolean, prefix?: boolean, postfix?: boolean, binop: number | null, updateContext?: (prevType: TokenType) => void }} TokenTypeInternal acorn's internal TokenType fields, absent from its public types */
50
- /** @typedef {import("estree").SourceLocation} SourceLocation */
92
+ /** @typedef {TokenType & { beforeExpr: boolean, isAssign?: boolean, prefix?: boolean, postfix?: boolean, binop: number | null, updateContext: ((prevType: TokenType) => void) | null }} TokenTypeInternal acorn's internal TokenType fields, absent from its public types */
51
93
  /** @typedef {[number, number]} Range */
52
94
  /** @typedef {"defer" | "source"} ImportPhase */
53
95
  /** @typedef {import("estree").Comment & { start: number, end: number }} CollectedComment comment as JavascriptParser exposes it */
54
96
 
55
97
  // Symbol-keyed so they stay out of for-in, Object.keys and JSON.stringify
56
98
  // over AST nodes.
99
+ // Per-TokContext "the owned token loop must step aside" flag, cached on the
100
+ // context itself so the hot guard reads one slot instead of two.
101
+ const kSlowContext = Symbol("slow context");
57
102
  const kSource = Symbol("source");
58
103
  const kRange = Symbol("range");
59
104
  const kText = Symbol("text");
@@ -63,12 +108,23 @@ const kTextStart = Symbol("text start");
63
108
  const LEGACY_ASSERT_ATTRIBUTES = Symbol("assert");
64
109
 
65
110
  // acorn's binding types and scope flags, stable across acorn 8
111
+ const BIND_NONE = 0;
66
112
  const BIND_VAR = 1;
67
113
  const BIND_LEXICAL = 2;
114
+ const BIND_OUTSIDE = 5;
68
115
  const SCOPE_TOP = 1;
116
+ const SCOPE_FUNCTION = 2;
117
+ const SCOPE_ASYNC = 4;
118
+ const SCOPE_GENERATOR = 8;
119
+ const SCOPE_ARROW = 16;
69
120
  const SCOPE_SIMPLE_CATCH = 32;
70
121
  // SCOPE_TOP | SCOPE_FUNCTION | SCOPE_CLASS_STATIC_BLOCK
122
+ const SCOPE_SWITCH = 1024;
71
123
  const SCOPE_VAR = 0b100000011;
124
+ // acorn's parseFunction statement bit flags
125
+ const FUNC_STATEMENT = 1;
126
+ const FUNC_HANGING_STATEMENT = 2;
127
+ const FUNC_NULLABLE_ID = 4;
72
128
 
73
129
  // ASCII identifier-continuation chars ($ 0-9 A-Z _ a-z); css/html-style
74
130
  // Uint8Array table so the tokenizer fast path is one load per char
@@ -103,6 +159,18 @@ SIMPLE_PUNCT[123] = tokTypes.braceL;
103
159
  SIMPLE_PUNCT[125] = tokTypes.braceR;
104
160
  SIMPLE_PUNCT[58] = tokTypes.colon;
105
161
 
162
+ // Characters that can only start a token which cannot continue an expression,
163
+ // so an atom directly followed by one is the whole expression (see
164
+ // `parseMaybeAssign`'s fast path). `}` included: it only ever closes a block,
165
+ // an object or a template substitution.
166
+ const EXPRESSION_END_CHAR = new Uint8Array(128);
167
+ EXPRESSION_END_CHAR[41] = 1; // )
168
+ EXPRESSION_END_CHAR[44] = 1; // ,
169
+ EXPRESSION_END_CHAR[58] = 1; // :
170
+ EXPRESSION_END_CHAR[59] = 1; // ;
171
+ EXPRESSION_END_CHAR[93] = 1; // ]
172
+ EXPRESSION_END_CHAR[125] = 1; // }
173
+
106
174
  // Char classification for the owned `nextToken` (yuku's ws_class): one table
107
175
  // load steers both the whitespace skip loop and the token dispatch. Token
108
176
  // classes sort below CLS_SPACE so the skip loop exits on a single compare.
@@ -592,6 +660,235 @@ class PropertyNode {
592
660
  }
593
661
  }
594
662
 
663
+ /**
664
+ * Single-shape `ForStatement`.
665
+ */
666
+ class ForStatementNode {
667
+ /**
668
+ * @param {number} start start offset
669
+ * @param {number} end end offset
670
+ * @param {Node | null} init init statement or expression
671
+ * @param {Expression | null} test loop condition
672
+ * @param {Expression | null} update update expression
673
+ * @param {Node} body loop body
674
+ */
675
+ constructor(start, end, init, test, update, body) {
676
+ this.type = "ForStatement";
677
+ this.start = start;
678
+ this.end = end;
679
+ this.init = init;
680
+ this.test = test;
681
+ this.update = update;
682
+ this.body = body;
683
+ }
684
+ }
685
+
686
+ /**
687
+ * Single-shape `ForInStatement` (no `await` slot, matching acorn).
688
+ */
689
+ class ForInStatementNode {
690
+ /**
691
+ * @param {number} start start offset
692
+ * @param {number} end end offset
693
+ * @param {Node} left loop target
694
+ * @param {Expression} right iterated expression
695
+ * @param {Node} body loop body
696
+ */
697
+ constructor(start, end, left, right, body) {
698
+ this.type = "ForInStatement";
699
+ this.start = start;
700
+ this.end = end;
701
+ this.left = left;
702
+ this.right = right;
703
+ this.body = body;
704
+ }
705
+ }
706
+
707
+ /**
708
+ * Single-shape `ForOfStatement`; `await` leads, matching acorn's write order.
709
+ */
710
+ class ForOfStatementNode {
711
+ /**
712
+ * @param {number} start start offset
713
+ * @param {number} end end offset
714
+ * @param {boolean} isAwait whether this is `for await`
715
+ * @param {Node} left loop target
716
+ * @param {Expression} right iterated expression
717
+ * @param {Node} body loop body
718
+ */
719
+ constructor(start, end, isAwait, left, right, body) {
720
+ this.type = "ForOfStatement";
721
+ this.start = start;
722
+ this.end = end;
723
+ this.await = isAwait;
724
+ this.left = left;
725
+ this.right = right;
726
+ this.body = body;
727
+ }
728
+ }
729
+
730
+ /**
731
+ * Single-shape `WhileStatement`.
732
+ */
733
+ class WhileStatementNode {
734
+ /**
735
+ * @param {number} start start offset
736
+ * @param {number} end end offset
737
+ * @param {Expression} test loop condition
738
+ * @param {Node} body loop body
739
+ */
740
+ constructor(start, end, test, body) {
741
+ this.type = "WhileStatement";
742
+ this.start = start;
743
+ this.end = end;
744
+ this.test = test;
745
+ this.body = body;
746
+ }
747
+ }
748
+
749
+ /**
750
+ * Single-shape `SwitchStatement`.
751
+ */
752
+ class SwitchStatementNode {
753
+ /**
754
+ * @param {number} start start offset
755
+ * @param {number} end end offset
756
+ * @param {Expression} discriminant switched expression
757
+ * @param {Node[]} cases case clauses
758
+ */
759
+ constructor(start, end, discriminant, cases) {
760
+ this.type = "SwitchStatement";
761
+ this.start = start;
762
+ this.end = end;
763
+ this.discriminant = discriminant;
764
+ this.cases = cases;
765
+ }
766
+ }
767
+
768
+ /**
769
+ * Pre-shaped `SwitchCase`: filled in acorn's write order (`consequent` before
770
+ * `test`), finished via `finishNode` like acorn's.
771
+ */
772
+ class SwitchCaseNode {
773
+ /**
774
+ * @param {number} start start offset
775
+ */
776
+ constructor(start) {
777
+ this.type = "";
778
+ this.start = start;
779
+ this.end = 0;
780
+ /** @type {Node[] | null} */
781
+ this.consequent = null;
782
+ /** @type {Expression | null} */
783
+ this.test = null;
784
+ }
785
+ }
786
+
787
+ /**
788
+ * Single-shape `ThrowStatement`.
789
+ */
790
+ class ThrowStatementNode {
791
+ /**
792
+ * @param {number} start start offset
793
+ * @param {number} end end offset
794
+ * @param {Expression} argument thrown expression
795
+ */
796
+ constructor(start, end, argument) {
797
+ this.type = "ThrowStatement";
798
+ this.start = start;
799
+ this.end = end;
800
+ this.argument = argument;
801
+ }
802
+ }
803
+
804
+ /**
805
+ * Single-shape `BreakStatement`/`ContinueStatement` — identical field sets, so
806
+ * both node types share one hidden class.
807
+ */
808
+ class BreakContinueNode {
809
+ /**
810
+ * @param {number} start start offset
811
+ * @param {number} end end offset
812
+ * @param {"BreakStatement" | "ContinueStatement"} type node type
813
+ * @param {Identifier | null} label target label
814
+ */
815
+ constructor(start, end, type, label) {
816
+ this.type = type;
817
+ this.start = start;
818
+ this.end = end;
819
+ this.label = label;
820
+ }
821
+ }
822
+
823
+ /**
824
+ * Single-shape `TryStatement`.
825
+ */
826
+ class TryStatementNode {
827
+ /**
828
+ * @param {number} start start offset
829
+ * @param {number} end end offset
830
+ * @param {Node} block try block
831
+ * @param {Node | null} handler catch clause
832
+ * @param {Node | null} finalizer finally block
833
+ */
834
+ constructor(start, end, block, handler, finalizer) {
835
+ this.type = "TryStatement";
836
+ this.start = start;
837
+ this.end = end;
838
+ this.block = block;
839
+ this.handler = handler;
840
+ this.finalizer = finalizer;
841
+ }
842
+ }
843
+
844
+ /**
845
+ * Single-shape `CatchClause`.
846
+ */
847
+ class CatchClauseNode {
848
+ /**
849
+ * @param {number} start start offset
850
+ * @param {number} end end offset
851
+ * @param {Node | null} param catch parameter
852
+ * @param {Node} body catch block
853
+ */
854
+ constructor(start, end, param, body) {
855
+ this.type = "CatchClause";
856
+ this.start = start;
857
+ this.end = end;
858
+ this.param = param;
859
+ this.body = body;
860
+ }
861
+ }
862
+
863
+ /**
864
+ * Pre-shaped `FunctionDeclaration`/`FunctionExpression`/
865
+ * `ArrowFunctionExpression`: acorn fills function nodes through shared
866
+ * subroutines (`parseFunctionParams`/`parseFunctionBody`), so like
867
+ * `PropertyNode` the fields are all declared up-front — in acorn's exact write
868
+ * order (`initFunction` assigns `expression` before `generator`) so JSON key
869
+ * order is unchanged — and acorn's writes land in existing slots: one hidden
870
+ * class for all three function node types, no transitions.
871
+ */
872
+ class FunctionNode {
873
+ /**
874
+ * @param {number} start start offset
875
+ */
876
+ constructor(start) {
877
+ this.type = "";
878
+ this.start = start;
879
+ this.end = 0;
880
+ /** @type {Identifier | null} */
881
+ this.id = null;
882
+ this.expression = false;
883
+ this.generator = false;
884
+ this.async = false;
885
+ /** @type {Node[] | null} */
886
+ this.params = null;
887
+ /** @type {Node | null} */
888
+ this.body = null;
889
+ }
890
+ }
891
+
595
892
  /**
596
893
  * Single-shape `SpreadElement`/`RestElement` — identical field sets, so both
597
894
  * node types share one hidden class.
@@ -611,6 +908,10 @@ class RestSpreadNode {
611
908
  }
612
909
  }
613
910
 
911
+ // Mirrors of acorn's module-level `loopLabel`/`switchLabel`.
912
+ const LOOP_LABEL = { kind: "loop" };
913
+ const SWITCH_LABEL = { kind: "switch" };
914
+
614
915
  // Shared zero-length arguments array for `new X` without parens, mirroring
615
916
  // acorn's module-level `empty`.
616
917
  /** @type {Expression[]} */
@@ -676,7 +977,18 @@ for (const NodeClass of [
676
977
  TemplateElementNode,
677
978
  ObjectNode,
678
979
  PropertyNode,
679
- RestSpreadNode
980
+ RestSpreadNode,
981
+ FunctionNode,
982
+ ForStatementNode,
983
+ ForInStatementNode,
984
+ ForOfStatementNode,
985
+ WhileStatementNode,
986
+ SwitchStatementNode,
987
+ SwitchCaseNode,
988
+ ThrowStatementNode,
989
+ BreakContinueNode,
990
+ TryStatementNode,
991
+ CatchClauseNode
680
992
  ]) {
681
993
  for (const key of ["range"]) {
682
994
  Object.defineProperty(
@@ -690,8 +1002,9 @@ for (const NodeClass of [
690
1002
 
691
1003
  /**
692
1004
  * Comment collected without slicing its text out of the source: only magic
693
- * comments and pure annotations ever get their text read, so the slice is
694
- * deferred to the first `value` access and memoized like `loc`.
1005
+ * comments and pure annotations ever get their text read, and only binary
1006
+ * searches around magic-comment sites read `range`, so the text slice and the
1007
+ * range array are both deferred to first access and memoized like `loc`.
695
1008
  */
696
1009
  class LazyComment {
697
1010
  /**
@@ -706,12 +1019,30 @@ class LazyComment {
706
1019
  this.type = block ? "Block" : "Line";
707
1020
  this.start = start;
708
1021
  this.end = end;
709
- /** @type {Range} */
710
- this.range = [start, end];
711
1022
  this[kSource] = source;
712
1023
  this[kTextStart] = textStart;
713
1024
  }
714
1025
 
1026
+ /**
1027
+ * Memoized like `LazyLocNode#range`; offsets are final at construction so
1028
+ * the slot is cached unconditionally.
1029
+ * @returns {Range} source range
1030
+ */
1031
+ get range() {
1032
+ const cached = this[kRange];
1033
+ if (cached !== undefined) return cached;
1034
+ /** @type {Range} */
1035
+ const range = [this.start, this.end];
1036
+ return (this[kRange] = range);
1037
+ }
1038
+
1039
+ /**
1040
+ * @param {Range} value source range
1041
+ */
1042
+ set range(value) {
1043
+ this[kRange] = value;
1044
+ }
1045
+
715
1046
  /**
716
1047
  * @returns {string} comment text without the delimiters
717
1048
  */
@@ -786,9 +1117,10 @@ class Scope {
786
1117
  * parseLiteral: (value: unknown) => Node,
787
1118
  * awaitIdentPos: number,
788
1119
  * lastTokStart: number,
1120
+ * lastTokStartLoc?: Position,
789
1121
  * yieldPos: number,
790
1122
  * awaitPos: number,
791
- * parseExpression: () => Expression,
1123
+ * parseExpression: (forInit?: boolean | string, refDestructuringErrors?: DestructuringErrorsShim | null) => Expression,
792
1124
  * parseSpread: (refDestructuringErrors?: DestructuringErrorsShim | null) => Node,
793
1125
  * braceIsBlock: (prevType: TokenType) => boolean,
794
1126
  * _gapHasNewline: () => boolean,
@@ -800,10 +1132,11 @@ class Scope {
800
1132
  * checkPatternErrors: (refDestructuringErrors: DestructuringErrorsShim, isAssign: boolean) => void,
801
1133
  * checkYieldAwaitInDefaultParams: () => void,
802
1134
  * checkExpressionErrors: (refDestructuringErrors?: DestructuringErrorsShim | null, andThrow?: boolean) => boolean,
803
- * parseSubscript: (base: Expression, startPos: number, startLoc: Position | undefined, noCalls: boolean | undefined, maybeAsyncArrow: boolean, optionalChained: boolean, forInit: boolean | string) => Expression,
1135
+ * parseSubscript: (base: Expression, startPos: number, startLoc: Position | undefined, noCalls: boolean | undefined, maybeAsyncArrow: boolean, optionalChained: boolean, forInit?: boolean | string) => Expression,
804
1136
  * parseExprAtom: (refDestructuringErrors?: DestructuringErrorsShim | null, forInit?: boolean | string, forNew?: boolean) => Expression,
805
1137
  * buildBinary: (startPos: number, startLoc: Position | undefined, left: Expression, right: Expression, op: string, logical: boolean) => Expression,
806
1138
  * parseMaybeAssign: (forInit?: boolean | string, refDestructuringErrors?: DestructuringErrorsShim | null, afterLeftParse?: (this: unknown, left: Expression, startPos: number, startLoc?: Position) => Expression) => Expression,
1139
+ * _parseTrivialAtom: (forInit?: boolean | string) => Expression | null,
807
1140
  * parseMaybeConditional: (forInit?: boolean | string, refDestructuringErrors?: DestructuringErrorsShim | null) => Expression,
808
1141
  * parseMaybeUnary: (refDestructuringErrors: DestructuringErrorsShim | null, sawUnary: boolean, incDec: boolean, forInit?: boolean | string) => Expression,
809
1142
  * parseExprSubscripts: (refDestructuringErrors?: DestructuringErrorsShim | null, forInit?: boolean | string) => Expression,
@@ -835,6 +1168,18 @@ class Scope {
835
1168
  * _propHashDepth: number,
836
1169
  * _acquireDestructuringErrors: () => DestructuringErrorsShim,
837
1170
  * _releaseDestructuringErrors: () => void,
1171
+ * _arrStack: EXPECTED_ANY[][],
1172
+ * _arrDepth: number,
1173
+ * _acquireScratch: () => EXPECTED_ANY[],
1174
+ * _releaseScratch: (scratch: EXPECTED_ANY[], count: number) => EXPECTED_ANY[],
1175
+ * parseRestBinding: () => Node,
1176
+ * parseBindingListItem: (param: Node) => Node,
1177
+ * parseAssignableListItem: (allowModifiers?: boolean) => Node,
1178
+ * parseParenItem: (item: Expression | Node) => Expression,
1179
+ * shouldParseArrow: (exprList: Expression[]) => boolean,
1180
+ * parseParenArrowList: (startPos: number, startLoc: Position | undefined, exprList: Expression[], forInit?: boolean | string) => Expression,
1181
+ * finishNodeAt: (node: Node, type: string, pos: number, loc?: Position) => Node,
1182
+ * parseParenAndDistinguishExpression: (canBeArrow: boolean, forInit?: boolean | string) => Expression,
838
1183
  * parseSubscripts: (base: Expression, startPos: number, startLoc: Position | undefined, noCalls?: boolean, forInit?: boolean | string) => Expression,
839
1184
  * parseNew: () => Expression,
840
1185
  * parseTemplateElement: (opts: { isTagged: boolean }) => Node,
@@ -851,7 +1196,9 @@ class Scope {
851
1196
  * parseFunction: (node: Node, statement: number, allowExpressionBody?: boolean, isAsync?: boolean, forInit?: boolean | string) => Expression,
852
1197
  * parseArrowExpression: (node: Node, params: Node[], isAsync: boolean, forInit?: boolean | string) => Expression,
853
1198
  * _subscriptFastPath: boolean,
854
- * checkLValSimple: (expr: Node, bindingType?: number) => void,
1199
+ * checkLValSimple: (expr: Node, bindingType?: number, checkClashes?: Record<string, boolean> | null) => void,
1200
+ * checkLValInnerPattern: (expr: Node, bindingType?: number, checkClashes?: Record<string, boolean> | null) => void,
1201
+ * declareName: (name: string, bindingType: number, pos: number) => void,
855
1202
  * startNode: () => Node,
856
1203
  * startNodeAt: (pos: number, loc?: Position) => Node,
857
1204
  * finishNode: (node: Node, type: string) => Node,
@@ -867,6 +1214,19 @@ class Scope {
867
1214
  * readNumber: (startsWithDot: boolean) => void,
868
1215
  * readRadixNumber: (radix: number) => void,
869
1216
  * readTmplToken: () => void,
1217
+ * invalidStringToken: (position: number, message: string) => void,
1218
+ * _readInt: (radix: number, len?: number, maybeLegacyOctal?: boolean) => number | null,
1219
+ * _readCodePoint: () => number,
1220
+ * _readHexChar: (len: number) => number,
1221
+ * _readEscapedChar: (inTemplate: boolean) => string,
1222
+ * _readStringCold: (quote: number) => void,
1223
+ * _readTmplTokenCold: () => void,
1224
+ * _readNumberCold: (startsWithDot: boolean) => void,
1225
+ * _readRadixNumber: (radix: number) => void,
1226
+ * _readWord1Cold: () => string,
1227
+ * _finishWordSlow: (word: string) => void,
1228
+ * _skipSpaceCold: () => void,
1229
+ * _getUnknownOrPrivate: (code: number) => void,
870
1230
  * finishToken: (type: TokenType, value?: unknown) => void,
871
1231
  * context: TokContextShim[],
872
1232
  * pos: number,
@@ -902,6 +1262,20 @@ class Scope {
902
1262
  * parseExprImport: (forNew: boolean) => Expression,
903
1263
  * parseImportMeta: (node: Node) => Expression,
904
1264
  * parseDynamicImport: (node: Node) => Expression,
1265
+ * updateContext: (prevType: TokenType) => void,
1266
+ * finishOp: (type: TokenType, size: number) => void,
1267
+ * readToken_dot: () => void,
1268
+ * readToken_slash: () => void,
1269
+ * readToken_mult_modulo_exp: (code: number) => void,
1270
+ * readToken_pipe_amp: (code: number) => void,
1271
+ * readToken_caret: () => void,
1272
+ * readToken_plus_min: (code: number) => void,
1273
+ * readToken_lt_gt: (code: number) => void,
1274
+ * readToken_eq_excl: (code: number) => void,
1275
+ * readToken_question: () => void,
1276
+ * readToken_numberSign: () => void,
1277
+ * _tokenFastPath: boolean,
1278
+ * _inlineFinish: boolean,
905
1279
  * _lazy: boolean,
906
1280
  * _importPhase: ImportPhase | null,
907
1281
  * _importPhasesEnabled: boolean,
@@ -909,13 +1283,53 @@ class Scope {
909
1283
  * _newlineBefore: 0 | 1 | 2,
910
1284
  * _fullTokenFastPath: boolean,
911
1285
  * _stmtFastPath: boolean,
1286
+ * _lastArrow: Expression | null,
1287
+ * _arrowFastPath: boolean,
1288
+ * labels: { kind?: string | null, name?: string, statementStart?: number }[],
1289
+ * initFunction: (node: Node) => void,
1290
+ * parseFunctionStatement: (node: Node, isAsync: boolean, declarationPosition: boolean) => Node,
1291
+ * parseFunctionBody: (node: Node, isArrowFunction: boolean, isMethod: boolean, forInit?: boolean | string) => void,
1292
+ * parseFunctionParams: (node: Node) => void,
1293
+ * isSimpleParamList: (params: Node[]) => boolean,
1294
+ * checkParams: (node: Node, allowDuplicates: boolean) => void,
1295
+ * adaptDirectivePrologue: (statements: Node[]) => void,
1296
+ * toAssignableList: (exprList: Node[], isBinding: boolean) => Node[],
1297
+ * parseBindingList: (close: TokenType, allowEmpty: boolean, allowTrailingComma: boolean, allowModifiers?: boolean) => Node[],
1298
+ * strictDirective: (start: number) => boolean,
1299
+ * _funcFastPath: boolean,
1300
+ * _funcStmtOwn: boolean,
1301
+ * _parseFunctionAt: (start: number, statement: number, allowExpressionBody: boolean, isAsync: boolean, forInit?: boolean | string) => Expression,
912
1302
  * isLet: (context?: string | null) => boolean,
913
1303
  * parseLabeledStatement: (node: Node, maybeName: string, expr: Identifier, context: string | null) => Node,
914
- * _parseVarInto: (declarations: Node[], isFor: boolean, kind: string, allowMissingInitializer?: boolean) => void,
1304
+ * _parseVarInto: (declarations: Node[], isFor: boolean, kind: string, allowMissingInitializer?: boolean) => number,
915
1305
  * _parseVarStatementAt: (start: number, kind: string, allowMissingInitializer?: boolean) => Node,
916
1306
  * _parseIfStatementAt: (start: number) => Node,
917
1307
  * _parseReturnStatementAt: (start: number) => Node,
918
1308
  * _parseExpressionStatementAt: (start: number, expr: Expression) => Node,
1309
+ * eatContextual: (name: string) => boolean,
1310
+ * isUsing: (isFor: boolean) => boolean,
1311
+ * isAwaitUsing: (isFor: boolean) => boolean,
1312
+ * parseForStatement: (node: Node) => Node,
1313
+ * parseFor: (node: Node, init: Node | null) => Node,
1314
+ * parseForIn: (node: Node, init: Node) => Node,
1315
+ * parseForAfterInit: (node: Node, init: Node, awaitAt: number) => Node,
1316
+ * parseWhileStatement: (node: Node) => Node,
1317
+ * parseSwitchStatement: (node: Node) => Node,
1318
+ * parseThrowStatement: (node: Node) => Node,
1319
+ * parseTryStatement: (node: Node) => Node,
1320
+ * parseBreakContinueStatement: (node: Node, keyword: string) => Node,
1321
+ * parseCatchClauseParam: () => Node,
1322
+ * _stmt2FastPath: boolean,
1323
+ * _exprFastPath: boolean,
1324
+ * _parseForStatementAt: (start: number) => Node,
1325
+ * _parseForAt: (start: number, init: Node | null) => Node,
1326
+ * _parseForInAt: (start: number, isAwait: boolean, init: Node) => Node,
1327
+ * _parseForAfterInitAt: (start: number, init: Node, awaitAt: number) => Node,
1328
+ * _parseWhileStatementAt: (start: number) => Node,
1329
+ * _parseSwitchStatementAt: (start: number) => Node,
1330
+ * _parseThrowStatementAt: (start: number) => Node,
1331
+ * _parseTryStatementAt: (start: number) => Node,
1332
+ * _parseBreakContinueStatementAt: (start: number, keyword: string) => Node,
919
1333
  * _moduleFallback: boolean,
920
1334
  * _moduleSyntaxSeen: boolean,
921
1335
  * _tryModuleFallback: () => boolean,
@@ -993,6 +1407,10 @@ const createDestructuringErrors = () => {
993
1407
  * @property {Map<string, ReservedKind>} reservedKinds identifier name → reserved kind
994
1408
  * @property {number} reservedMaxLen longest key in `reservedKinds`
995
1409
  * @property {{ test: (name: string) => boolean }} reservedBindTest strict-mode binding check, a Set-backed stand-in for acorn's `reservedWordsStrictBind` regexp
1410
+ * @property {Set<string>} reservedBindSet strict-mode binding-reserved names
1411
+ * @property {number} reservedBindMinLen shortest key in `reservedBindSet`
1412
+ * @property {number} reservedBindMaxLen longest key in `reservedBindSet`
1413
+ * @property {number} id owner tag for the shared `WORD_TYPES` classification memo
996
1414
  */
997
1415
 
998
1416
  // One entry per distinct keyword/reserved-word set; webpack parses with a
@@ -1001,20 +1419,6 @@ const createDestructuringErrors = () => {
1001
1419
  /** @type {Map<string, WordLookups>} */
1002
1420
  const wordLookupsCache = new Map();
1003
1421
 
1004
- /**
1005
- * @param {string} word interned candidate
1006
- * @param {string} input source code
1007
- * @param {number} start word start offset
1008
- * @param {number} len word length (already known equal to `word.length`)
1009
- * @returns {boolean} whether the source span spells `word`
1010
- */
1011
- const sameWord = (word, input, start, len) => {
1012
- for (let i = 0; i < len; i++) {
1013
- if (word.charCodeAt(i) !== input.charCodeAt(start + i)) return false;
1014
- }
1015
- return true;
1016
- };
1017
-
1018
1422
  // Direct-mapped identifier cache for `readWord1`: one slot per hash, verified
1019
1423
  // by char compare, overwritten on collision. Shared across parses — hits are
1020
1424
  // content-checked, so a stale entry is merely a miss. Only words short enough
@@ -1024,12 +1428,60 @@ const WORD_CACHE_MASK = 0x1fff;
1024
1428
  const WORD_CACHE = Array.from({ length: WORD_CACHE_MASK + 1 }, () => null);
1025
1429
  const WORD_CACHE_MAX_LEN = 12;
1026
1430
 
1431
+ // Classification memo parallel to `WORD_CACHE`, same slot: every cache write
1432
+ // also writes the word's token type plus the id of the keyword set that
1433
+ // classified it, so a hit skips `classifyWord` and another option set (or a
1434
+ // `readWord1` write, owner 0) invalidates instead of mis-classifying. Keying
1435
+ // by the cache's own slot adds no extra string references — the memo can
1436
+ // never retain a word the cache itself dropped.
1437
+ /** @type {(TokenType | null)[]} */
1438
+ const WORD_TYPES = Array.from({ length: WORD_CACHE_MASK + 1 }, () => null);
1439
+ const WORD_TYPE_OWNERS = new Int32Array(WORD_CACHE_MASK + 1);
1440
+ let nextWordLookupsId = 1;
1441
+
1442
+ /**
1443
+ * Keyword-or-name classification: pure in (word, keywords), so it can be
1444
+ * memoized per `WORD_CACHE` slot.
1445
+ * @param {string} word word
1446
+ * @param {Map<string, TokenType>} keywords keyword name → token type
1447
+ * @returns {TokenType} token type for `word`
1448
+ */
1449
+ const classifyWord = (word, keywords) => {
1450
+ const len = word.length;
1451
+ // every acorn keyword is 2-10 lowercase ASCII chars (`do`…`instanceof`)
1452
+ if (len >= 2 && len <= 10) {
1453
+ const first = word.charCodeAt(0);
1454
+ if (first >= 97 && first <= 122) return keywords.get(word) || tokTypes.name;
1455
+ }
1456
+ return tokTypes.name;
1457
+ };
1458
+
1027
1459
  // Multi-char operator strings for `finishOp` (`=>`, `===`, `&&=`, …), keyed by
1028
1460
  // their char codes packed 7 bits apart — collision-free for ASCII operators up
1029
1461
  // to acorn's maximum of 4 chars (`>>>=`), so the set stays ~40 entries.
1030
1462
  /** @type {Map<number, string>} */
1031
1463
  const OP_CACHE = new Map();
1032
1464
 
1465
+ // The regexp flag whitelist depends only on the ecmaVersion, so build one
1466
+ // string per version instead of a fresh one in every parser constructor.
1467
+ /** @type {Map<number, string>} */
1468
+ const VALID_REGEXP_FLAGS = new Map();
1469
+
1470
+ /**
1471
+ * @param {number} ecmaVersion normalized acorn ecmaVersion
1472
+ * @returns {string} the regexp flags allowed at `ecmaVersion`
1473
+ */
1474
+ const getValidRegexpFlags = (ecmaVersion) => {
1475
+ let flags = VALID_REGEXP_FLAGS.get(ecmaVersion);
1476
+ if (flags === undefined) {
1477
+ flags = `gim${ecmaVersion >= 6 ? "uy" : ""}${ecmaVersion >= 9 ? "s" : ""}${
1478
+ ecmaVersion >= 13 ? "d" : ""
1479
+ }${ecmaVersion >= 15 ? "v" : ""}`;
1480
+ VALID_REGEXP_FLAGS.set(ecmaVersion, flags);
1481
+ }
1482
+ return flags;
1483
+ };
1484
+
1033
1485
  // Sticky mirrors of acorn's `skipWhiteSpace` / string-literal / `lineBreak`
1034
1486
  // regexes for the owned `strictDirective` (they scan at an offset, no slice).
1035
1487
  const STRICT_SKIP_WS = /(?:\s|\/\/.*|\/\*[^]*?\*\/)*/g;
@@ -1101,6 +1553,14 @@ const getWordLookups = (parser) => {
1101
1553
  // keyword classification wins, matching acorn's keyword-first check
1102
1554
  for (const name of keywords.keys()) reservedKinds.set(name, 1);
1103
1555
  const reservedBind = wordsRegexpToSet(parser.reservedWordsStrictBind);
1556
+ // length bounds so the owned checkLValSimple can skip the Set probe for
1557
+ // most identifiers
1558
+ let reservedBindMinLen = 0x7fffffff;
1559
+ let reservedBindMaxLen = 0;
1560
+ for (const name of reservedBind) {
1561
+ if (name.length < reservedBindMinLen) reservedBindMinLen = name.length;
1562
+ if (name.length > reservedBindMaxLen) reservedBindMaxLen = name.length;
1563
+ }
1104
1564
  let reservedMaxLen = 0;
1105
1565
  for (const name of reservedKinds.keys()) {
1106
1566
  if (name.length > reservedMaxLen) reservedMaxLen = name.length;
@@ -1110,7 +1570,11 @@ const getWordLookups = (parser) => {
1110
1570
  keywords,
1111
1571
  reservedKinds,
1112
1572
  reservedMaxLen,
1113
- reservedBindTest: { test: (name) => reservedBind.has(name) }
1573
+ reservedBindTest: { test: (name) => reservedBind.has(name) },
1574
+ reservedBindSet: reservedBind,
1575
+ reservedBindMinLen,
1576
+ reservedBindMaxLen,
1577
+ id: nextWordLookupsId++
1114
1578
  };
1115
1579
  wordLookupsCache.set(key, lookups);
1116
1580
  lastWordLookups = lookups;
@@ -1189,9 +1653,50 @@ class WebpackParser extends BaseParser {
1189
1653
  // the owned parseSubscript assumes optional chaining exists (it bakes
1190
1654
  // `optional` into the node shape), so gate it on the normalized version
1191
1655
  this._subscriptFastPath = lazy && this._ecmaVersion >= 11;
1656
+ const proto = WebpackParser.prototype;
1657
+ const self = /** @type {ParserInternals} */ (/** @type {unknown} */ (this));
1658
+ // the owned per-token loop (nextToken/finishToken/next) also serves the
1659
+ // public non-lazy tokenizer(): locations off, no onToken, and none of the
1660
+ // acorn tokenizer methods it inlines or skips overridden by a plugin
1661
+ // (`nextToken`'s punct/dot/eq shortcuts bypass `getTokenFromCode`,
1662
+ // `finishOp`, `readToken_dot` and `readToken_eq_excl` even when the full
1663
+ // token path below is off, so their overrides must gate this loop too)
1664
+ this._tokenFastPath =
1665
+ lazy ||
1666
+ (this._noLocations &&
1667
+ !normalizedOptions.onToken &&
1668
+ this.nextToken === proto.nextToken &&
1669
+ this.finishToken === proto.finishToken &&
1670
+ this.next === proto.next &&
1671
+ this.skipSpace === proto.skipSpace &&
1672
+ this.getTokenFromCode === proto.getTokenFromCode &&
1673
+ this.finishOp === proto.finishOp &&
1674
+ self.readToken === base.readToken &&
1675
+ self.updateContext === base.updateContext &&
1676
+ self.readToken_dot === base.readToken_dot &&
1677
+ self.readToken_eq_excl === base.readToken_eq_excl);
1192
1678
  // the owned getTokenFromCode bakes in every ES2021 operator (?., ??=,
1193
- // &&=, ...), so it needs at least that version
1194
- this._fullTokenFastPath = lazy && this._ecmaVersion >= 12;
1679
+ // &&=, ...), so it needs at least that version; outside lazy mode it also
1680
+ // bypasses acorn's readToken_* family, so any override there turns it off
1681
+ this._fullTokenFastPath =
1682
+ this._ecmaVersion >= 12 &&
1683
+ (lazy ||
1684
+ (this._tokenFastPath &&
1685
+ self.readToken_slash === base.readToken_slash &&
1686
+ self.readToken_mult_modulo_exp === base.readToken_mult_modulo_exp &&
1687
+ self.readToken_pipe_amp === base.readToken_pipe_amp &&
1688
+ self.readToken_caret === base.readToken_caret &&
1689
+ self.readToken_plus_min === base.readToken_plus_min &&
1690
+ self.readToken_lt_gt === base.readToken_lt_gt &&
1691
+ self.readToken_question === base.readToken_question &&
1692
+ self.readToken_numberSign === base.readToken_numberSign &&
1693
+ self.readRadixNumber === base.readRadixNumber));
1694
+ // the owned nextToken/readWord finish the commonest tokens in place,
1695
+ // which skips this.finishToken: needs the fast token loop (readWord runs
1696
+ // in every mode) and no finishToken override by a plugin
1697
+ this._inlineFinish =
1698
+ this._tokenFastPath &&
1699
+ this.finishToken === WebpackParser.prototype.finishToken;
1195
1700
  // whether the gap before the current token holds a line terminator:
1196
1701
  // 0 no, 1 yes, 2 unknown (canInsertSemicolon then scans the gap)
1197
1702
  /** @type {0 | 1 | 2} */
@@ -1210,13 +1715,16 @@ class WebpackParser extends BaseParser {
1210
1715
  /** @type {{ proto: boolean }[]} */
1211
1716
  this._propHashStack = [];
1212
1717
  this._propHashDepth = 0;
1718
+ // LIFO pool of scratch arrays for list productions: elements are written
1719
+ // by index, then copied out exactly sized — a push-grown array retains
1720
+ // ~17 slots of capacity slack per list otherwise
1721
+ /** @type {unknown[][]} */
1722
+ this._arrStack = [];
1723
+ this._arrDepth = 0;
1213
1724
  // `readRegexp`'s flag whitelist depends only on the ecmaVersion
1214
- this._validRegexpFlags = `gim${this._ecmaVersion >= 6 ? "uy" : ""}${
1215
- this._ecmaVersion >= 9 ? "s" : ""
1216
- }${this._ecmaVersion >= 13 ? "d" : ""}${this._ecmaVersion >= 15 ? "v" : ""}`;
1725
+ this._validRegexpFlags = getValidRegexpFlags(this._ecmaVersion);
1217
1726
  // the owned parseStatement inlines these methods, so a parser plugin
1218
1727
  // overriding any of them turns the statement fast path off
1219
- const proto = WebpackParser.prototype;
1220
1728
  this._stmtFastPath =
1221
1729
  lazy &&
1222
1730
  this.parseVarStatement === proto.parseVarStatement &&
@@ -1224,6 +1732,62 @@ class WebpackParser extends BaseParser {
1224
1732
  this.parseIfStatement === proto.parseIfStatement &&
1225
1733
  this.parseReturnStatement === proto.parseReturnStatement &&
1226
1734
  this.parseExpressionStatement === proto.parseExpressionStatement;
1735
+ // last arrow finished by parseArrowExpression: `expr === this._lastArrow`
1736
+ // replaces the megamorphic `expr.type === "ArrowFunctionExpression"`
1737
+ // probes on the expression spine (arrows are created in exactly one place
1738
+ // and never backtracked, so identity captures the type test)
1739
+ /** @type {Expression | null} */
1740
+ this._lastArrow = null;
1741
+ // arrows must flow through parseArrowExpression for the identity probe;
1742
+ // a plugin overriding it falls back to the type-based probes
1743
+ this._arrowFastPath =
1744
+ lazy && this.parseArrowExpression === proto.parseArrowExpression;
1745
+ // the owned parseFunction/parseFunctionBody inline initFunction and
1746
+ // isSimpleParamList; a plugin overriding either turns the fast path off
1747
+ const internals = /** @type {ParserInternals} */ (
1748
+ /** @type {unknown} */ (this)
1749
+ );
1750
+ this._funcFastPath =
1751
+ lazy &&
1752
+ this._ecmaVersion >= 9 &&
1753
+ internals.initFunction === base.initFunction &&
1754
+ internals.isSimpleParamList === base.isSimpleParamList;
1755
+ // the owned parseStatement inlines parseFunctionStatement's body
1756
+ this._funcStmtOwn =
1757
+ this._funcFastPath &&
1758
+ internals.parseFunctionStatement === base.parseFunctionStatement &&
1759
+ this.parseFunction === proto.parseFunction;
1760
+ // the owned parseStatement inlines these statement parsers too; any
1761
+ // override falls back to acorn's dispatch for these heads
1762
+ this._stmt2FastPath =
1763
+ lazy &&
1764
+ this._ecmaVersion >= 9 &&
1765
+ internals.parseForStatement === base.parseForStatement &&
1766
+ internals.parseFor === base.parseFor &&
1767
+ internals.parseForIn === base.parseForIn &&
1768
+ internals.parseForAfterInit === base.parseForAfterInit &&
1769
+ this.parseVar === proto.parseVar &&
1770
+ internals.parseWhileStatement === base.parseWhileStatement &&
1771
+ internals.parseSwitchStatement === base.parseSwitchStatement &&
1772
+ internals.parseThrowStatement === base.parseThrowStatement &&
1773
+ internals.parseTryStatement === base.parseTryStatement &&
1774
+ internals.parseBreakContinueStatement ===
1775
+ base.parseBreakContinueStatement;
1776
+ // `parseMaybeAssign`'s trivial-atom fast path returns the atom without
1777
+ // descending the seven-layer expression chain, so every layer it skips
1778
+ // must be the owned one
1779
+ this._exprFastPath =
1780
+ lazy &&
1781
+ this.parseMaybeConditional === proto.parseMaybeConditional &&
1782
+ this.parseExprOps === proto.parseExprOps &&
1783
+ this.parseExprOp === proto.parseExprOp &&
1784
+ this.parseMaybeUnary === proto.parseMaybeUnary &&
1785
+ this.parseExprSubscripts === proto.parseExprSubscripts &&
1786
+ this.parseSubscripts === proto.parseSubscripts &&
1787
+ this.parseSubscript === proto.parseSubscript &&
1788
+ this.parseExprAtom === proto.parseExprAtom &&
1789
+ this.checkExpressionErrors === proto.checkExpressionErrors &&
1790
+ internals.isContextual === base.isContextual;
1227
1791
  }
1228
1792
 
1229
1793
  /**
@@ -1258,27 +1822,62 @@ class WebpackParser extends BaseParser {
1258
1822
  this._deDepth--;
1259
1823
  }
1260
1824
 
1825
+ /**
1826
+ * Fetches a scratch array for a list production: write elements by index,
1827
+ * then materialize with `_releaseScratch`. Entries past the caller's write
1828
+ * index are stale and meaningless.
1829
+ * @returns {EXPECTED_ANY[]} scratch array
1830
+ * @this {ParserInternals}
1831
+ */
1832
+ _acquireScratch() {
1833
+ const stack = this._arrStack;
1834
+ const depth = this._arrDepth++;
1835
+ const cached = stack[depth];
1836
+ if (cached !== undefined) return cached;
1837
+ return (stack[depth] = []);
1838
+ }
1839
+
1840
+ /**
1841
+ * @param {EXPECTED_ANY[]} scratch scratch array from `_acquireScratch`
1842
+ * @param {number} count number of elements written
1843
+ * @returns {EXPECTED_ANY[]} exactly-sized copy of the first `count` entries
1844
+ * @this {ParserInternals}
1845
+ */
1846
+ _releaseScratch(scratch, count) {
1847
+ this._arrDepth--;
1848
+ return scratch.slice(0, count);
1849
+ }
1850
+
1261
1851
  // ----- tokenizer fast paths -----
1262
1852
 
1263
1853
  /**
1264
1854
  * Owned per-token loop: acorn's `nextToken` chains `skipSpace` →
1265
1855
  * `fullCharCodeAtPos` → `readToken` → `isIdentifierStart` with a dead
1266
- * `locations` check at each step. For the common lazy, non-template context
1856
+ * `locations` check at each step. For the common non-template context of
1857
+ * `_tokenFastPath` mode (lazy, or locations-off with no tokenizer overrides)
1267
1858
  * this folds whitespace and comment skipping and the ASCII token dispatch
1268
1859
  * into one function so nothing re-enters acorn's per-step option checks.
1269
- * Template/`preserveSpace` contexts and non-lazy mode use acorn's tokenizer.
1860
+ * Template/`preserveSpace` contexts and other modes use acorn's tokenizer.
1861
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/tokenize.js
1270
1862
  * @returns {void}
1271
1863
  * @this {ParserInternals}
1272
1864
  */
1273
1865
  nextToken() {
1274
1866
  const context = this.context;
1275
1867
  const curContext = context[context.length - 1];
1276
- if (
1277
- !this._lazy ||
1278
- !curContext ||
1279
- curContext.preserveSpace ||
1280
- curContext.override
1281
- ) {
1868
+ let slow;
1869
+ if (curContext !== undefined) {
1870
+ const flagged =
1871
+ /** @type {TokContextShim & { [kSlowContext]?: boolean }} */
1872
+ (curContext);
1873
+ slow = flagged[kSlowContext];
1874
+ if (slow === undefined) {
1875
+ slow = flagged[kSlowContext] = Boolean(
1876
+ curContext.preserveSpace || curContext.override
1877
+ );
1878
+ }
1879
+ }
1880
+ if (!this._tokenFastPath || curContext === undefined || slow) {
1282
1881
  this._newlineBefore = 2;
1283
1882
  return base.nextToken.call(this);
1284
1883
  }
@@ -1300,9 +1899,9 @@ class WebpackParser extends BaseParser {
1300
1899
  cls = CHAR_CLASS[code];
1301
1900
  if (cls < CLS_SPACE) {
1302
1901
  if (cls === CLS_UNICODE) {
1303
- // unicode whitespace / line terminators: acorn consumes the rest
1902
+ // unicode whitespace / line terminators: the cold reader consumes them
1304
1903
  this.pos = pos;
1305
- base.skipSpace.call(this);
1904
+ this._skipSpaceCold();
1306
1905
  pos = this.pos;
1307
1906
  if (newline === 0) newline = 2;
1308
1907
  code = pos < len ? input.charCodeAt(pos) : 0;
@@ -1342,9 +1941,71 @@ class WebpackParser extends BaseParser {
1342
1941
  switch (cls) {
1343
1942
  case CLS_IDENT:
1344
1943
  return this.readWord();
1345
- case CLS_PUNCT:
1944
+ case CLS_PUNCT: {
1945
+ const type = /** @type {TokenType} */ (SIMPLE_PUNCT[code]);
1346
1946
  this.pos = pos + 1;
1347
- return this.finishToken(/** @type {TokenType} */ (SIMPLE_PUNCT[code]));
1947
+ if (!this._inlineFinish) return this.finishToken(type);
1948
+ // finishToken inlined for the simple punctuators (no value, and
1949
+ // their context updates are per-char static)
1950
+ this.end = pos + 1;
1951
+ const prevType = this.type;
1952
+ this.type = type;
1953
+ this.value = undefined;
1954
+ switch (code) {
1955
+ case 41:
1956
+ case 125: {
1957
+ // parenR/braceR.updateContext, inlined
1958
+ if (context.length === 1) {
1959
+ this.exprAllowed = true;
1960
+ break;
1961
+ }
1962
+ let out = /** @type {TokContextShim} */ (context.pop());
1963
+ if (
1964
+ out === CTX_B_STAT &&
1965
+ /** @type {TokContextShim} */ (context[context.length - 1])
1966
+ .token === "function"
1967
+ ) {
1968
+ out = /** @type {TokContextShim} */ (context.pop());
1969
+ }
1970
+ this.exprAllowed = !out.isExpr;
1971
+ break;
1972
+ }
1973
+ case 123:
1974
+ // braceL.updateContext, inlined
1975
+ context.push(this.braceIsBlock(prevType) ? CTX_B_STAT : CTX_B_EXPR);
1976
+ this.exprAllowed = true;
1977
+ break;
1978
+ case 40: {
1979
+ // parenL.updateContext, inlined
1980
+ const statementParens =
1981
+ prevType === tokTypes._if ||
1982
+ prevType === tokTypes._for ||
1983
+ prevType === tokTypes._with ||
1984
+ prevType === tokTypes._while;
1985
+ context.push(statementParens ? CTX_P_STAT : CTX_P_EXPR);
1986
+ this.exprAllowed = true;
1987
+ break;
1988
+ }
1989
+ case 58:
1990
+ // colon.updateContext, inlined
1991
+ if (
1992
+ /** @type {TokContextShim} */ (context[context.length - 1])
1993
+ .token === "function"
1994
+ ) {
1995
+ context.pop();
1996
+ }
1997
+ this.exprAllowed = true;
1998
+ break;
1999
+ case 93:
2000
+ // bracketR: no context hook, beforeExpr is false
2001
+ this.exprAllowed = false;
2002
+ break;
2003
+ default:
2004
+ // semi, comma, bracketL: no context hook, beforeExpr is true
2005
+ this.exprAllowed = true;
2006
+ }
2007
+ return;
2008
+ }
1348
2009
  case CLS_DOT: {
1349
2010
  // `.` not starting `.5` or `...`: skip readToken_dot's re-dispatch
1350
2011
  const next = input.charCodeAt(pos + 1);
@@ -1371,17 +2032,18 @@ class WebpackParser extends BaseParser {
1371
2032
  }
1372
2033
 
1373
2034
  /**
1374
- * Lazy-mode `finishToken`: acorn probes `options.locations` for a dead
1375
- * `endLoc` write on every token and reaches `updateContext` through an extra
1376
- * method call. Skip the probe and inline acorn's `updateContext` body — this
1377
- * runs once per token. Other modes use acorn's.
2035
+ * `_tokenFastPath` `finishToken`: acorn probes `options.locations` for a
2036
+ * dead `endLoc` write on every token and reaches `updateContext` through an
2037
+ * extra method call. Skip the probe and inline acorn's `updateContext` body —
2038
+ * this runs once per token. Other modes use acorn's.
2039
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/tokenize.js
1378
2040
  * @param {TokenType} type token type
1379
2041
  * @param {unknown=} value token value
1380
2042
  * @returns {void}
1381
2043
  * @this {ParserInternals}
1382
2044
  */
1383
2045
  finishToken(type, value) {
1384
- if (!this._lazy) {
2046
+ if (!this._tokenFastPath) {
1385
2047
  return base.finishToken.call(this, type, value);
1386
2048
  }
1387
2049
  this.end = this.pos;
@@ -1400,41 +2062,53 @@ class WebpackParser extends BaseParser {
1400
2062
  (value === "yield" && this.inGeneratorContext())) &&
1401
2063
  prevType !== tokTypes.dot &&
1402
2064
  this._ecmaVersion >= 6;
1403
- } else if (type.keyword && prevType === tokTypes.dot) {
1404
- this.exprAllowed = false;
1405
- } else if (type === tokTypes.parenR || type === tokTypes.braceR) {
1406
- // parenR/braceR.updateContext, inlined
1407
- const context = this.context;
1408
- if (context.length === 1) {
2065
+ } else {
2066
+ const update = internal.updateContext;
2067
+ if (update === null) {
2068
+ // no context hook (most punctuation, operators, literals and
2069
+ // keywords): acorn's keyword-after-dot probe, else `beforeExpr` —
2070
+ // checked first so this majority skips the per-type compares below
2071
+ this.exprAllowed =
2072
+ prevType === tokTypes.dot && internal.keyword !== undefined
2073
+ ? false
2074
+ : internal.beforeExpr;
2075
+ } else if (type === tokTypes.parenR || type === tokTypes.braceR) {
2076
+ // parenR/braceR.updateContext, inlined
2077
+ const context = this.context;
2078
+ if (context.length === 1) {
2079
+ this.exprAllowed = true;
2080
+ } else {
2081
+ let out = /** @type {TokContextShim} */ (context.pop());
2082
+ if (
2083
+ out === CTX_B_STAT &&
2084
+ /** @type {TokContextShim} */ (context[context.length - 1])
2085
+ .token === "function"
2086
+ ) {
2087
+ out = /** @type {TokContextShim} */ (context.pop());
2088
+ }
2089
+ this.exprAllowed = !out.isExpr;
2090
+ }
2091
+ } else if (type === tokTypes.braceL) {
2092
+ // braceL.updateContext, inlined
2093
+ this.context.push(
2094
+ this.braceIsBlock(prevType) ? CTX_B_STAT : CTX_B_EXPR
2095
+ );
1409
2096
  this.exprAllowed = true;
2097
+ } else if (type === tokTypes.parenL) {
2098
+ // parenL.updateContext, inlined
2099
+ const statementParens =
2100
+ prevType === tokTypes._if ||
2101
+ prevType === tokTypes._for ||
2102
+ prevType === tokTypes._with ||
2103
+ prevType === tokTypes._while;
2104
+ this.context.push(statementParens ? CTX_P_STAT : CTX_P_EXPR);
2105
+ this.exprAllowed = true;
2106
+ } else if (internal.keyword !== undefined && prevType === tokTypes.dot) {
2107
+ // `.function` etc.: keyword-after-dot wins over the type's own hook
2108
+ this.exprAllowed = false;
1410
2109
  } else {
1411
- let out = /** @type {TokContextShim} */ (context.pop());
1412
- if (
1413
- out === CTX_B_STAT &&
1414
- /** @type {TokContextShim} */ (context[context.length - 1]).token ===
1415
- "function"
1416
- ) {
1417
- out = /** @type {TokContextShim} */ (context.pop());
1418
- }
1419
- this.exprAllowed = !out.isExpr;
1420
- }
1421
- } else if (type === tokTypes.braceL) {
1422
- // braceL.updateContext, inlined
1423
- this.context.push(this.braceIsBlock(prevType) ? CTX_B_STAT : CTX_B_EXPR);
1424
- this.exprAllowed = true;
1425
- } else if (type === tokTypes.parenL) {
1426
- // parenL.updateContext, inlined
1427
- const statementParens =
1428
- prevType === tokTypes._if ||
1429
- prevType === tokTypes._for ||
1430
- prevType === tokTypes._with ||
1431
- prevType === tokTypes._while;
1432
- this.context.push(statementParens ? CTX_P_STAT : CTX_P_EXPR);
1433
- this.exprAllowed = true;
1434
- } else if (internal.updateContext) {
1435
- internal.updateContext.call(this, prevType);
1436
- } else {
1437
- this.exprAllowed = internal.beforeExpr;
2110
+ update.call(this, prevType);
2111
+ }
1438
2112
  }
1439
2113
  }
1440
2114
 
@@ -1442,6 +2116,7 @@ class WebpackParser extends BaseParser {
1442
2116
  * Owned `braceIsBlock`, acorn's verbatim except the line-terminator probe:
1443
2117
  * acorn slices the inter-token gap and runs a regexp; `_gapHasNewline`
1444
2118
  * answers from the tokenizer's newline flag (scanning only when unknown).
2119
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/tokencontext.js
1445
2120
  * @param {TokenType} prevType type of the previous token
1446
2121
  * @returns {boolean} whether a `{` opens a block in this context
1447
2122
  * @this {ParserInternals}
@@ -1513,18 +2188,21 @@ class WebpackParser extends BaseParser {
1513
2188
  /**
1514
2189
  * Owned per-token advance: acorn's `next` writes `lastTokEndLoc`/
1515
2190
  * `lastTokStartLoc` and probes `options.onToken` on every token, both dead in
1516
- * lazy mode (locations off, no token stream), leaving only the two offset
1517
- * writes and the keyword-escape guard. Other modes use acorn's.
2191
+ * `_tokenFastPath` mode (locations off, no token stream), leaving only the
2192
+ * two offset writes and the keyword-escape guard. Other modes use acorn's.
2193
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/tokenize.js
1518
2194
  * @param {boolean=} ignoreEscapeSequenceInKeyword whether an escape in a keyword is allowed here
1519
2195
  * @returns {void}
1520
2196
  * @this {ParserInternals}
1521
2197
  */
1522
2198
  next(ignoreEscapeSequenceInKeyword) {
1523
- if (!this._lazy) {
2199
+ if (!this._tokenFastPath) {
1524
2200
  return base.next.call(this, ignoreEscapeSequenceInKeyword);
1525
2201
  }
1526
2202
  const type = this.type;
1527
- if (!ignoreEscapeSequenceInKeyword && type.keyword && this.containsEsc) {
2203
+ // `containsEsc` is a parser field and almost always false; testing it first
2204
+ // keeps the TokenType load off the common path.
2205
+ if (this.containsEsc && !ignoreEscapeSequenceInKeyword && type.keyword) {
1528
2206
  this.raiseRecoverable(
1529
2207
  this.start,
1530
2208
  `Escape sequence in keyword ${type.keyword}`
@@ -1540,6 +2218,7 @@ class WebpackParser extends BaseParser {
1540
2218
  * every operator token, allocating a fresh 2-4 char string per `=>`, `===`,
1541
2219
  * `&&` etc. Serve those from `OP_CACHE` instead; single-char operators keep
1542
2220
  * the direct slice, which V8 serves from its single-character table.
2221
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/tokenize.js
1543
2222
  * @param {TokenType} type token type
1544
2223
  * @param {number} size operator length
1545
2224
  * @returns {void}
@@ -1571,7 +2250,9 @@ class WebpackParser extends BaseParser {
1571
2250
  * `readToken_*` methods that each end in `finishOp`'s source slice. Resolve
1572
2251
  * every operator by direct char peeks to a static string instead (yuku's
1573
2252
  * `scanPunctuation`) — no method chain, no slice, no `OP_CACHE` probe. The
1574
- * HTML-comment forms (`<!--`, `-->`), `#`, and unknown chars delegate.
2253
+ * HTML-comment forms (`<!--`, `-->`) inline acorn's line-comment handling;
2254
+ * `#` and unknown chars use the owned cold reader.
2255
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/tokenize.js
1575
2256
  * @param {number} code current char code
1576
2257
  * @returns {void}
1577
2258
  * @this {ParserInternals}
@@ -1697,8 +2378,17 @@ class WebpackParser extends BaseParser {
1697
2378
  // '-': --, -= or -; `-->` may open an HTML line comment
1698
2379
  const next = input.charCodeAt(pos + 1);
1699
2380
  if (next === 45) {
1700
- if (input.charCodeAt(pos + 2) === 62 && !this.inModule) {
1701
- return base.getTokenFromCode.call(this, code);
2381
+ // `-->` opens an HTML line comment only at the start of a line
2382
+ // (acorn readToken_plus_min): https://github.com/acornjs/acorn/blob/8.15.0/acorn/src/tokenize.js#L599-L618
2383
+ if (
2384
+ input.charCodeAt(pos + 2) === 62 &&
2385
+ !this.inModule &&
2386
+ (this.lastTokEnd === 0 ||
2387
+ lineBreak.test(input.slice(this.lastTokEnd, pos)))
2388
+ ) {
2389
+ this.skipLineComment(3);
2390
+ this.skipSpace();
2391
+ return this.nextToken();
1702
2392
  }
1703
2393
  this.pos = pos + 2;
1704
2394
  return this.finishToken(tokTypes.incDec, "--");
@@ -1727,7 +2417,11 @@ class WebpackParser extends BaseParser {
1727
2417
  input.charCodeAt(pos + 2) === 45 &&
1728
2418
  input.charCodeAt(pos + 3) === 45
1729
2419
  ) {
1730
- return base.getTokenFromCode.call(this, code);
2420
+ // `<!--` opens an HTML line comment (acorn readToken_lt_gt):
2421
+ // https://github.com/acornjs/acorn/blob/8.15.0/acorn/src/tokenize.js#L620-L649
2422
+ this.skipLineComment(4);
2423
+ this.skipSpace();
2424
+ return this.nextToken();
1731
2425
  }
1732
2426
  if (next === 61) {
1733
2427
  this.pos = pos + 2;
@@ -1828,9 +2522,9 @@ class WebpackParser extends BaseParser {
1828
2522
  case 48: {
1829
2523
  // '0': radix literals split off before the decimal reader
1830
2524
  const next = input.charCodeAt(pos + 1);
1831
- if (next === 120 || next === 88) return this.readRadixNumber(16);
1832
- if (next === 111 || next === 79) return this.readRadixNumber(8);
1833
- if (next === 98 || next === 66) return this.readRadixNumber(2);
2525
+ if (next === 120 || next === 88) return this._readRadixNumber(16);
2526
+ if (next === 111 || next === 79) return this._readRadixNumber(8);
2527
+ if (next === 98 || next === 66) return this._readRadixNumber(2);
1834
2528
  return this.readNumber(false);
1835
2529
  }
1836
2530
  case 49:
@@ -1847,18 +2541,19 @@ class WebpackParser extends BaseParser {
1847
2541
  case 39:
1848
2542
  return this.readString(code);
1849
2543
  default:
1850
- return base.getTokenFromCode.call(this, code);
2544
+ return this._getUnknownOrPrivate(code);
1851
2545
  }
1852
2546
  }
1853
2547
 
1854
2548
  /**
1855
2549
  * ASCII fast path for acorn's `readWord1`, which pays a surrogate-aware
1856
2550
  * method call and a range-check helper per character. Escapes, non-ASCII
1857
- * and astral input restart the base implementation from the word start.
2551
+ * and astral input restart the owned cold reader from the word start.
1858
2552
  * Words are deduplicated through `WORD_CACHE` so repeated identifiers —
1859
2553
  * which dominate real code — reuse one string instead of slicing a fresh
1860
2554
  * one per occurrence; sharing also keeps their cached string hashes warm
1861
2555
  * for the keyword/scope Map lookups downstream.
2556
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/tokenize.js
1862
2557
  * @this {ParserInternals}
1863
2558
  * @returns {string} the word
1864
2559
  */
@@ -1876,13 +2571,13 @@ class WebpackParser extends BaseParser {
1876
2571
  if (ch < 128) {
1877
2572
  if (IDENT_CHAR[ch] === 0) {
1878
2573
  // backslash escape: restart cold so escape rules see the word
1879
- if (ch === 92) return base.readWord1.call(this);
2574
+ if (ch === 92) return this._readWord1Cold();
1880
2575
  break;
1881
2576
  }
1882
2577
  hash = (Math.imul(hash, 33) + ch) | 0;
1883
2578
  pos++;
1884
2579
  } else {
1885
- return base.readWord1.call(this);
2580
+ return this._readWord1Cold();
1886
2581
  }
1887
2582
  }
1888
2583
  this.containsEsc = false;
@@ -1896,23 +2591,27 @@ class WebpackParser extends BaseParser {
1896
2591
  if (
1897
2592
  cached !== null &&
1898
2593
  cached.length === wordLen &&
1899
- sameWord(cached, input, start, wordLen)
2594
+ // length already matched, so a prefix test is an exact compare —
2595
+ // one builtin instead of a charCodeAt loop per cached character
2596
+ input.startsWith(cached, start)
1900
2597
  ) {
1901
2598
  return cached;
1902
2599
  }
1903
2600
  const word = input.slice(start, pos);
1904
2601
  WORD_CACHE[slot] = word;
2602
+ WORD_TYPE_OWNERS[slot] = 0;
1905
2603
  return word;
1906
2604
  }
1907
2605
  return input.slice(start, pos);
1908
2606
  }
1909
2607
 
1910
2608
  /**
1911
- * String fast path: one scan that both finds the closing quote and vets the
1912
- * span (the former `indexOf` + verify-loop pair read every string twice).
1913
- * Escapes, newlines in the span, old ecmaVersions (LS/PS terminate strings
1914
- * there) and location tracking restart acorn's implementation, which also
1915
- * produces its exact errors.
2609
+ * String fast path: one scan finds the closing quote and cooks the common
2610
+ * single-char escapes (`\n \t \\ \" \'` …) and line continuations inline.
2611
+ * Hex/unicode/octal escapes (`\x`, `\u`, `\0`-`\9`) restart the owned cold
2612
+ * reader, which owns their readers and strict-mode errors; old ecmaVersions
2613
+ * (LS/PS terminate strings there) and location tracking use acorn's own.
2614
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/tokenize.js
1916
2615
  * @this {ParserInternals}
1917
2616
  * @param {number} quote quote char code
1918
2617
  * @returns {void}
@@ -1923,33 +2622,94 @@ class WebpackParser extends BaseParser {
1923
2622
  }
1924
2623
  const input = this.input;
1925
2624
  const len = input.length;
1926
- const start = this.pos + 1;
2625
+ const quotePos = this.pos;
2626
+ const start = quotePos + 1;
1927
2627
  let pos = start;
2628
+ // spans between escapes accumulate here; a plain string never touches
2629
+ // `out` and slices once at the end
2630
+ let out = "";
2631
+ let chunkStart = start;
1928
2632
  for (;;) {
1929
2633
  if (pos >= len) this.raise(this.start, "Unterminated string constant");
1930
2634
  const ch = input.charCodeAt(pos);
1931
2635
  if (ch === quote) break;
1932
- // backslash, LF, CR
1933
- if (ch === 92 || ch === 10 || ch === 13) {
1934
- return base.readString.call(this, quote);
2636
+ if (ch === 92) {
2637
+ const esc = input.charCodeAt(pos + 1);
2638
+ // hex/unicode/octal escapes (and a trailing `\`) need the cold
2639
+ // reader's escape handling; restart from the quote (this.pos unmoved)
2640
+ if (
2641
+ pos + 1 >= len ||
2642
+ esc === 120 ||
2643
+ esc === 117 ||
2644
+ (esc >= 48 && esc <= 57)
2645
+ ) {
2646
+ this.pos = quotePos;
2647
+ return this._readStringCold(quote);
2648
+ }
2649
+ let rep;
2650
+ let advance = 2;
2651
+ switch (esc) {
2652
+ case 110:
2653
+ rep = "\n";
2654
+ break;
2655
+ case 114:
2656
+ rep = "\r";
2657
+ break;
2658
+ case 116:
2659
+ rep = "\t";
2660
+ break;
2661
+ case 98:
2662
+ rep = "\b";
2663
+ break;
2664
+ case 102:
2665
+ rep = "\f";
2666
+ break;
2667
+ case 118:
2668
+ rep = "\u000B";
2669
+ break;
2670
+ case 13:
2671
+ // `\` + CR(LF): line continuation cooked away
2672
+ rep = "";
2673
+ if (input.charCodeAt(pos + 2) === 10) advance = 3;
2674
+ break;
2675
+ case 10:
2676
+ case 0x2028:
2677
+ case 0x2029:
2678
+ rep = "";
2679
+ break;
2680
+ default:
2681
+ // any other escape is the escaped code unit verbatim (`\\`, `\q`)
2682
+ rep = input[pos + 1];
2683
+ }
2684
+ out += input.slice(chunkStart, pos) + rep;
2685
+ pos += advance;
2686
+ chunkStart = pos;
2687
+ } else if (ch === 10 || ch === 13) {
2688
+ // bare LF/CR is unterminated; LS/PS stay valid at ES2019+
2689
+ this.raise(this.start, "Unterminated string constant");
2690
+ } else {
2691
+ pos++;
1935
2692
  }
1936
- pos++;
1937
2693
  }
1938
2694
  this.pos = pos + 1;
1939
- this.finishToken(tokTypes.string, input.slice(start, pos));
2695
+ if (chunkStart === start) {
2696
+ return this.finishToken(tokTypes.string, input.slice(start, pos));
2697
+ }
2698
+ this.finishToken(tokTypes.string, out + input.slice(chunkStart, pos));
1940
2699
  }
1941
2700
 
1942
2701
  /**
1943
2702
  * Number fast path: plain integer literals (no leading zero, up to 15
1944
2703
  * digits so the float is exact) are accumulated numerically — no slice,
1945
2704
  * no parseFloat, no separator handling. Everything else (dots, exponents,
1946
- * bigints, separators, octal forms) restarts acorn's implementation.
2705
+ * bigints, separators, octal forms) restarts the owned cold reader.
2706
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/tokenize.js
1947
2707
  * @this {ParserInternals}
1948
2708
  * @param {boolean} startsWithDot whether the number started with a dot
1949
2709
  * @returns {void}
1950
2710
  */
1951
2711
  readNumber(startsWithDot) {
1952
- if (startsWithDot) return base.readNumber.call(this, startsWithDot);
2712
+ if (startsWithDot) return this._readNumberCold(startsWithDot);
1953
2713
  const input = this.input;
1954
2714
  const start = this.pos;
1955
2715
  const len = input.length;
@@ -1962,7 +2722,7 @@ class WebpackParser extends BaseParser {
1962
2722
  pos = start + 1;
1963
2723
  } else if (c1 > 127 || IDENT_CHAR[c1] === 1) {
1964
2724
  // 0x/0o/0b, 0e…, 0n, 0_, legacy `0NN`, or `0`+identifier: acorn
1965
- return base.readNumber.call(this, startsWithDot);
2725
+ return this._readNumberCold(startsWithDot);
1966
2726
  } else {
1967
2727
  // bare `0` before punctuation/operator/whitespace/EOF
1968
2728
  this.pos = start + 1;
@@ -1993,18 +2753,18 @@ class WebpackParser extends BaseParser {
1993
2753
  after > 127 ||
1994
2754
  IDENT_CHAR[after] === 1
1995
2755
  ) {
1996
- return base.readNumber.call(this, startsWithDot);
2756
+ return this._readNumberCold(startsWithDot);
1997
2757
  }
1998
2758
  // 15 digits always fit exactly into a double
1999
2759
  if (pos - start > 15) {
2000
- return base.readNumber.call(this, startsWithDot);
2760
+ return this._readNumberCold(startsWithDot);
2001
2761
  }
2002
2762
  this.pos = pos;
2003
2763
  return this.finishToken(tokTypes.num, value);
2004
2764
  }
2005
2765
  // a fraction follows the integer part
2006
2766
  } else {
2007
- return base.readNumber.call(this, startsWithDot);
2767
+ return this._readNumberCold(startsWithDot);
2008
2768
  }
2009
2769
  // decimal fraction: `pos` is at the '.'
2010
2770
  pos++;
@@ -2024,7 +2784,7 @@ class WebpackParser extends BaseParser {
2024
2784
  after > 127 ||
2025
2785
  IDENT_CHAR[after] === 1
2026
2786
  ) {
2027
- return base.readNumber.call(this, startsWithDot);
2787
+ return this._readNumberCold(startsWithDot);
2028
2788
  }
2029
2789
  this.pos = pos;
2030
2790
  this.finishToken(tokTypes.num, Number.parseFloat(input.slice(start, pos)));
@@ -2035,6 +2795,7 @@ class WebpackParser extends BaseParser {
2035
2795
  * `this.input.slice(start)` — a fresh sliced string per (sloppy-mode)
2036
2796
  * function body. Sticky regexes at the offset scan the same grammar with no
2037
2797
  * slice. Same directive-prologue semantics, including the ASI tail checks.
2798
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/parseutil.js
2038
2799
  * @param {number} start offset of the function body's first statement
2039
2800
  * @returns {boolean} true when a 'use strict' directive leads the prologue
2040
2801
  * @this {ParserInternals & { input: string, options: { ecmaVersion: number } }}
@@ -2079,9 +2840,10 @@ class WebpackParser extends BaseParser {
2079
2840
 
2080
2841
  /**
2081
2842
  * Template fast path: when the chunk contains no backslash and no CR, the
2082
- * cooked value is one slice (LF/LS/PS cook to themselves). Escapes, CR
2083
- * normalization and location tracking restart acorn's implementation,
2084
- * which also produces its exact errors.
2843
+ * cooked value is one slice (LF/LS/PS cook to themselves). Escapes and CR
2844
+ * normalization restart the owned cold reader (its exact errors); location
2845
+ * tracking uses acorn's own.
2846
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/tokenize.js
2085
2847
  * @this {ParserInternals}
2086
2848
  * @returns {void}
2087
2849
  */
@@ -2109,9 +2871,9 @@ class WebpackParser extends BaseParser {
2109
2871
  this.pos = pos;
2110
2872
  return this.finishToken(tokTypes.template, input.slice(start, pos));
2111
2873
  }
2112
- // backslash and CR need acorn's cooked-string building
2874
+ // backslash and CR need the cooked-string building of the cold reader
2113
2875
  if (ch === 92 || ch === 13) {
2114
- return base.readTmplToken.call(this);
2876
+ return this._readTmplTokenCold();
2115
2877
  }
2116
2878
  pos++;
2117
2879
  }
@@ -2119,8 +2881,9 @@ class WebpackParser extends BaseParser {
2119
2881
  }
2120
2882
 
2121
2883
  /**
2122
- * Fast path for the common run of plain ASCII whitespace; comments,
2123
- * unicode whitespace and location tracking delegate to acorn.
2884
+ * Fast path for the common run of plain ASCII whitespace; comments and
2885
+ * unicode whitespace use the owned cold reader, location tracking acorn's.
2886
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/tokenize.js
2124
2887
  * @this {ParserInternals & { pos: number }}
2125
2888
  * @returns {void}
2126
2889
  */
@@ -2135,9 +2898,9 @@ class WebpackParser extends BaseParser {
2135
2898
  if (ch === 32 || (ch > 8 && ch < 14)) {
2136
2899
  pos++;
2137
2900
  } else if (ch === 47 || ch > 127) {
2138
- // comments or unicode whitespace: let acorn handle the rest
2901
+ // comments or unicode whitespace: hand off to the cold reader
2139
2902
  this.pos = pos;
2140
- return base.skipSpace.call(this);
2903
+ return this._skipSpaceCold();
2141
2904
  } else {
2142
2905
  break;
2143
2906
  }
@@ -2145,30 +2908,563 @@ class WebpackParser extends BaseParser {
2145
2908
  this.pos = pos;
2146
2909
  }
2147
2910
 
2148
- // ----- word classification (Map/Set lookups, replaces acorn's regexps) -----
2911
+ // ----- cold-path readers (ported from acorn's tokenizer, reached only from
2912
+ // the lazy fast paths above; non-lazy mode keeps acorn's own prototype
2913
+ // methods so plugins that patch them still see acorn's tokenizer) -----
2149
2914
 
2150
2915
  /**
2151
- * Replaces acorn's `readWord`, whose `this.keywords.test(word)` runs an
2152
- * anchored alternation regexp for every identifier and keyword token; a
2153
- * Map lookup on the same keyword set is cheaper.
2916
+ * acorn `readInt`: reads `radix`-base digits (optionally exactly `len` of
2917
+ * them), honoring ES2021 `_` separators.
2918
+ * https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/tokenize.js
2154
2919
  * @this {ParserInternals}
2155
- * @returns {void}
2920
+ * @param {number} radix numeric radix
2921
+ * @param {number=} len fixed digit count (for escapes), else read greedily
2922
+ * @param {boolean=} maybeLegacyOctal whether a leading zero disallows separators
2923
+ * @returns {number | null} the integer value, or null when no digits matched
2156
2924
  */
2157
- readWord() {
2158
- const word = this.readWord1();
2159
- let type = tokTypes.name;
2160
- const len = word.length;
2161
- // every acorn keyword is 2-10 lowercase ASCII chars (`do`…`instanceof`),
2162
- // so most identifiers skip the Map probe entirely (yuku gates its keyword
2163
- // switch the same way)
2164
- if (len >= 2 && len <= 10) {
2165
- const first = word.charCodeAt(0);
2166
- if (first >= 97 && first <= 122) {
2167
- type = this._wordLookups.keywords.get(word) || tokTypes.name;
2168
- }
2169
- }
2170
- this.finishToken(type, word);
2171
- }
2925
+ _readInt(radix, len, maybeLegacyOctal) {
2926
+ const input = this.input;
2927
+ const allowSeparators = this._ecmaVersion >= 12 && len === undefined;
2928
+ const isLegacyOctal =
2929
+ maybeLegacyOctal === true && input.charCodeAt(this.pos) === 48;
2930
+ const start = this.pos;
2931
+ let total = 0;
2932
+ let lastCode = 0;
2933
+ for (
2934
+ let i = 0, e = len === undefined ? Infinity : len;
2935
+ i < e;
2936
+ ++i, ++this.pos
2937
+ ) {
2938
+ const code = input.charCodeAt(this.pos);
2939
+ let val;
2940
+ if (allowSeparators && code === 95) {
2941
+ if (isLegacyOctal) {
2942
+ this.raiseRecoverable(
2943
+ this.pos,
2944
+ "Numeric separator is not allowed in legacy octal numeric literals"
2945
+ );
2946
+ }
2947
+ if (lastCode === 95) {
2948
+ this.raiseRecoverable(
2949
+ this.pos,
2950
+ "Numeric separator must be exactly one underscore"
2951
+ );
2952
+ }
2953
+ if (i === 0) {
2954
+ this.raiseRecoverable(
2955
+ this.pos,
2956
+ "Numeric separator is not allowed at the first of digits"
2957
+ );
2958
+ }
2959
+ lastCode = code;
2960
+ continue;
2961
+ }
2962
+ if (code >= 97) val = code - 97 + 10;
2963
+ else if (code >= 65) val = code - 65 + 10;
2964
+ else if (code >= 48 && code <= 57) val = code - 48;
2965
+ else val = Infinity;
2966
+ if (val >= radix) break;
2967
+ lastCode = code;
2968
+ total = total * radix + val;
2969
+ }
2970
+ if (allowSeparators && lastCode === 95) {
2971
+ this.raiseRecoverable(
2972
+ this.pos - 1,
2973
+ "Numeric separator is not allowed at the last of digits"
2974
+ );
2975
+ }
2976
+ if (this.pos === start || (len !== undefined && this.pos - start !== len)) {
2977
+ return null;
2978
+ }
2979
+ return total;
2980
+ }
2981
+
2982
+ /**
2983
+ * acorn `readRadixNumber`: `0x`/`0o`/`0b` integer or bigint literals.
2984
+ * https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/tokenize.js
2985
+ * @this {ParserInternals}
2986
+ * @param {number} radix numeric radix (2/8/16)
2987
+ * @returns {void}
2988
+ */
2989
+ _readRadixNumber(radix) {
2990
+ const input = this.input;
2991
+ const start = this.pos;
2992
+ this.pos += 2;
2993
+ let val = this._readInt(radix);
2994
+ if (val === null) {
2995
+ this.raise(this.start + 2, `Expected number in radix ${radix}`);
2996
+ }
2997
+ if (this._ecmaVersion >= 11 && input.charCodeAt(this.pos) === 110) {
2998
+ val = /** @type {EXPECTED_ANY} */ (
2999
+ stringToBigInt(input.slice(start, this.pos))
3000
+ );
3001
+ ++this.pos;
3002
+ } else if (isIdentifierStart(this.fullCharCodeAtPos())) {
3003
+ this.raise(this.pos, "Identifier directly after number");
3004
+ }
3005
+ return this.finishToken(tokTypes.num, val);
3006
+ }
3007
+
3008
+ /**
3009
+ * acorn `readNumber`: decimal/float/legacy-octal/bigint literals (the fast
3010
+ * path handles only plain integers and simple fractions).
3011
+ * https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/tokenize.js
3012
+ * @this {ParserInternals}
3013
+ * @param {boolean} startsWithDot whether the literal started with `.`
3014
+ * @returns {void}
3015
+ */
3016
+ _readNumberCold(startsWithDot) {
3017
+ const input = this.input;
3018
+ const start = this.pos;
3019
+ if (!startsWithDot && this._readInt(10, undefined, true) === null) {
3020
+ this.raise(start, "Invalid number");
3021
+ }
3022
+ let octal = this.pos - start >= 2 && input.charCodeAt(start) === 48;
3023
+ if (octal && this.strict) this.raise(start, "Invalid number");
3024
+ let next = input.charCodeAt(this.pos);
3025
+ if (!octal && !startsWithDot && this._ecmaVersion >= 11 && next === 110) {
3026
+ const val = stringToBigInt(input.slice(start, this.pos));
3027
+ ++this.pos;
3028
+ if (isIdentifierStart(this.fullCharCodeAtPos())) {
3029
+ this.raise(this.pos, "Identifier directly after number");
3030
+ }
3031
+ return this.finishToken(tokTypes.num, /** @type {EXPECTED_ANY} */ (val));
3032
+ }
3033
+ if (octal && /[89]/.test(input.slice(start, this.pos))) octal = false;
3034
+ if (next === 46 && !octal) {
3035
+ ++this.pos;
3036
+ this._readInt(10);
3037
+ next = input.charCodeAt(this.pos);
3038
+ }
3039
+ if ((next === 69 || next === 101) && !octal) {
3040
+ next = input.charCodeAt(++this.pos);
3041
+ if (next === 43 || next === 45) ++this.pos;
3042
+ if (this._readInt(10) === null) this.raise(start, "Invalid number");
3043
+ }
3044
+ if (isIdentifierStart(this.fullCharCodeAtPos())) {
3045
+ this.raise(this.pos, "Identifier directly after number");
3046
+ }
3047
+ const val = stringToNumber(input.slice(start, this.pos), octal);
3048
+ return this.finishToken(tokTypes.num, val);
3049
+ }
3050
+
3051
+ /**
3052
+ * acorn `readCodePoint`: a `\u{...}` or `\uXXXX` escape.
3053
+ * https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/tokenize.js
3054
+ * @this {ParserInternals}
3055
+ * @returns {number} the escaped code point
3056
+ */
3057
+ _readCodePoint() {
3058
+ const input = this.input;
3059
+ const ch = input.charCodeAt(this.pos);
3060
+ let code;
3061
+ if (ch === 123) {
3062
+ if (this._ecmaVersion < 6) this.unexpected();
3063
+ const codePos = ++this.pos;
3064
+ code = this._readHexChar(input.indexOf("}", this.pos) - this.pos);
3065
+ ++this.pos;
3066
+ if (code > 0x10ffff) {
3067
+ this.invalidStringToken(codePos, "Code point out of bounds");
3068
+ }
3069
+ } else {
3070
+ code = this._readHexChar(4);
3071
+ }
3072
+ return code;
3073
+ }
3074
+
3075
+ /**
3076
+ * acorn `readHexChar`: exactly `len` hex digits.
3077
+ * https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/tokenize.js
3078
+ * @this {ParserInternals}
3079
+ * @param {number} len number of hex digits
3080
+ * @returns {number} the parsed code
3081
+ */
3082
+ _readHexChar(len) {
3083
+ const codePos = this.pos;
3084
+ const n = this._readInt(16, len);
3085
+ if (n === null) {
3086
+ this.invalidStringToken(codePos, "Bad character escape sequence");
3087
+ }
3088
+ return /** @type {number} */ (n);
3089
+ }
3090
+
3091
+ /**
3092
+ * acorn `readString`: the escape-bearing string reader (the fast path cooks
3093
+ * only the common single-char escapes inline).
3094
+ * https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/tokenize.js
3095
+ * @this {ParserInternals}
3096
+ * @param {number} quote quote char code
3097
+ * @returns {void}
3098
+ */
3099
+ _readStringCold(quote) {
3100
+ const input = this.input;
3101
+ let out = "";
3102
+ let chunkStart = ++this.pos;
3103
+ for (;;) {
3104
+ if (this.pos >= input.length) {
3105
+ this.raise(this.start, "Unterminated string constant");
3106
+ }
3107
+ const ch = input.charCodeAt(this.pos);
3108
+ if (ch === quote) break;
3109
+ if (ch === 92) {
3110
+ out += input.slice(chunkStart, this.pos);
3111
+ out += this._readEscapedChar(false);
3112
+ chunkStart = this.pos;
3113
+ } else if (ch === 0x2028 || ch === 0x2029) {
3114
+ if (this._ecmaVersion < 10) {
3115
+ this.raise(this.start, "Unterminated string constant");
3116
+ }
3117
+ ++this.pos;
3118
+ } else {
3119
+ if (isNewLine(ch)) {
3120
+ this.raise(this.start, "Unterminated string constant");
3121
+ }
3122
+ ++this.pos;
3123
+ }
3124
+ }
3125
+ out += input.slice(chunkStart, this.pos++);
3126
+ return this.finishToken(tokTypes.string, out);
3127
+ }
3128
+
3129
+ /**
3130
+ * acorn `readTmplToken`: the escape/CR-normalizing template reader. Invalid
3131
+ * escapes throw acorn's sentinel via `invalidStringToken`, which acorn's own
3132
+ * `tryReadTemplateToken` (not overridden here) catches to re-read raw.
3133
+ * https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/tokenize.js
3134
+ * @this {ParserInternals}
3135
+ * @returns {void}
3136
+ */
3137
+ _readTmplTokenCold() {
3138
+ const input = this.input;
3139
+ let out = "";
3140
+ let chunkStart = this.pos;
3141
+ for (;;) {
3142
+ if (this.pos >= input.length) {
3143
+ this.raise(this.start, "Unterminated template");
3144
+ }
3145
+ const ch = input.charCodeAt(this.pos);
3146
+ if (ch === 96 || (ch === 36 && input.charCodeAt(this.pos + 1) === 123)) {
3147
+ if (
3148
+ this.pos === this.start &&
3149
+ (this.type === tokTypes.template ||
3150
+ this.type === tokTypes.invalidTemplate)
3151
+ ) {
3152
+ if (ch === 36) {
3153
+ this.pos += 2;
3154
+ return this.finishToken(tokTypes.dollarBraceL);
3155
+ }
3156
+ ++this.pos;
3157
+ return this.finishToken(tokTypes.backQuote);
3158
+ }
3159
+ out += input.slice(chunkStart, this.pos);
3160
+ return this.finishToken(tokTypes.template, out);
3161
+ }
3162
+ if (ch === 92) {
3163
+ out += input.slice(chunkStart, this.pos);
3164
+ out += this._readEscapedChar(true);
3165
+ chunkStart = this.pos;
3166
+ } else if (isNewLine(ch)) {
3167
+ out += input.slice(chunkStart, this.pos);
3168
+ ++this.pos;
3169
+ if (ch === 13 && input.charCodeAt(this.pos) === 10) ++this.pos;
3170
+ out += "\n";
3171
+ chunkStart = this.pos;
3172
+ } else {
3173
+ ++this.pos;
3174
+ }
3175
+ }
3176
+ }
3177
+
3178
+ /**
3179
+ * acorn `readEscapedChar`: cooks one backslash escape in a string/template.
3180
+ * https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/tokenize.js
3181
+ * @this {ParserInternals}
3182
+ * @param {boolean} inTemplate whether the escape is in a template literal
3183
+ * @returns {string} the cooked replacement (may be `""`)
3184
+ */
3185
+ _readEscapedChar(inTemplate) {
3186
+ const input = this.input;
3187
+ let ch = input.charCodeAt(++this.pos);
3188
+ ++this.pos;
3189
+ switch (ch) {
3190
+ case 110:
3191
+ return "\n";
3192
+ case 114:
3193
+ return "\r";
3194
+ case 120:
3195
+ return String.fromCharCode(this._readHexChar(2));
3196
+ case 117:
3197
+ return codePointToString(this._readCodePoint());
3198
+ case 116:
3199
+ return "\t";
3200
+ case 98:
3201
+ return "\b";
3202
+ case 118:
3203
+ return "\u000B";
3204
+ case 102:
3205
+ return "\f";
3206
+ case 13:
3207
+ if (input.charCodeAt(this.pos) === 10) ++this.pos;
3208
+ // falls through
3209
+ case 10:
3210
+ return "";
3211
+ case 56:
3212
+ case 57:
3213
+ if (this.strict) {
3214
+ this.invalidStringToken(this.pos - 1, "Invalid escape sequence");
3215
+ }
3216
+ if (inTemplate) {
3217
+ this.invalidStringToken(
3218
+ this.pos - 1,
3219
+ "Invalid escape sequence in template string"
3220
+ );
3221
+ }
3222
+ // falls through
3223
+ default:
3224
+ if (ch >= 48 && ch <= 55) {
3225
+ let octalStr = /** @type {RegExpMatchArray} */ (
3226
+ input.slice(this.pos - 1, this.pos + 2).match(/^[0-7]+/)
3227
+ )[0];
3228
+ let octal = Number.parseInt(octalStr, 8);
3229
+ if (octal > 255) {
3230
+ octalStr = octalStr.slice(0, -1);
3231
+ octal = Number.parseInt(octalStr, 8);
3232
+ }
3233
+ this.pos += octalStr.length - 1;
3234
+ ch = input.charCodeAt(this.pos);
3235
+ if (
3236
+ (octalStr !== "0" || ch === 56 || ch === 57) &&
3237
+ (this.strict || inTemplate)
3238
+ ) {
3239
+ this.invalidStringToken(
3240
+ this.pos - 1 - octalStr.length,
3241
+ inTemplate
3242
+ ? "Octal literal in template string"
3243
+ : "Octal literal in strict mode"
3244
+ );
3245
+ }
3246
+ return String.fromCharCode(octal);
3247
+ }
3248
+ if (isNewLine(ch)) return "";
3249
+ return String.fromCharCode(ch);
3250
+ }
3251
+ }
3252
+
3253
+ /**
3254
+ * acorn `readWord1`: the escape-/astral-aware identifier reader (the fast
3255
+ * path handles pure-ASCII words).
3256
+ * https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/tokenize.js
3257
+ * @this {ParserInternals}
3258
+ * @returns {string} the (possibly escape-cooked) word
3259
+ */
3260
+ _readWord1Cold() {
3261
+ const input = this.input;
3262
+ this.containsEsc = false;
3263
+ let word = "";
3264
+ let first = true;
3265
+ let chunkStart = this.pos;
3266
+ const astral = this._ecmaVersion >= 6;
3267
+ while (this.pos < input.length) {
3268
+ const ch = this.fullCharCodeAtPos();
3269
+ if (isIdentifierChar(ch, astral)) {
3270
+ this.pos += ch <= 0xffff ? 1 : 2;
3271
+ } else if (ch === 92) {
3272
+ this.containsEsc = true;
3273
+ word += input.slice(chunkStart, this.pos);
3274
+ const escStart = this.pos;
3275
+ if (input.charCodeAt(++this.pos) !== 117) {
3276
+ this.invalidStringToken(
3277
+ this.pos,
3278
+ "Expecting Unicode escape sequence \\uXXXX"
3279
+ );
3280
+ }
3281
+ ++this.pos;
3282
+ const esc = this._readCodePoint();
3283
+ if (!(first ? isIdentifierStart : isIdentifierChar)(esc, astral)) {
3284
+ this.invalidStringToken(escStart, "Invalid Unicode escape");
3285
+ }
3286
+ word += codePointToString(esc);
3287
+ chunkStart = this.pos;
3288
+ } else {
3289
+ break;
3290
+ }
3291
+ first = false;
3292
+ }
3293
+ return word + input.slice(chunkStart, this.pos);
3294
+ }
3295
+
3296
+ /**
3297
+ * acorn `skipSpace` (location-free): unicode whitespace and comments. Only
3298
+ * reached from the lazy fast paths, which keep no line bookkeeping.
3299
+ * https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/tokenize.js
3300
+ * @this {ParserInternals}
3301
+ * @returns {void}
3302
+ */
3303
+ _skipSpaceCold() {
3304
+ const input = this.input;
3305
+ const len = input.length;
3306
+ loop: while (this.pos < len) {
3307
+ const ch = input.charCodeAt(this.pos);
3308
+ switch (ch) {
3309
+ case 32:
3310
+ case 160:
3311
+ ++this.pos;
3312
+ break;
3313
+ case 13:
3314
+ if (input.charCodeAt(this.pos + 1) === 10) ++this.pos;
3315
+ // falls through
3316
+ case 10:
3317
+ case 8232:
3318
+ case 8233:
3319
+ ++this.pos;
3320
+ break;
3321
+ case 47:
3322
+ switch (input.charCodeAt(this.pos + 1)) {
3323
+ case 42:
3324
+ this.skipBlockComment();
3325
+ break;
3326
+ case 47:
3327
+ this.skipLineComment(2);
3328
+ break;
3329
+ default:
3330
+ break loop;
3331
+ }
3332
+ break;
3333
+ default:
3334
+ if (
3335
+ (ch > 8 && ch < 14) ||
3336
+ (ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch)))
3337
+ ) {
3338
+ ++this.pos;
3339
+ } else {
3340
+ break loop;
3341
+ }
3342
+ }
3343
+ }
3344
+ }
3345
+
3346
+ /**
3347
+ * acorn `readToken_numberSign` plus `getTokenFromCode`'s default: a private
3348
+ * identifier (`#x`) or an unexpected-character error.
3349
+ * https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/tokenize.js
3350
+ * @this {ParserInternals}
3351
+ * @param {number} code current char code
3352
+ * @returns {void}
3353
+ */
3354
+ _getUnknownOrPrivate(code) {
3355
+ if (code === 35 && this._ecmaVersion >= 13) {
3356
+ ++this.pos;
3357
+ const next = this.fullCharCodeAtPos();
3358
+ if (isIdentifierStart(next, true) || next === 92) {
3359
+ return this.finishToken(tokTypes.privateId, this.readWord1());
3360
+ }
3361
+ this.raise(this.pos, `Unexpected character '${codePointToString(next)}'`);
3362
+ }
3363
+ this.raise(this.pos, `Unexpected character '${codePointToString(code)}'`);
3364
+ }
3365
+
3366
+ // ----- word classification (Map/Set lookups, replaces acorn's regexps) -----
3367
+
3368
+ /**
3369
+ * Replaces acorn's `readWord`: `readWord1`'s ASCII fast path inlined so the
3370
+ * token type can be memoized per `WORD_CACHE` slot (classification is pure in
3371
+ * the word and the per-option keyword set) — a cache hit serves the type by
3372
+ * identity compare instead of a keywords Map probe.
3373
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/tokenize.js
3374
+ * @this {ParserInternals}
3375
+ * @returns {void}
3376
+ */
3377
+ readWord() {
3378
+ const input = this.input;
3379
+ const start = this.pos;
3380
+ const len = input.length;
3381
+ let hash = 0;
3382
+ let pos = start;
3383
+ while (pos < len) {
3384
+ const ch = input.charCodeAt(pos);
3385
+ if (ch < 128) {
3386
+ if (IDENT_CHAR[ch] === 0) {
3387
+ // backslash escape: restart cold so escape rules see the word
3388
+ if (ch === 92) return this._finishWordSlow(this._readWord1Cold());
3389
+ break;
3390
+ }
3391
+ hash = (Math.imul(hash, 33) + ch) | 0;
3392
+ pos++;
3393
+ } else {
3394
+ return this._finishWordSlow(this._readWord1Cold());
3395
+ }
3396
+ }
3397
+ this.containsEsc = false;
3398
+ this.pos = pos;
3399
+ const wordLen = pos - start;
3400
+ if (wordLen >= 2 && wordLen <= WORD_CACHE_MAX_LEN) {
3401
+ const slot = hash & WORD_CACHE_MASK;
3402
+ const lookups = this._wordLookups;
3403
+ const owner = lookups.id;
3404
+ const cached = WORD_CACHE[slot];
3405
+ /** @type {string} */
3406
+ let word;
3407
+ /** @type {TokenType} */
3408
+ let type;
3409
+ if (
3410
+ cached !== null &&
3411
+ cached.length === wordLen &&
3412
+ // length already matched, so a prefix test is an exact compare —
3413
+ // one builtin instead of a charCodeAt loop per cached character
3414
+ input.startsWith(cached, start)
3415
+ ) {
3416
+ word = cached;
3417
+ if (WORD_TYPE_OWNERS[slot] === owner) {
3418
+ type = /** @type {TokenType} */ (WORD_TYPES[slot]);
3419
+ } else {
3420
+ type = classifyWord(cached, lookups.keywords);
3421
+ WORD_TYPES[slot] = type;
3422
+ WORD_TYPE_OWNERS[slot] = owner;
3423
+ }
3424
+ } else {
3425
+ word = input.slice(start, pos);
3426
+ WORD_CACHE[slot] = word;
3427
+ type = classifyWord(word, lookups.keywords);
3428
+ WORD_TYPES[slot] = type;
3429
+ WORD_TYPE_OWNERS[slot] = owner;
3430
+ }
3431
+ if (type === tokTypes.name && this._inlineFinish) {
3432
+ // finishToken(name) inlined: offset/type/value stores plus
3433
+ // name.updateContext (as in the owned finishToken)
3434
+ this.end = pos;
3435
+ const prevType = this.type;
3436
+ this.type = type;
3437
+ this.value = word;
3438
+ this.exprAllowed =
3439
+ ((word === "of" && !this.exprAllowed) ||
3440
+ (word === "yield" && this.inGeneratorContext())) &&
3441
+ prevType !== tokTypes.dot &&
3442
+ this._ecmaVersion >= 6;
3443
+ return;
3444
+ }
3445
+ return this.finishToken(type, word);
3446
+ }
3447
+ // Every keyword is 2-10 chars, so a word outside the cacheable range is a
3448
+ // plain name and can be neither `of` nor `yield`.
3449
+ if (this._inlineFinish) {
3450
+ this.end = pos;
3451
+ this.type = tokTypes.name;
3452
+ this.value = input.slice(start, pos);
3453
+ this.exprAllowed = false;
3454
+ return;
3455
+ }
3456
+ return this._finishWordSlow(input.slice(start, pos));
3457
+ }
3458
+
3459
+ /**
3460
+ * Uncached tail of `readWord`: escaped/non-ASCII, 1-char and over-long words.
3461
+ * @this {ParserInternals}
3462
+ * @param {string} word the word
3463
+ * @returns {void}
3464
+ */
3465
+ _finishWordSlow(word) {
3466
+ this.finishToken(classifyWord(word, this._wordLookups.keywords), word);
3467
+ }
2172
3468
 
2173
3469
  /**
2174
3470
  * Mirror of acorn's `checkUnreserved` with its two per-identifier regexp
@@ -2176,6 +3472,7 @@ class WebpackParser extends BaseParser {
2176
3472
  * single `reservedKinds` lookup — one hash probe instead of two, and the
2177
3473
  * common plain identifier misses it and returns. Branches and error
2178
3474
  * messages match acorn exactly.
3475
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/expression.js
2179
3476
  * @param {Identifier} ref identifier node
2180
3477
  * @this {ParserInternals}
2181
3478
  */
@@ -2239,38 +3536,196 @@ class WebpackParser extends BaseParser {
2239
3536
  }
2240
3537
 
2241
3538
  /**
2242
- * Replaces acorn's `canInsertSemicolon`, whose line-break check slices the
2243
- * inter-token gap and runs a regexp on it for every ASI decision (hundreds
2244
- * of thousands per file). Scan the gap for a line terminator instead — no
2245
- * slice, no regexp.
2246
- * @returns {boolean} whether a semicolon may be inserted here
3539
+ * Owned `checkLValSimple`, an exact-semantics copy of acorn 8's with the
3540
+ * strict-bind probe inlined: the constructor-installed Set stand-in is one
3541
+ * `has` behind a length gate instead of a closure call per checked name.
3542
+ * A runtime-replaced `reservedWordsStrictBind` falls back to `.test()`.
3543
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/lval.js
3544
+ * @param {Node} expr assignment/binding target
3545
+ * @param {number=} bindingType acorn BIND_* binding type
3546
+ * @param {Record<string, boolean> | null=} checkClashes param-name clash record
3547
+ * @returns {void}
2247
3548
  * @this {ParserInternals}
2248
3549
  */
2249
- canInsertSemicolon() {
2250
- if (this.type === tokTypes.eof || this.type === tokTypes.braceR) {
2251
- return true;
3550
+ checkLValSimple(expr, bindingType, checkClashes) {
3551
+ if (bindingType === undefined) bindingType = BIND_NONE;
3552
+ const isBind = bindingType !== BIND_NONE;
3553
+ const type = expr.type;
3554
+ if (type === "Identifier") {
3555
+ const name = /** @type {Identifier} */ (expr).name;
3556
+ if (this.strict) {
3557
+ const lookups = this._wordLookups;
3558
+ let reserved;
3559
+ if (this.reservedWordsStrictBind === lookups.reservedBindTest) {
3560
+ const len = name.length;
3561
+ reserved =
3562
+ len >= lookups.reservedBindMinLen &&
3563
+ len <= lookups.reservedBindMaxLen &&
3564
+ lookups.reservedBindSet.has(name);
3565
+ } else {
3566
+ reserved = this.reservedWordsStrictBind.test(name);
3567
+ }
3568
+ if (reserved) {
3569
+ this.raiseRecoverable(
3570
+ expr.start,
3571
+ `${isBind ? "Binding " : "Assigning to "}${name} in strict mode`
3572
+ );
3573
+ }
3574
+ }
3575
+ if (isBind) {
3576
+ if (bindingType === BIND_LEXICAL && name === "let") {
3577
+ this.raiseRecoverable(
3578
+ expr.start,
3579
+ "let is disallowed as a lexically bound name"
3580
+ );
3581
+ }
3582
+ if (checkClashes) {
3583
+ if (Object.prototype.hasOwnProperty.call(checkClashes, name)) {
3584
+ this.raiseRecoverable(expr.start, "Argument name clash");
3585
+ }
3586
+ checkClashes[name] = true;
3587
+ }
3588
+ if (bindingType !== BIND_OUTSIDE) {
3589
+ this.declareName(name, bindingType, expr.start);
3590
+ }
3591
+ }
3592
+ return;
2252
3593
  }
2253
- // the owned nextToken already classified the gap; 2 (comment/unicode in
2254
- // the gap, or a token from acorn's tokenizer) falls back to the scan
2255
- return this._gapHasNewline();
3594
+ if (type === "ChainExpression") {
3595
+ this.raiseRecoverable(
3596
+ expr.start,
3597
+ "Optional chaining cannot appear in left-hand side"
3598
+ );
3599
+ return;
3600
+ }
3601
+ if (type === "MemberExpression") {
3602
+ if (isBind) {
3603
+ this.raiseRecoverable(expr.start, "Binding member expression");
3604
+ }
3605
+ return;
3606
+ }
3607
+ if (type === "ParenthesizedExpression") {
3608
+ if (isBind) {
3609
+ this.raiseRecoverable(expr.start, "Binding parenthesized expression");
3610
+ }
3611
+ return this.checkLValSimple(
3612
+ /** @type {Node} */ (
3613
+ /** @type {Node & { expression?: Node }} */ (expr).expression
3614
+ ),
3615
+ bindingType,
3616
+ checkClashes
3617
+ );
3618
+ }
3619
+ this.raise(expr.start, `${isBind ? "Binding" : "Assigning to"} rvalue`);
2256
3620
  }
2257
3621
 
2258
- // ----- comment collection without eager text slicing -----
2259
-
2260
3622
  /**
2261
- * Replaces acorn's `skipLineComment` when comments are collected lazily:
2262
- * the same scan, but no text slice and no position objects. Acorn calls
2263
- * this for `//`, hashbangs and HTML-style comments (varying `startSkip`).
2264
- * @this {ParserInternals}
2265
- * @param {number} startSkip length of the comment opener
3623
+ * Owned `checkLValPattern`, acorn's verbatim, so declarator/assignment
3624
+ * targets stay on owned code down to `checkLValSimple`/`declareName`.
3625
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/lval.js
3626
+ * @param {Node} expr binding pattern
3627
+ * @param {number=} bindingType acorn BIND_* binding type
3628
+ * @param {Record<string, boolean> | null=} checkClashes param-name clash record
2266
3629
  * @returns {void}
3630
+ * @this {ParserInternals}
2267
3631
  */
2268
- skipLineComment(startSkip) {
2269
- const comments = this._lazyComments;
2270
- if (comments === undefined) {
2271
- return base.skipLineComment.call(this, startSkip);
2272
- }
2273
- const input = this.input;
3632
+ checkLValPattern(expr, bindingType, checkClashes) {
3633
+ const type = expr.type;
3634
+ if (type === "ObjectPattern") {
3635
+ const properties = /** @type {Node & { properties: Node[] }} */ (expr)
3636
+ .properties;
3637
+ for (let i = 0; i < properties.length; i++) {
3638
+ this.checkLValInnerPattern(properties[i], bindingType, checkClashes);
3639
+ }
3640
+ } else if (type === "ArrayPattern") {
3641
+ const elements = /** @type {Node & { elements: (Node | null)[] }} */ (
3642
+ expr
3643
+ ).elements;
3644
+ for (let i = 0; i < elements.length; i++) {
3645
+ const element = elements[i];
3646
+ if (element) {
3647
+ this.checkLValInnerPattern(element, bindingType, checkClashes);
3648
+ }
3649
+ }
3650
+ } else {
3651
+ this.checkLValSimple(expr, bindingType, checkClashes);
3652
+ }
3653
+ }
3654
+
3655
+ /**
3656
+ * Owned `checkLValInnerPattern`, acorn's verbatim.
3657
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/lval.js
3658
+ * @param {Node} expr pattern element
3659
+ * @param {number=} bindingType acorn BIND_* binding type
3660
+ * @param {Record<string, boolean> | null=} checkClashes param-name clash record
3661
+ * @returns {void}
3662
+ * @this {ParserInternals}
3663
+ */
3664
+ checkLValInnerPattern(expr, bindingType, checkClashes) {
3665
+ const type = expr.type;
3666
+ if (type === "Property") {
3667
+ // AssignmentProperty has type === "Property"
3668
+ this.checkLValInnerPattern(
3669
+ /** @type {Node} */ (
3670
+ /** @type {Node & { value?: Node }} */ (expr).value
3671
+ ),
3672
+ bindingType,
3673
+ checkClashes
3674
+ );
3675
+ } else if (type === "AssignmentPattern") {
3676
+ this.checkLValPattern(
3677
+ /** @type {Node} */ (/** @type {Node & { left?: Node }} */ (expr).left),
3678
+ bindingType,
3679
+ checkClashes
3680
+ );
3681
+ } else if (type === "RestElement") {
3682
+ this.checkLValPattern(
3683
+ /** @type {Node} */ (
3684
+ /** @type {Node & { argument?: Node }} */ (expr).argument
3685
+ ),
3686
+ bindingType,
3687
+ checkClashes
3688
+ );
3689
+ } else {
3690
+ this.checkLValPattern(expr, bindingType, checkClashes);
3691
+ }
3692
+ }
3693
+
3694
+ /**
3695
+ * Replaces acorn's `canInsertSemicolon`, whose line-break check slices the
3696
+ * inter-token gap and runs a regexp on it for every ASI decision (hundreds
3697
+ * of thousands per file). Scan the gap for a line terminator instead — no
3698
+ * slice, no regexp.
3699
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/parseutil.js
3700
+ * @returns {boolean} whether a semicolon may be inserted here
3701
+ * @this {ParserInternals}
3702
+ */
3703
+ canInsertSemicolon() {
3704
+ if (this.type === tokTypes.eof || this.type === tokTypes.braceR) {
3705
+ return true;
3706
+ }
3707
+ // the owned nextToken already classified the gap; 2 (comment/unicode in
3708
+ // the gap, or a token from acorn's tokenizer) falls back to the scan
3709
+ return this._gapHasNewline();
3710
+ }
3711
+
3712
+ // ----- comment collection without eager text slicing -----
3713
+
3714
+ /**
3715
+ * Replaces acorn's `skipLineComment` when comments are collected lazily:
3716
+ * the same scan, but no text slice and no position objects. Acorn calls
3717
+ * this for `//`, hashbangs and HTML-style comments (varying `startSkip`).
3718
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/tokenize.js
3719
+ * @this {ParserInternals}
3720
+ * @param {number} startSkip length of the comment opener
3721
+ * @returns {void}
3722
+ */
3723
+ skipLineComment(startSkip) {
3724
+ const comments = this._lazyComments;
3725
+ if (comments === undefined) {
3726
+ return base.skipLineComment.call(this, startSkip);
3727
+ }
3728
+ const input = this.input;
2274
3729
  const start = this.pos;
2275
3730
  const len = input.length;
2276
3731
  let pos = start + startSkip;
@@ -2287,6 +3742,7 @@ class WebpackParser extends BaseParser {
2287
3742
  /**
2288
3743
  * Replaces acorn's `skipBlockComment` when comments are collected lazily.
2289
3744
  * Locations are always off in lazy mode, so line breaks need no handling.
3745
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/tokenize.js
2290
3746
  * @this {ParserInternals}
2291
3747
  * @returns {void}
2292
3748
  */
@@ -2307,6 +3763,7 @@ class WebpackParser extends BaseParser {
2307
3763
  // ----- lazy range -----
2308
3764
 
2309
3765
  /**
3766
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/node.js
2310
3767
  * @returns {Node} new node
2311
3768
  * @this {ParserInternals}
2312
3769
  */
@@ -2316,6 +3773,7 @@ class WebpackParser extends BaseParser {
2316
3773
  }
2317
3774
 
2318
3775
  /**
3776
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/node.js
2319
3777
  * @param {number} pos start offset
2320
3778
  * @param {Position=} loc start position when acorn tracks locations
2321
3779
  * @returns {Node} new node
@@ -2330,6 +3788,7 @@ class WebpackParser extends BaseParser {
2330
3788
  * Lazy-mode `finishNode`: acorn's `locations`/`ranges` writes are dead when
2331
3789
  * `range` is served lazily and `loc` not at all, so skip them and the `finishNodeAt`
2332
3790
  * indirection. Runs once per node.
3791
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/node.js
2333
3792
  * @param {Node} node node to finish
2334
3793
  * @param {string} type node type
2335
3794
  * @returns {Node} the finished node
@@ -2345,6 +3804,7 @@ class WebpackParser extends BaseParser {
2345
3804
  /**
2346
3805
  * Mirror of acorn's `copyNode`, which bypasses `startNodeAt` via
2347
3806
  * `new Node(...)` and would otherwise produce non-lazy nodes.
3807
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/node.js
2348
3808
  * @param {Node} node node to copy
2349
3809
  * @returns {Node} copied node
2350
3810
  * @this {ParserInternals}
@@ -2369,6 +3829,7 @@ class WebpackParser extends BaseParser {
2369
3829
  * `IdentifierNode` directly, skipping acorn's `parseIdentNode`/`startNode`/
2370
3830
  * `finishNode` chain and its keyword branches. Keyword-as-identifier
2371
3831
  * (`obj.class`) and non-lazy mode delegate to acorn.
3832
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/expression.js
2372
3833
  * @param {boolean=} liberal whether reserved words are allowed
2373
3834
  * @returns {Identifier} identifier node
2374
3835
  * @this {ParserInternals}
@@ -2396,6 +3857,79 @@ class WebpackParser extends BaseParser {
2396
3857
  return node;
2397
3858
  }
2398
3859
 
3860
+ /**
3861
+ * Owned `parseSubscripts`, an exact-semantics copy of acorn 8's with the
3862
+ * async-arrow-head probe reordered cheapest-first (all operands are pure)
3863
+ * and the arrow exit probe served by `_lastArrow` identity instead of the
3864
+ * megamorphic `.type` load. Delegates when either fast path is off.
3865
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/expression.js
3866
+ * @param {Expression} baseExpr subscript base
3867
+ * @param {number} startPos expression start offset
3868
+ * @param {Position | undefined} startLoc expression start position
3869
+ * @param {boolean=} noCalls whether calls are forbidden (`new` callee)
3870
+ * @param {boolean | string=} forInit for-init context flag
3871
+ * @returns {Expression} expression with subscripts applied
3872
+ * @this {ParserInternals}
3873
+ */
3874
+ parseSubscripts(baseExpr, startPos, startLoc, noCalls, forInit) {
3875
+ if (!this._subscriptFastPath || !this._arrowFastPath) {
3876
+ return base.parseSubscripts.call(
3877
+ this,
3878
+ baseExpr,
3879
+ startPos,
3880
+ startLoc,
3881
+ noCalls,
3882
+ forInit
3883
+ );
3884
+ }
3885
+ // acorn's probe order is (ecmaVersion, type, name, offsets, ASI,
3886
+ // potentialArrowAt); every operand is pure, so the SMI compares run
3887
+ // first and the megamorphic `.type` load only when `async` matched
3888
+ const maybeAsyncArrow =
3889
+ baseExpr.start === this.potentialArrowAt &&
3890
+ baseExpr.end - baseExpr.start === 5 &&
3891
+ this.lastTokEnd === baseExpr.end &&
3892
+ this._ecmaVersion >= 8 &&
3893
+ /** @type {Identifier} */ (baseExpr).name === "async" &&
3894
+ baseExpr.type === "Identifier" &&
3895
+ !this.canInsertSemicolon();
3896
+ let optionalChained = false;
3897
+ for (;;) {
3898
+ const element = this.parseSubscript(
3899
+ baseExpr,
3900
+ startPos,
3901
+ startLoc,
3902
+ noCalls,
3903
+ maybeAsyncArrow,
3904
+ optionalChained,
3905
+ forInit
3906
+ );
3907
+ if (
3908
+ /** @type {Expression & { optional?: boolean }} */ (element).optional
3909
+ ) {
3910
+ optionalChained = true;
3911
+ }
3912
+ if (element === baseExpr || element === this._lastArrow) {
3913
+ if (optionalChained) {
3914
+ const chainNode =
3915
+ /** @type {Node & { expression?: Expression }} */
3916
+ (this.startNodeAt(startPos, startLoc));
3917
+ chainNode.expression = element;
3918
+ return /** @type {Expression} */ (
3919
+ /** @type {unknown} */ (
3920
+ this.finishNode(
3921
+ /** @type {Node} */ (chainNode),
3922
+ "ChainExpression"
3923
+ )
3924
+ )
3925
+ );
3926
+ }
3927
+ return element;
3928
+ }
3929
+ baseExpr = element;
3930
+ }
3931
+ }
3932
+
2399
3933
  /**
2400
3934
  * Owned `parseSubscript`, an exact-semantics copy of acorn 8's with the
2401
3935
  * node construction replaced: member and call nodes are built fully-formed
@@ -2403,6 +3937,7 @@ class WebpackParser extends BaseParser {
2403
3937
  * reachable during it), landing on `MemberExpressionNode`/
2404
3938
  * `CallExpressionNode`'s single shapes. Pre-optional-chaining ecmaVersions
2405
3939
  * and non-lazy mode delegate to acorn.
3940
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/expression.js
2406
3941
  * @param {Expression} baseExpr subscript base
2407
3942
  * @param {number} startPos expression start offset
2408
3943
  * @param {Position | undefined} startLoc expression start position
@@ -2554,6 +4089,7 @@ class WebpackParser extends BaseParser {
2554
4089
  * blocks and plain expression statements) without it; everything rarer, the
2555
4090
  * `name`-token ambiguities (`let`/`async`/`using`/`await` heads) and
2556
4091
  * plugin-overridden parsers delegate to acorn.
4092
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/statement.js
2557
4093
  * @param {string | null} context statement context
2558
4094
  * @param {boolean=} topLevel whether parsing top-level statements
2559
4095
  * @param {unknown=} exports export tracking object
@@ -2579,17 +4115,65 @@ class WebpackParser extends BaseParser {
2579
4115
  case tokTypes.braceL:
2580
4116
  // the owned parseBlock never reads the started node
2581
4117
  return this.parseBlock(true);
4118
+ case tokTypes._function: {
4119
+ if (!this._funcStmtOwn) {
4120
+ return base.parseStatement.call(this, context, topLevel, exports);
4121
+ }
4122
+ // acorn's hanging-function guard (context = single-statement position)
4123
+ if (
4124
+ context &&
4125
+ (this.strict || (context !== "if" && context !== "label"))
4126
+ ) {
4127
+ this.unexpected();
4128
+ }
4129
+ // parseFunctionStatement, inlined without the started node
4130
+ const start = this.start;
4131
+ this.next();
4132
+ return this._parseFunctionAt(
4133
+ start,
4134
+ FUNC_STATEMENT | (context ? FUNC_HANGING_STATEMENT : 0),
4135
+ false,
4136
+ false,
4137
+ undefined
4138
+ );
4139
+ }
2582
4140
  case tokTypes._break:
2583
4141
  case tokTypes._continue:
2584
- case tokTypes._debugger:
2585
- case tokTypes._do:
4142
+ if (!this._stmt2FastPath) {
4143
+ return base.parseStatement.call(this, context, topLevel, exports);
4144
+ }
4145
+ return this._parseBreakContinueStatementAt(
4146
+ this.start,
4147
+ /** @type {string} */ (startType.keyword)
4148
+ );
2586
4149
  case tokTypes._for:
2587
- case tokTypes._function:
2588
- case tokTypes._class:
4150
+ if (!this._stmt2FastPath) {
4151
+ return base.parseStatement.call(this, context, topLevel, exports);
4152
+ }
4153
+ return this._parseForStatementAt(this.start);
2589
4154
  case tokTypes._switch:
4155
+ if (!this._stmt2FastPath) {
4156
+ return base.parseStatement.call(this, context, topLevel, exports);
4157
+ }
4158
+ return this._parseSwitchStatementAt(this.start);
2590
4159
  case tokTypes._throw:
4160
+ if (!this._stmt2FastPath) {
4161
+ return base.parseStatement.call(this, context, topLevel, exports);
4162
+ }
4163
+ return this._parseThrowStatementAt(this.start);
2591
4164
  case tokTypes._try:
4165
+ if (!this._stmt2FastPath) {
4166
+ return base.parseStatement.call(this, context, topLevel, exports);
4167
+ }
4168
+ return this._parseTryStatementAt(this.start);
2592
4169
  case tokTypes._while:
4170
+ if (!this._stmt2FastPath) {
4171
+ return base.parseStatement.call(this, context, topLevel, exports);
4172
+ }
4173
+ return this._parseWhileStatementAt(this.start);
4174
+ case tokTypes._debugger:
4175
+ case tokTypes._do:
4176
+ case tokTypes._class:
2593
4177
  case tokTypes._with:
2594
4178
  case tokTypes.semi:
2595
4179
  case tokTypes._export:
@@ -2641,6 +4225,7 @@ class WebpackParser extends BaseParser {
2641
4225
  * Owned `parseVarStatement`: the passed started node is filled by
2642
4226
  * `parseVar` as acorn expects, then the finished statement lands on
2643
4227
  * `VariableDeclarationNode`'s single shape. Non-lazy mode delegates.
4228
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/statement.js
2644
4229
  * @param {Node} node started statement node from `parseStatement`
2645
4230
  * @param {string} kind declaration kind
2646
4231
  * @param {boolean=} allowMissingInitializer whether `const x;` is allowed
@@ -2662,6 +4247,7 @@ class WebpackParser extends BaseParser {
2662
4247
  /**
2663
4248
  * Statement-position `var`/`let`/`const` without a started node: the
2664
4249
  * declaration lands directly on `VariableDeclarationNode`'s single shape.
4250
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/statement.js
2665
4251
  * @param {number} start statement start offset
2666
4252
  * @param {string} kind declaration kind
2667
4253
  * @param {boolean=} allowMissingInitializer whether `const x;` is allowed
@@ -2670,9 +4256,14 @@ class WebpackParser extends BaseParser {
2670
4256
  */
2671
4257
  _parseVarStatementAt(start, kind, allowMissingInitializer) {
2672
4258
  this.next();
2673
- /** @type {Node[]} */
2674
- const declarations = [];
2675
- this._parseVarInto(declarations, false, kind, allowMissingInitializer);
4259
+ const scratch = this._acquireScratch();
4260
+ const count = this._parseVarInto(
4261
+ scratch,
4262
+ false,
4263
+ kind,
4264
+ allowMissingInitializer
4265
+ );
4266
+ const declarations = this._releaseScratch(scratch, count);
2676
4267
  this.semicolon();
2677
4268
  return /** @type {Node} */ (
2678
4269
  /** @type {unknown} */ (
@@ -2686,6 +4277,7 @@ class WebpackParser extends BaseParser {
2686
4277
  * declarator fully-formed on `VariableDeclaratorNode`'s single shape. The
2687
4278
  * passed node keeps receiving `declarations`/`kind` because
2688
4279
  * `parseForStatement` finishes it itself. Non-lazy mode delegates.
4280
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/statement.js
2689
4281
  * @param {Node} node declaration node to fill
2690
4282
  * @param {boolean} isFor whether parsing a `for` head
2691
4283
  * @param {string} kind declaration kind
@@ -2703,29 +4295,41 @@ class WebpackParser extends BaseParser {
2703
4295
  allowMissingInitializer
2704
4296
  );
2705
4297
  }
2706
- /** @type {Node[]} */
2707
- const declarations = [];
2708
4298
  const target =
2709
- /** @type {Node & { declarations: Node[], kind: string }} */ (node);
2710
- target.declarations = declarations;
4299
+ /** @type {Node & { declarations: Node[] | null, kind: string }} */ (
4300
+ node
4301
+ );
4302
+ // null placeholder keeps acorn's declarations-then-kind key order while
4303
+ // the list is built in scratch (the node is a local in every caller
4304
+ // until this returns, so the placeholder is unobservable)
4305
+ target.declarations = null;
2711
4306
  target.kind = kind;
2712
- this._parseVarInto(declarations, isFor, kind, allowMissingInitializer);
4307
+ const scratch = this._acquireScratch();
4308
+ const count = this._parseVarInto(
4309
+ scratch,
4310
+ isFor,
4311
+ kind,
4312
+ allowMissingInitializer
4313
+ );
4314
+ target.declarations = this._releaseScratch(scratch, count);
2713
4315
  return node;
2714
4316
  }
2715
4317
 
2716
4318
  /**
2717
4319
  * The declarator loop of the owned `parseVar`, shared with the owned
2718
4320
  * `parseStatement`'s node-free statement path.
2719
- * @param {Node[]} declarations output array for the declarators
4321
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/statement.js
4322
+ * @param {Node[]} declarations scratch array for the declarators
2720
4323
  * @param {boolean} isFor whether parsing a `for` head
2721
4324
  * @param {string} kind declaration kind
2722
4325
  * @param {boolean=} allowMissingInitializer whether `const x;` is allowed
2723
- * @returns {void}
4326
+ * @returns {number} number of declarators written
2724
4327
  * @this {ParserInternals}
2725
4328
  */
2726
4329
  _parseVarInto(declarations, isFor, kind, allowMissingInitializer) {
2727
4330
  const ecmaVersion = this._ecmaVersion;
2728
4331
  const usingKind = kind === "using" || kind === "await using";
4332
+ let count = 0;
2729
4333
  for (;;) {
2730
4334
  const declStart = this.start;
2731
4335
  const id = usingKind ? this.parseIdent() : this.parseBindingAtom();
@@ -2768,21 +4372,21 @@ class WebpackParser extends BaseParser {
2768
4372
  "Complex binding patterns require an initialization value"
2769
4373
  );
2770
4374
  }
2771
- declarations.push(
2772
- /** @type {Node} */ (
2773
- /** @type {unknown} */ (
2774
- new VariableDeclaratorNode(declStart, this.lastTokEnd, id, init)
2775
- )
4375
+ declarations[count++] = /** @type {Node} */ (
4376
+ /** @type {unknown} */ (
4377
+ new VariableDeclaratorNode(declStart, this.lastTokEnd, id, init)
2776
4378
  )
2777
4379
  );
2778
4380
  if (!this.eat(tokTypes.comma)) break;
2779
4381
  }
4382
+ return count;
2780
4383
  }
2781
4384
 
2782
4385
  /**
2783
4386
  * Owned `parseExpressionStatement`: the statement lands on
2784
4387
  * `ExpressionStatementNode`'s single shape (the passed started node is
2785
4388
  * discarded, matching acorn's observable output). Non-lazy mode delegates.
4389
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/statement.js
2786
4390
  * @param {Node} node started statement node from `parseStatement`
2787
4391
  * @param {Expression} expr the parsed expression
2788
4392
  * @returns {Node} expression statement
@@ -2797,6 +4401,7 @@ class WebpackParser extends BaseParser {
2797
4401
 
2798
4402
  /**
2799
4403
  * `parseExpressionStatement` without a started node.
4404
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/statement.js
2800
4405
  * @param {number} start statement start offset
2801
4406
  * @param {Expression} expr the parsed expression
2802
4407
  * @returns {Node} expression statement
@@ -2814,6 +4419,7 @@ class WebpackParser extends BaseParser {
2814
4419
  /**
2815
4420
  * Owned `parseBlock`, an exact-semantics copy of acorn 8's landing on
2816
4421
  * `BlockStatementNode`'s single shape. Non-lazy mode delegates.
4422
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/statement.js
2817
4423
  * @param {boolean=} createNewLexicalScope whether the block opens a scope
2818
4424
  * @param {Node=} node started statement node, when called from `parseStatement`
2819
4425
  * @param {boolean=} exitStrict whether to restore sloppy mode at the end
@@ -2831,19 +4437,23 @@ class WebpackParser extends BaseParser {
2831
4437
  }
2832
4438
  if (createNewLexicalScope === undefined) createNewLexicalScope = true;
2833
4439
  const start = this.start;
2834
- /** @type {Node[]} */
2835
- const body = [];
4440
+ const scratch = this._acquireScratch();
4441
+ let count = 0;
2836
4442
  this.expect(tokTypes.braceL);
2837
4443
  if (createNewLexicalScope) this.enterScope(0);
2838
4444
  while (this.type !== tokTypes.braceR) {
2839
- body.push(this.parseStatement(null));
4445
+ scratch[count++] = this.parseStatement(null);
2840
4446
  }
2841
4447
  if (exitStrict) this.strict = false;
2842
4448
  this.next();
2843
4449
  if (createNewLexicalScope) this.exitScope();
2844
4450
  return /** @type {Node} */ (
2845
4451
  /** @type {unknown} */ (
2846
- new BlockStatementNode(start, this.lastTokEnd, body)
4452
+ new BlockStatementNode(
4453
+ start,
4454
+ this.lastTokEnd,
4455
+ this._releaseScratch(scratch, count)
4456
+ )
2847
4457
  )
2848
4458
  );
2849
4459
  }
@@ -2851,6 +4461,7 @@ class WebpackParser extends BaseParser {
2851
4461
  /**
2852
4462
  * Owned `parseIfStatement` landing on `IfStatementNode`'s single shape.
2853
4463
  * Non-lazy mode delegates to acorn.
4464
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/statement.js
2854
4465
  * @param {Node} node started statement node from `parseStatement`
2855
4466
  * @returns {Node} if statement
2856
4467
  * @this {ParserInternals}
@@ -2862,6 +4473,7 @@ class WebpackParser extends BaseParser {
2862
4473
 
2863
4474
  /**
2864
4475
  * `parseIfStatement` without a started node.
4476
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/statement.js
2865
4477
  * @param {number} start statement start offset
2866
4478
  * @returns {Node} if statement
2867
4479
  * @this {ParserInternals}
@@ -2884,6 +4496,7 @@ class WebpackParser extends BaseParser {
2884
4496
  /**
2885
4497
  * Owned `parseReturnStatement` landing on `ReturnStatementNode`'s single
2886
4498
  * shape. Non-lazy mode delegates to acorn.
4499
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/statement.js
2887
4500
  * @param {Node} node started statement node from `parseStatement`
2888
4501
  * @returns {Node} return statement
2889
4502
  * @this {ParserInternals}
@@ -2896,116 +4509,1169 @@ class WebpackParser extends BaseParser {
2896
4509
  return this._parseReturnStatementAt(node.start);
2897
4510
  }
2898
4511
 
2899
- /**
2900
- * In `auto` source type a top-level `return` is script-only syntax. When the
2901
- * strict module parse reaches it with no module construct seen yet, downgrade
2902
- * to a sloppy script in place: the prefix parsed under stricter rules stays a
2903
- * valid sloppy prefix, so the second full parse is avoided.
2904
- * @returns {boolean} true when the parse was downgraded to script
2905
- * @this {ParserInternals}
2906
- */
2907
- _tryModuleFallback() {
4512
+ /**
4513
+ * In `auto` source type a top-level `return` is script-only syntax. When the
4514
+ * strict module parse reaches it with no module construct seen yet, downgrade
4515
+ * to a sloppy script in place: the prefix parsed under stricter rules stays a
4516
+ * valid sloppy prefix, so the second full parse is avoided.
4517
+ * @returns {boolean} true when the parse was downgraded to script
4518
+ * @this {ParserInternals}
4519
+ */
4520
+ _tryModuleFallback() {
4521
+ if (
4522
+ !this._moduleFallback ||
4523
+ this._moduleSyntaxSeen ||
4524
+ !(this.currentVarScope().flags & SCOPE_TOP)
4525
+ ) {
4526
+ return false;
4527
+ }
4528
+ this.options.allowReturnOutsideFunction = true;
4529
+ // acorn stamps Program.sourceType from this at parseTopLevel's end
4530
+ this.options.sourceType = "script";
4531
+ this.strict = false;
4532
+ this.inModule = false;
4533
+ this._moduleFallback = false;
4534
+ return true;
4535
+ }
4536
+
4537
+ /**
4538
+ * `parseReturnStatement` without a started node.
4539
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/statement.js
4540
+ * @param {number} start statement start offset
4541
+ * @returns {Node} return statement
4542
+ * @this {ParserInternals}
4543
+ */
4544
+ _parseReturnStatementAt(start) {
4545
+ if (!this.allowReturn && !this._tryModuleFallback()) {
4546
+ this.raise(this.start, "'return' outside of function");
4547
+ }
4548
+ this.next();
4549
+ /** @type {Expression | null} */
4550
+ let argument = null;
4551
+ // `return` takes an optional argument, so eagerly look for a semicolon
4552
+ if (!(this.eat(tokTypes.semi) || this.insertSemicolon())) {
4553
+ argument = this.parseExpression();
4554
+ this.semicolon();
4555
+ }
4556
+ return /** @type {Node} */ (
4557
+ /** @type {unknown} */ (
4558
+ new ReturnStatementNode(start, this.lastTokEnd, argument)
4559
+ )
4560
+ );
4561
+ }
4562
+
4563
+ /**
4564
+ * `parseBreakContinueStatement` without a started node, landing on
4565
+ * `BreakContinueNode`'s single shape.
4566
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/statement.js
4567
+ * @param {number} start statement start offset
4568
+ * @param {string} keyword `break` or `continue`
4569
+ * @returns {Node} break or continue statement
4570
+ * @this {ParserInternals}
4571
+ */
4572
+ _parseBreakContinueStatementAt(start, keyword) {
4573
+ const isBreak = keyword === "break";
4574
+ this.next();
4575
+ /** @type {Identifier | null} */
4576
+ let label = null;
4577
+ if (this.eat(tokTypes.semi) || this.insertSemicolon()) {
4578
+ // no label
4579
+ } else if (this.type !== tokTypes.name) {
4580
+ this.unexpected();
4581
+ } else {
4582
+ label = this.parseIdent();
4583
+ this.semicolon();
4584
+ }
4585
+ // verify there is an actual destination to break or continue to
4586
+ const labels = this.labels;
4587
+ let i = 0;
4588
+ for (; i < labels.length; ++i) {
4589
+ const lab = labels[i];
4590
+ if (label === null || lab.name === label.name) {
4591
+ if (
4592
+ lab.kind !== null &&
4593
+ lab.kind !== undefined &&
4594
+ (isBreak || lab.kind === "loop")
4595
+ ) {
4596
+ break;
4597
+ }
4598
+ if (label && isBreak) break;
4599
+ }
4600
+ }
4601
+ if (i === labels.length) this.raise(start, `Unsyntactic ${keyword}`);
4602
+ return /** @type {Node} */ (
4603
+ /** @type {unknown} */ (
4604
+ new BreakContinueNode(
4605
+ start,
4606
+ this.lastTokEnd,
4607
+ isBreak ? "BreakStatement" : "ContinueStatement",
4608
+ label
4609
+ )
4610
+ )
4611
+ );
4612
+ }
4613
+
4614
+ /**
4615
+ * `parseForStatement` without a started node (fast-path gate guarantees
4616
+ * `ecmaVersion >= 9`, folding acorn's version probes).
4617
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/statement.js
4618
+ * @param {number} start statement start offset
4619
+ * @returns {Node} for/for-in/for-of statement
4620
+ * @this {ParserInternals}
4621
+ */
4622
+ _parseForStatementAt(start) {
4623
+ this.next();
4624
+ const awaitAt =
4625
+ this.canAwait && this.eatContextual("await") ? this.lastTokStart : -1;
4626
+ this.labels.push(LOOP_LABEL);
4627
+ this.enterScope(0);
4628
+ this.expect(tokTypes.parenL);
4629
+ if (this.type === tokTypes.semi) {
4630
+ if (awaitAt > -1) this.unexpected(awaitAt);
4631
+ return this._parseForAt(start, null);
4632
+ }
4633
+ const isLet = this.isLet();
4634
+ if (this.type === tokTypes._var || this.type === tokTypes._const || isLet) {
4635
+ const initStart = this.start;
4636
+ const kind = isLet ? "let" : /** @type {string} */ (this.value);
4637
+ this.next();
4638
+ const scratch = this._acquireScratch();
4639
+ const count = this._parseVarInto(scratch, true, kind);
4640
+ const declarations = this._releaseScratch(scratch, count);
4641
+ const init = /** @type {Node} */ (
4642
+ /** @type {unknown} */ (
4643
+ new VariableDeclarationNode(
4644
+ initStart,
4645
+ this.lastTokEnd,
4646
+ declarations,
4647
+ kind
4648
+ )
4649
+ )
4650
+ );
4651
+ return this._parseForAfterInitAt(start, init, awaitAt);
4652
+ }
4653
+ const startsWithLet = this.isContextual("let");
4654
+ let isForOf = false;
4655
+ const usingKind = this.isUsing(true)
4656
+ ? "using"
4657
+ : this.isAwaitUsing(true)
4658
+ ? "await using"
4659
+ : null;
4660
+ if (usingKind) {
4661
+ const initStart = this.start;
4662
+ this.next();
4663
+ if (usingKind === "await using") {
4664
+ if (!this.canAwait) {
4665
+ this.raise(
4666
+ this.start,
4667
+ "Await using cannot appear outside of async function"
4668
+ );
4669
+ }
4670
+ this.next();
4671
+ }
4672
+ const scratch = this._acquireScratch();
4673
+ const count = this._parseVarInto(scratch, true, usingKind);
4674
+ const declarations = this._releaseScratch(scratch, count);
4675
+ const init = /** @type {Node} */ (
4676
+ /** @type {unknown} */ (
4677
+ new VariableDeclarationNode(
4678
+ initStart,
4679
+ this.lastTokEnd,
4680
+ declarations,
4681
+ usingKind
4682
+ )
4683
+ )
4684
+ );
4685
+ return this._parseForAfterInitAt(start, init, awaitAt);
4686
+ }
4687
+ const containsEsc = this.containsEsc;
4688
+ const refDestructuringErrors = this._acquireDestructuringErrors();
4689
+ const initPos = this.start;
4690
+ const init =
4691
+ awaitAt > -1
4692
+ ? this.parseExprSubscripts(refDestructuringErrors, "await")
4693
+ : this.parseExpression(true, refDestructuringErrors);
4694
+ if (this.type === tokTypes._in || (isForOf = this.isContextual("of"))) {
4695
+ let isAwait = false;
4696
+ if (awaitAt > -1) {
4697
+ if (this.type === tokTypes._in) this.unexpected(awaitAt);
4698
+ isAwait = true;
4699
+ } else if (
4700
+ isForOf &&
4701
+ init.start === initPos &&
4702
+ !containsEsc &&
4703
+ init.type === "Identifier" &&
4704
+ /** @type {Identifier} */ (init).name === "async"
4705
+ ) {
4706
+ this.unexpected();
4707
+ }
4708
+ if (startsWithLet && isForOf) {
4709
+ this.raise(
4710
+ init.start,
4711
+ "The left-hand side of a for-of loop may not start with 'let'."
4712
+ );
4713
+ }
4714
+ this.toAssignable(init, false, refDestructuringErrors);
4715
+ this.checkLValPattern(init);
4716
+ this._releaseDestructuringErrors();
4717
+ return this._parseForInAt(start, isAwait, /** @type {Node} */ (init));
4718
+ }
4719
+ this.checkExpressionErrors(refDestructuringErrors, true);
4720
+ this._releaseDestructuringErrors();
4721
+ if (awaitAt > -1) this.unexpected(awaitAt);
4722
+ return this._parseForAt(start, /** @type {Node} */ (init));
4723
+ }
4724
+
4725
+ /**
4726
+ * `parseForAfterInit` without a started node.
4727
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/statement.js
4728
+ * @param {number} start statement start offset
4729
+ * @param {Node} init parsed variable declaration
4730
+ * @param {number} awaitAt offset of a consumed `await`, or -1
4731
+ * @returns {Node} for/for-in/for-of statement
4732
+ * @this {ParserInternals}
4733
+ */
4734
+ _parseForAfterInitAt(start, init, awaitAt) {
4735
+ const target =
4736
+ /** @type {Node & { declarations: Node[], kind: string }} */ (init);
4737
+ if (
4738
+ (this.type === tokTypes._in || this.isContextual("of")) &&
4739
+ target.declarations.length === 1
4740
+ ) {
4741
+ if (this.type === tokTypes._in) {
4742
+ if (
4743
+ (target.kind === "using" || target.kind === "await using") &&
4744
+ !(
4745
+ /** @type {Node & { init: Node | null }} */ (target.declarations[0])
4746
+ .init
4747
+ )
4748
+ ) {
4749
+ this.raise(
4750
+ this.start,
4751
+ "Using declaration is not allowed in for-in loops"
4752
+ );
4753
+ }
4754
+ if (awaitAt > -1) this.unexpected(awaitAt);
4755
+ return this._parseForInAt(start, false, init);
4756
+ }
4757
+ return this._parseForInAt(start, awaitAt > -1, init);
4758
+ }
4759
+ if (awaitAt > -1) this.unexpected(awaitAt);
4760
+ return this._parseForAt(start, init);
4761
+ }
4762
+
4763
+ /**
4764
+ * `parseFor` without a started node, landing on `ForStatementNode`'s
4765
+ * single shape.
4766
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/statement.js
4767
+ * @param {number} start statement start offset
4768
+ * @param {Node | null} init init clause
4769
+ * @returns {Node} for statement
4770
+ * @this {ParserInternals}
4771
+ */
4772
+ _parseForAt(start, init) {
4773
+ this.expect(tokTypes.semi);
4774
+ const test = this.type === tokTypes.semi ? null : this.parseExpression();
4775
+ this.expect(tokTypes.semi);
4776
+ const update =
4777
+ this.type === tokTypes.parenR ? null : this.parseExpression();
4778
+ this.expect(tokTypes.parenR);
4779
+ const body = this.parseStatement("for");
4780
+ this.exitScope();
4781
+ this.labels.pop();
4782
+ return /** @type {Node} */ (
4783
+ /** @type {unknown} */ (
4784
+ new ForStatementNode(start, this.lastTokEnd, init, test, update, body)
4785
+ )
4786
+ );
4787
+ }
4788
+
4789
+ /**
4790
+ * `parseForIn` without a started node, landing on `ForInStatementNode`/
4791
+ * `ForOfStatementNode`'s single shapes.
4792
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/statement.js
4793
+ * @param {number} start statement start offset
4794
+ * @param {boolean} isAwait whether this is `for await`
4795
+ * @param {Node} init loop target
4796
+ * @returns {Node} for-in or for-of statement
4797
+ * @this {ParserInternals}
4798
+ */
4799
+ _parseForInAt(start, isAwait, init) {
4800
+ const isForIn = this.type === tokTypes._in;
4801
+ this.next();
4802
+ if (
4803
+ init.type === "VariableDeclaration" &&
4804
+ /** @type {Node & { init: Node | null }} */ (
4805
+ /** @type {Node & { declarations: Node[] }} */ (init).declarations[0]
4806
+ ).init !== null &&
4807
+ (!isForIn ||
4808
+ this.strict ||
4809
+ /** @type {Node & { kind: string }} */ (init).kind !== "var" ||
4810
+ /** @type {Node & { id: Node }} */ (
4811
+ /** @type {Node & { declarations: Node[] }} */ (init).declarations[0]
4812
+ ).id.type !== "Identifier")
4813
+ ) {
4814
+ this.raise(
4815
+ init.start,
4816
+ `${
4817
+ isForIn ? "for-in" : "for-of"
4818
+ } loop variable declaration may not have an initializer`
4819
+ );
4820
+ }
4821
+ const right = isForIn ? this.parseExpression() : this.parseMaybeAssign();
4822
+ this.expect(tokTypes.parenR);
4823
+ const body = this.parseStatement("for");
4824
+ this.exitScope();
4825
+ this.labels.pop();
4826
+ return /** @type {Node} */ (
4827
+ /** @type {unknown} */ (
4828
+ isForIn
4829
+ ? new ForInStatementNode(start, this.lastTokEnd, init, right, body)
4830
+ : new ForOfStatementNode(
4831
+ start,
4832
+ this.lastTokEnd,
4833
+ isAwait,
4834
+ init,
4835
+ right,
4836
+ body
4837
+ )
4838
+ )
4839
+ );
4840
+ }
4841
+
4842
+ /**
4843
+ * `parseSwitchStatement` without a started node, landing on
4844
+ * `SwitchStatementNode`'s single shape with pre-shaped `SwitchCaseNode`s.
4845
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/statement.js
4846
+ * @param {number} start statement start offset
4847
+ * @returns {Node} switch statement
4848
+ * @this {ParserInternals}
4849
+ */
4850
+ _parseSwitchStatementAt(start) {
4851
+ this.next();
4852
+ const discriminant = this.parseParenExpression();
4853
+ /** @type {Node[]} */
4854
+ const cases = [];
4855
+ this.expect(tokTypes.braceL);
4856
+ this.labels.push(SWITCH_LABEL);
4857
+ this.enterScope(SCOPE_SWITCH);
4858
+ /** @type {SwitchCaseNode | undefined} */
4859
+ let cur;
4860
+ for (let sawDefault = false; this.type !== tokTypes.braceR;) {
4861
+ if (this.type === tokTypes._case || this.type === tokTypes._default) {
4862
+ const isCase = this.type === tokTypes._case;
4863
+ if (cur) this.finishNode(/** @type {Node} */ (cur), "SwitchCase");
4864
+ cur = new SwitchCaseNode(this.start);
4865
+ cases.push(/** @type {Node} */ (cur));
4866
+ cur.consequent = [];
4867
+ this.next();
4868
+ if (isCase) {
4869
+ cur.test = this.parseExpression();
4870
+ } else {
4871
+ if (sawDefault) {
4872
+ this.raiseRecoverable(
4873
+ this.lastTokStart,
4874
+ "Multiple default clauses"
4875
+ );
4876
+ }
4877
+ sawDefault = true;
4878
+ cur.test = null;
4879
+ }
4880
+ this.expect(tokTypes.colon);
4881
+ } else {
4882
+ if (!cur) this.unexpected();
4883
+ /** @type {Node[]} */ (
4884
+ /** @type {SwitchCaseNode} */ (cur).consequent
4885
+ ).push(this.parseStatement(null));
4886
+ }
4887
+ }
4888
+ this.exitScope();
4889
+ if (cur) this.finishNode(/** @type {Node} */ (cur), "SwitchCase");
4890
+ this.next(); // closing brace
4891
+ this.labels.pop();
4892
+ return /** @type {Node} */ (
4893
+ /** @type {unknown} */ (
4894
+ new SwitchStatementNode(start, this.lastTokEnd, discriminant, cases)
4895
+ )
4896
+ );
4897
+ }
4898
+
4899
+ /**
4900
+ * `parseThrowStatement` without a started node; the illegal-newline probe
4901
+ * runs on the tokenizer's newline flag instead of acorn's gap slice+regexp.
4902
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/statement.js
4903
+ * @param {number} start statement start offset
4904
+ * @returns {Node} throw statement
4905
+ * @this {ParserInternals}
4906
+ */
4907
+ _parseThrowStatementAt(start) {
4908
+ this.next();
4909
+ if (this._gapHasNewline()) {
4910
+ this.raise(this.lastTokEnd, "Illegal newline after throw");
4911
+ }
4912
+ const argument = this.parseExpression();
4913
+ this.semicolon();
4914
+ return /** @type {Node} */ (
4915
+ /** @type {unknown} */ (
4916
+ new ThrowStatementNode(start, this.lastTokEnd, argument)
4917
+ )
4918
+ );
4919
+ }
4920
+
4921
+ /**
4922
+ * `parseTryStatement` without a started node, landing on
4923
+ * `TryStatementNode`/`CatchClauseNode`'s single shapes.
4924
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/statement.js
4925
+ * @param {number} start statement start offset
4926
+ * @returns {Node} try statement
4927
+ * @this {ParserInternals}
4928
+ */
4929
+ _parseTryStatementAt(start) {
4930
+ this.next();
4931
+ const block = this.parseBlock();
4932
+ /** @type {Node | null} */
4933
+ let handler = null;
4934
+ if (this.type === tokTypes._catch) {
4935
+ const clauseStart = this.start;
4936
+ this.next();
4937
+ /** @type {Node | null} */
4938
+ let param;
4939
+ if (this.eat(tokTypes.parenL)) {
4940
+ param = this.parseCatchClauseParam();
4941
+ } else {
4942
+ if (this._ecmaVersion < 10) this.unexpected();
4943
+ param = null;
4944
+ this.enterScope(0);
4945
+ }
4946
+ const body = this.parseBlock(false);
4947
+ this.exitScope();
4948
+ handler = /** @type {Node} */ (
4949
+ /** @type {unknown} */ (
4950
+ new CatchClauseNode(clauseStart, this.lastTokEnd, param, body)
4951
+ )
4952
+ );
4953
+ }
4954
+ const finalizer = this.eat(tokTypes._finally) ? this.parseBlock() : null;
4955
+ if (!handler && !finalizer) {
4956
+ this.raise(start, "Missing catch or finally clause");
4957
+ }
4958
+ return /** @type {Node} */ (
4959
+ /** @type {unknown} */ (
4960
+ new TryStatementNode(start, this.lastTokEnd, block, handler, finalizer)
4961
+ )
4962
+ );
4963
+ }
4964
+
4965
+ /**
4966
+ * `parseWhileStatement` without a started node, landing on
4967
+ * `WhileStatementNode`'s single shape.
4968
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/statement.js
4969
+ * @param {number} start statement start offset
4970
+ * @returns {Node} while statement
4971
+ * @this {ParserInternals}
4972
+ */
4973
+ _parseWhileStatementAt(start) {
4974
+ this.next();
4975
+ const test = this.parseParenExpression();
4976
+ this.labels.push(LOOP_LABEL);
4977
+ const body = this.parseStatement("while");
4978
+ this.labels.pop();
4979
+ return /** @type {Node} */ (
4980
+ /** @type {unknown} */ (
4981
+ new WhileStatementNode(start, this.lastTokEnd, test, body)
4982
+ )
4983
+ );
4984
+ }
4985
+
4986
+ /**
4987
+ * Owned `parseSpread` landing on `RestSpreadNode`'s single shape.
4988
+ * Non-lazy mode delegates.
4989
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/lval.js
4990
+ * @param {DestructuringErrorsShim | null=} refDestructuringErrors destructuring errors to fill
4991
+ * @returns {Node} spread element
4992
+ * @this {ParserInternals}
4993
+ */
4994
+ parseSpread(refDestructuringErrors) {
4995
+ if (!this._lazy) {
4996
+ return base.parseSpread.call(this, refDestructuringErrors);
4997
+ }
4998
+ const start = this.start;
4999
+ this.next();
5000
+ const argument = this.parseMaybeAssign(false, refDestructuringErrors);
5001
+ return /** @type {Node} */ (
5002
+ /** @type {unknown} */ (
5003
+ new RestSpreadNode(start, this.lastTokEnd, "SpreadElement", argument)
5004
+ )
5005
+ );
5006
+ }
5007
+
5008
+ /**
5009
+ * Owned `parseMaybeConditional` landing on
5010
+ * `ConditionalExpressionNode`'s single shape. Non-lazy mode delegates.
5011
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/expression.js
5012
+ * @param {boolean | string=} forInit for-init context flag
5013
+ * @param {DestructuringErrorsShim | null=} refDestructuringErrors destructuring errors to fill
5014
+ * @returns {Expression} expression node
5015
+ * @this {ParserInternals}
5016
+ */
5017
+ parseMaybeConditional(forInit, refDestructuringErrors) {
5018
+ if (!this._lazy) {
5019
+ return base.parseMaybeConditional.call(
5020
+ this,
5021
+ forInit,
5022
+ refDestructuringErrors
5023
+ );
5024
+ }
5025
+ const startPos = this.start;
5026
+ const expr = this.parseExprOps(forInit, refDestructuringErrors);
5027
+ if (this.checkExpressionErrors(refDestructuringErrors)) return expr;
5028
+ const isArrowAtStart = this._arrowFastPath
5029
+ ? expr === this._lastArrow && expr.start === startPos
5030
+ : expr.type === "ArrowFunctionExpression" && expr.start === startPos;
5031
+ if (!isArrowAtStart && this.eat(tokTypes.question)) {
5032
+ const consequent = this.parseMaybeAssign();
5033
+ this.expect(tokTypes.colon);
5034
+ const alternate = this.parseMaybeAssign(forInit);
5035
+ return /** @type {Expression} */ (
5036
+ /** @type {unknown} */ (
5037
+ new ConditionalExpressionNode(
5038
+ startPos,
5039
+ this.lastTokEnd,
5040
+ expr,
5041
+ consequent,
5042
+ alternate
5043
+ )
5044
+ )
5045
+ );
5046
+ }
5047
+ return expr;
5048
+ }
5049
+
5050
+ /**
5051
+ * acorn's `parseArrowExpression` plus `_lastArrow` tracking — arrows are
5052
+ * created and returned only here, so the expression-spine probes can test
5053
+ * node identity instead of the megamorphic `.type` load. A plugin fabricating
5054
+ * arrow nodes in an overridden `parseExprAtom` bypasses the gate (same
5055
+ * acceptance class as `_stmtFastPath`).
5056
+ * @param {Node} node started node
5057
+ * @param {Node[]} params parameter expressions
5058
+ * @param {boolean} isAsync async arrow
5059
+ * @param {boolean | string=} forInit for-init context flag
5060
+ * @returns {Expression} arrow function expression
5061
+ * @this {ParserInternals}
5062
+ */
5063
+ parseArrowExpression(node, params, isAsync, forInit) {
5064
+ if (!this._funcFastPath) {
5065
+ const result = base.parseArrowExpression.call(
5066
+ this,
5067
+ node,
5068
+ params,
5069
+ isAsync,
5070
+ forInit
5071
+ );
5072
+ this._lastArrow = result;
5073
+ return result;
5074
+ }
5075
+ // acorn's body with the started node replaced by the pre-shaped
5076
+ // FunctionNode (initFunction's writes are its constructor defaults)
5077
+ const functionNode = /** @type {Node} */ (
5078
+ /** @type {unknown} */ (new FunctionNode(node.start))
5079
+ );
5080
+ const oldYieldPos = this.yieldPos;
5081
+ const oldAwaitPos = this.awaitPos;
5082
+ const oldAwaitIdentPos = this.awaitIdentPos;
5083
+ this.enterScope(SCOPE_FUNCTION | (isAsync ? SCOPE_ASYNC : 0) | SCOPE_ARROW);
5084
+ /** @type {Node & { async: boolean, params?: Node[] }} */ (
5085
+ functionNode
5086
+ ).async = Boolean(isAsync);
5087
+ this.yieldPos = 0;
5088
+ this.awaitPos = 0;
5089
+ this.awaitIdentPos = 0;
5090
+ /** @type {Node & { params?: Node[] }} */ (functionNode).params =
5091
+ /** @type {Node[]} */ (this.toAssignableList(params, true));
5092
+ this.parseFunctionBody(functionNode, true, false, forInit);
5093
+ this.yieldPos = oldYieldPos;
5094
+ this.awaitPos = oldAwaitPos;
5095
+ this.awaitIdentPos = oldAwaitIdentPos;
5096
+ const result = /** @type {Expression} */ (
5097
+ /** @type {unknown} */ (
5098
+ this.finishNode(functionNode, "ArrowFunctionExpression")
5099
+ )
5100
+ );
5101
+ this._lastArrow = result;
5102
+ return result;
5103
+ }
5104
+
5105
+ /**
5106
+ * Owned `parseFunction`, an exact-semantics copy of acorn 8's landing on
5107
+ * `FunctionNode`'s pre-declared shape (the passed started node is discarded,
5108
+ * matching acorn's observable output; `initFunction`'s writes are the
5109
+ * constructor defaults). Non-lazy mode and plugin overrides delegate.
5110
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/statement.js
5111
+ * @param {Node} node started node from the caller
5112
+ * @param {number} statement FUNC_* statement bit flags
5113
+ * @param {boolean=} allowExpressionBody whether an expression body is allowed
5114
+ * @param {boolean=} isAsync whether the function is async
5115
+ * @param {boolean | string=} forInit for-init context flag
5116
+ * @returns {Expression} function declaration or expression
5117
+ * @this {ParserInternals}
5118
+ */
5119
+ parseFunction(node, statement, allowExpressionBody, isAsync, forInit) {
5120
+ if (!this._funcFastPath) {
5121
+ return base.parseFunction.call(
5122
+ this,
5123
+ node,
5124
+ statement,
5125
+ allowExpressionBody,
5126
+ isAsync,
5127
+ forInit
5128
+ );
5129
+ }
5130
+ return this._parseFunctionAt(
5131
+ node.start,
5132
+ statement,
5133
+ allowExpressionBody === true,
5134
+ isAsync === true,
5135
+ forInit
5136
+ );
5137
+ }
5138
+
5139
+ /**
5140
+ * `parseFunction` without a started node (fast-path gate guarantees
5141
+ * `ecmaVersion >= 9`, folding acorn's version probes).
5142
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/statement.js
5143
+ * @param {number} start function start offset
5144
+ * @param {number} statement FUNC_* statement bit flags
5145
+ * @param {boolean} allowExpressionBody whether an expression body is allowed
5146
+ * @param {boolean} isAsync whether the function is async
5147
+ * @param {boolean | string=} forInit for-init context flag
5148
+ * @returns {Expression} function declaration or expression
5149
+ * @this {ParserInternals}
5150
+ */
5151
+ _parseFunctionAt(start, statement, allowExpressionBody, isAsync, forInit) {
5152
+ const node =
5153
+ /** @type {Node & { id: Identifier | null, generator: boolean, async: boolean }} */ (
5154
+ /** @type {unknown} */ (new FunctionNode(start))
5155
+ );
5156
+ if (this.type === tokTypes.star && statement & FUNC_HANGING_STATEMENT) {
5157
+ this.unexpected();
5158
+ }
5159
+ node.generator = this.eat(tokTypes.star);
5160
+ node.async = isAsync;
5161
+ if (statement & FUNC_STATEMENT) {
5162
+ node.id =
5163
+ statement & FUNC_NULLABLE_ID && this.type !== tokTypes.name
5164
+ ? null
5165
+ : this.parseIdent();
5166
+ if (node.id && !(statement & FUNC_HANGING_STATEMENT)) {
5167
+ // Annex B: sloppy-mode function declarations bind like vars
5168
+ this.checkLValSimple(
5169
+ node.id,
5170
+ this.strict || node.generator || node.async
5171
+ ? this.treatFunctionsAsVar
5172
+ ? BIND_VAR
5173
+ : BIND_LEXICAL
5174
+ : /* BIND_FUNCTION */ 3
5175
+ );
5176
+ }
5177
+ }
5178
+ const oldYieldPos = this.yieldPos;
5179
+ const oldAwaitPos = this.awaitPos;
5180
+ const oldAwaitIdentPos = this.awaitIdentPos;
5181
+ this.yieldPos = 0;
5182
+ this.awaitPos = 0;
5183
+ this.awaitIdentPos = 0;
5184
+ this.enterScope(
5185
+ SCOPE_FUNCTION |
5186
+ (node.async ? SCOPE_ASYNC : 0) |
5187
+ (node.generator ? SCOPE_GENERATOR : 0)
5188
+ );
5189
+ if (!(statement & FUNC_STATEMENT)) {
5190
+ node.id = this.type === tokTypes.name ? this.parseIdent() : null;
5191
+ }
5192
+ this.parseFunctionParams(node);
5193
+ this.parseFunctionBody(node, allowExpressionBody, false, forInit);
5194
+ this.yieldPos = oldYieldPos;
5195
+ this.awaitPos = oldAwaitPos;
5196
+ this.awaitIdentPos = oldAwaitIdentPos;
5197
+ return /** @type {Expression} */ (
5198
+ /** @type {unknown} */ (
5199
+ this.finishNode(
5200
+ node,
5201
+ statement & FUNC_STATEMENT
5202
+ ? "FunctionDeclaration"
5203
+ : "FunctionExpression"
5204
+ )
5205
+ )
5206
+ );
5207
+ }
5208
+
5209
+ /**
5210
+ * Owned `parseFunctionBody`, an exact-semantics copy of acorn 8's with
5211
+ * `isSimpleParamList` inlined and computed once (acorn walks the params
5212
+ * twice) — the fast-path gate pins it to acorn's own implementation.
5213
+ * Non-lazy mode and plugin overrides delegate.
5214
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/statement.js
5215
+ * @param {Node} node function node being filled
5216
+ * @param {boolean} isArrowFunction whether this is an arrow function
5217
+ * @param {boolean} isMethod whether this is an object/class method
5218
+ * @param {boolean | string=} forInit for-init context flag
5219
+ * @returns {void}
5220
+ * @this {ParserInternals}
5221
+ */
5222
+ parseFunctionBody(node, isArrowFunction, isMethod, forInit) {
5223
+ if (!this._funcFastPath) {
5224
+ return base.parseFunctionBody.call(
5225
+ this,
5226
+ node,
5227
+ isArrowFunction,
5228
+ isMethod,
5229
+ forInit
5230
+ );
5231
+ }
5232
+ const target =
5233
+ /** @type {Node & { id: Identifier | null, params: Node[], body: Node, expression: boolean }} */ (
5234
+ node
5235
+ );
5236
+ const isExpression = isArrowFunction && this.type !== tokTypes.braceL;
5237
+ const oldStrict = this.strict;
5238
+ let useStrict = false;
5239
+ if (isExpression) {
5240
+ target.body = this.parseMaybeAssign(forInit);
5241
+ target.expression = true;
5242
+ this.checkParams(node, false);
5243
+ } else {
5244
+ // acorn's isSimpleParamList, inlined and shared by both its call sites
5245
+ const params = target.params;
5246
+ let simpleParams = true;
5247
+ for (let i = 0; i < params.length; i++) {
5248
+ if (params[i].type !== "Identifier") {
5249
+ simpleParams = false;
5250
+ break;
5251
+ }
5252
+ }
5253
+ if (!oldStrict || !simpleParams) {
5254
+ useStrict = this.strictDirective(this.end);
5255
+ if (useStrict && !simpleParams) {
5256
+ this.raiseRecoverable(
5257
+ node.start,
5258
+ "Illegal 'use strict' directive in function with non-simple parameter list"
5259
+ );
5260
+ }
5261
+ }
5262
+ const oldLabels = this.labels;
5263
+ this.labels = [];
5264
+ if (useStrict) this.strict = true;
5265
+ this.checkParams(
5266
+ node,
5267
+ !oldStrict &&
5268
+ !useStrict &&
5269
+ !isArrowFunction &&
5270
+ !isMethod &&
5271
+ simpleParams
5272
+ );
5273
+ if (this.strict && target.id) {
5274
+ this.checkLValSimple(target.id, BIND_OUTSIDE);
5275
+ }
5276
+ target.body = this.parseBlock(false, undefined, useStrict && !oldStrict);
5277
+ target.expression = false;
5278
+ this.adaptDirectivePrologue(
5279
+ /** @type {Node & { body: Node[] }} */ (target.body).body
5280
+ );
5281
+ this.labels = oldLabels;
5282
+ }
5283
+ this.exitScope();
5284
+ }
5285
+
5286
+ /**
5287
+ * Owned `parseBindingAtom`: acorn's exact dispatch minus the per-call
5288
+ * normalized-options read. Non-lazy mode and pre-ES6 delegate.
5289
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/lval.js
5290
+ * @returns {Node} binding atom (identifier or pattern)
5291
+ * @this {ParserInternals}
5292
+ */
5293
+ parseBindingAtom() {
5294
+ if (!this._lazy || this._ecmaVersion < 6) {
5295
+ return base.parseBindingAtom.call(this);
5296
+ }
5297
+ const type = this.type;
5298
+ if (type === tokTypes.bracketL) {
5299
+ const node = /** @type {Node & { elements?: Node[] }} */ (
5300
+ this.startNode()
5301
+ );
5302
+ this.next();
5303
+ node.elements = this.parseBindingList(tokTypes.bracketR, true, true);
5304
+ return this.finishNode(node, "ArrayPattern");
5305
+ }
5306
+ if (type === tokTypes.braceL) return this.parseObj(true);
5307
+ return this.parseIdent();
5308
+ }
5309
+
5310
+ /**
5311
+ * Owned `parseExprSubscripts`, an exact copy of acorn 8's with the arrow
5312
+ * probe served by `_lastArrow` identity. Delegates when the fast path is off.
5313
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/expression.js
5314
+ * @param {DestructuringErrorsShim | null=} refDestructuringErrors destructuring errors to fill
5315
+ * @param {boolean | string=} forInit for-init context flag
5316
+ * @returns {Expression} expression node
5317
+ * @this {ParserInternals}
5318
+ */
5319
+ parseExprSubscripts(refDestructuringErrors, forInit) {
5320
+ if (!this._arrowFastPath) {
5321
+ return base.parseExprSubscripts.call(
5322
+ this,
5323
+ refDestructuringErrors,
5324
+ forInit
5325
+ );
5326
+ }
5327
+ const startPos = this.start;
5328
+ const startLoc = this.startLoc;
5329
+ const expr = this.parseExprAtom(refDestructuringErrors, forInit);
5330
+ if (
5331
+ expr === this._lastArrow &&
5332
+ this.input.slice(this.lastTokStart, this.lastTokEnd) !== ")"
5333
+ ) {
5334
+ return expr;
5335
+ }
5336
+ const result = this.parseSubscripts(
5337
+ expr,
5338
+ startPos,
5339
+ startLoc,
5340
+ false,
5341
+ forInit
5342
+ );
5343
+ if (refDestructuringErrors && result.type === "MemberExpression") {
5344
+ if (refDestructuringErrors.parenthesizedAssign >= result.start) {
5345
+ refDestructuringErrors.parenthesizedAssign = -1;
5346
+ }
5347
+ if (refDestructuringErrors.parenthesizedBind >= result.start) {
5348
+ refDestructuringErrors.parenthesizedBind = -1;
5349
+ }
5350
+ if (refDestructuringErrors.trailingComma >= result.start) {
5351
+ refDestructuringErrors.trailingComma = -1;
5352
+ }
5353
+ }
5354
+ return result;
5355
+ }
5356
+
5357
+ /**
5358
+ * Owned `parseExprList`, an exact-semantics copy of acorn 8's built in a
5359
+ * scratch array and materialized exactly sized. Non-lazy mode delegates.
5360
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/expression.js
5361
+ * @param {TokenType} close closing token type
5362
+ * @param {boolean} allowTrailingComma whether a trailing comma is allowed
5363
+ * @param {boolean} allowEmpty whether holes are allowed
5364
+ * @param {DestructuringErrorsShim | null=} refDestructuringErrors destructuring errors to fill
5365
+ * @returns {Expression[]} parsed elements (`null` for holes)
5366
+ * @this {ParserInternals}
5367
+ */
5368
+ parseExprList(close, allowTrailingComma, allowEmpty, refDestructuringErrors) {
5369
+ if (!this._lazy) {
5370
+ return base.parseExprList.call(
5371
+ this,
5372
+ close,
5373
+ allowTrailingComma,
5374
+ allowEmpty,
5375
+ refDestructuringErrors
5376
+ );
5377
+ }
5378
+ const scratch = this._acquireScratch();
5379
+ let count = 0;
5380
+ let first = true;
5381
+ while (!this.eat(close)) {
5382
+ if (!first) {
5383
+ this.expect(tokTypes.comma);
5384
+ if (allowTrailingComma && this.afterTrailingComma(close)) break;
5385
+ } else {
5386
+ first = false;
5387
+ }
5388
+ let elt;
5389
+ if (allowEmpty && this.type === tokTypes.comma) {
5390
+ elt = null;
5391
+ } else if (this.type === tokTypes.ellipsis) {
5392
+ elt = this.parseSpread(refDestructuringErrors);
5393
+ if (
5394
+ refDestructuringErrors &&
5395
+ this.type === tokTypes.comma &&
5396
+ refDestructuringErrors.trailingComma < 0
5397
+ ) {
5398
+ refDestructuringErrors.trailingComma = this.start;
5399
+ }
5400
+ } else {
5401
+ elt = this.parseMaybeAssign(false, refDestructuringErrors);
5402
+ }
5403
+ scratch[count++] = elt;
5404
+ }
5405
+ return this._releaseScratch(scratch, count);
5406
+ }
5407
+
5408
+ /**
5409
+ * Owned `parseBindingList`, an exact-semantics copy of acorn 8's built in a
5410
+ * scratch array and materialized exactly sized. Non-lazy mode delegates.
5411
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/lval.js
5412
+ * @param {TokenType} close closing token type
5413
+ * @param {boolean} allowEmpty whether holes are allowed
5414
+ * @param {boolean} allowTrailingComma whether a trailing comma is allowed
5415
+ * @param {boolean=} allowModifiers passed through to `parseAssignableListItem`
5416
+ * @returns {Node[]} parsed bindings (`null` for holes)
5417
+ * @this {ParserInternals}
5418
+ */
5419
+ parseBindingList(close, allowEmpty, allowTrailingComma, allowModifiers) {
5420
+ if (!this._lazy) {
5421
+ return base.parseBindingList.call(
5422
+ this,
5423
+ close,
5424
+ allowEmpty,
5425
+ allowTrailingComma,
5426
+ allowModifiers
5427
+ );
5428
+ }
5429
+ const scratch = this._acquireScratch();
5430
+ let count = 0;
5431
+ let first = true;
5432
+ while (!this.eat(close)) {
5433
+ if (first) first = false;
5434
+ else this.expect(tokTypes.comma);
5435
+ if (allowEmpty && this.type === tokTypes.comma) {
5436
+ scratch[count++] = null;
5437
+ } else if (allowTrailingComma && this.afterTrailingComma(close)) {
5438
+ break;
5439
+ } else if (this.type === tokTypes.ellipsis) {
5440
+ const rest = this.parseRestBinding();
5441
+ this.parseBindingListItem(rest);
5442
+ scratch[count++] = rest;
5443
+ if (this.type === tokTypes.comma) {
5444
+ this.raiseRecoverable(
5445
+ this.start,
5446
+ "Comma is not permitted after the rest element"
5447
+ );
5448
+ }
5449
+ this.expect(close);
5450
+ break;
5451
+ } else {
5452
+ scratch[count++] = this.parseAssignableListItem(allowModifiers);
5453
+ }
5454
+ }
5455
+ return this._releaseScratch(scratch, count);
5456
+ }
5457
+
5458
+ /**
5459
+ * Owned `parseParenAndDistinguishExpression`, an exact-semantics copy of
5460
+ * acorn 8's with the per-paren `DestructuringErrors` record pooled and the
5461
+ * expression list built in a scratch array. Non-lazy mode delegates (the
5462
+ * copy assumes ES6+, which `_lazy` guarantees via `ecmaVersion: "latest"`).
5463
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/expression.js
5464
+ * @param {boolean} canBeArrow whether this may be an arrow head
5465
+ * @param {boolean | string} forInit for-init context flag
5466
+ * @returns {Expression} expression node
5467
+ * @this {ParserInternals}
5468
+ */
5469
+ parseParenAndDistinguishExpression(canBeArrow, forInit) {
5470
+ if (!this._lazy || this._ecmaVersion < 6) {
5471
+ return base.parseParenAndDistinguishExpression.call(
5472
+ this,
5473
+ canBeArrow,
5474
+ forInit
5475
+ );
5476
+ }
5477
+ const startPos = this.start;
5478
+ const startLoc = this.startLoc;
5479
+ const allowTrailingComma = this._ecmaVersion >= 8;
5480
+ this.next();
5481
+
5482
+ const innerStartPos = this.start;
5483
+ const innerStartLoc = this.startLoc;
5484
+ const scratch = this._acquireScratch();
5485
+ let count = 0;
5486
+ let first = true;
5487
+ let lastIsComma = false;
5488
+ const refDestructuringErrors = this._acquireDestructuringErrors();
5489
+ const oldYieldPos = this.yieldPos;
5490
+ const oldAwaitPos = this.awaitPos;
5491
+ let spreadStart;
5492
+ this.yieldPos = 0;
5493
+ this.awaitPos = 0;
5494
+ // Do not save awaitIdentPos to allow checking awaits nested in parameters
5495
+ while (this.type !== tokTypes.parenR) {
5496
+ if (first) first = false;
5497
+ else this.expect(tokTypes.comma);
5498
+ if (
5499
+ allowTrailingComma &&
5500
+ this.afterTrailingComma(tokTypes.parenR, true)
5501
+ ) {
5502
+ lastIsComma = true;
5503
+ break;
5504
+ } else if (this.type === tokTypes.ellipsis) {
5505
+ spreadStart = this.start;
5506
+ scratch[count++] = this.parseParenItem(this.parseRestBinding());
5507
+ if (this.type === tokTypes.comma) {
5508
+ this.raiseRecoverable(
5509
+ this.start,
5510
+ "Comma is not permitted after the rest element"
5511
+ );
5512
+ }
5513
+ break;
5514
+ } else {
5515
+ scratch[count++] = this.parseMaybeAssign(
5516
+ false,
5517
+ refDestructuringErrors,
5518
+ this.parseParenItem
5519
+ );
5520
+ }
5521
+ }
5522
+ const exprList = this._releaseScratch(scratch, count);
5523
+ const innerEndPos = this.lastTokEnd;
5524
+ this.expect(tokTypes.parenR);
5525
+
2908
5526
  if (
2909
- !this._moduleFallback ||
2910
- this._moduleSyntaxSeen ||
2911
- !(this.currentVarScope().flags & SCOPE_TOP)
5527
+ canBeArrow &&
5528
+ this.shouldParseArrow(exprList) &&
5529
+ this.eat(tokTypes.arrow)
2912
5530
  ) {
2913
- return false;
5531
+ this.checkPatternErrors(refDestructuringErrors, false);
5532
+ this.checkYieldAwaitInDefaultParams();
5533
+ // last read of the pooled record was above, so the arrow body parse
5534
+ // below can already reuse its pool slot
5535
+ this._releaseDestructuringErrors();
5536
+ this.yieldPos = oldYieldPos;
5537
+ this.awaitPos = oldAwaitPos;
5538
+ return this.parseParenArrowList(startPos, startLoc, exprList, forInit);
2914
5539
  }
2915
- this.options.allowReturnOutsideFunction = true;
2916
- // acorn stamps Program.sourceType from this at parseTopLevel's end
2917
- this.options.sourceType = "script";
2918
- this.strict = false;
2919
- this.inModule = false;
2920
- this._moduleFallback = false;
2921
- return true;
5540
+
5541
+ if (exprList.length === 0 || lastIsComma) {
5542
+ this.unexpected(this.lastTokStart);
5543
+ }
5544
+ if (spreadStart) this.unexpected(spreadStart);
5545
+ this.checkExpressionErrors(refDestructuringErrors, true);
5546
+ this._releaseDestructuringErrors();
5547
+ this.yieldPos = oldYieldPos || this.yieldPos;
5548
+ this.awaitPos = oldAwaitPos || this.awaitPos;
5549
+
5550
+ /** @type {Expression} */
5551
+ let val;
5552
+ if (exprList.length > 1) {
5553
+ const seq = /** @type {Node & { expressions?: Expression[] }} */ (
5554
+ this.startNodeAt(innerStartPos, innerStartLoc)
5555
+ );
5556
+ seq.expressions = exprList;
5557
+ this.finishNodeAt(
5558
+ /** @type {Node} */ (seq),
5559
+ "SequenceExpression",
5560
+ innerEndPos,
5561
+ undefined
5562
+ );
5563
+ val = /** @type {Expression} */ (/** @type {unknown} */ (seq));
5564
+ } else {
5565
+ val = exprList[0];
5566
+ }
5567
+
5568
+ if (this.options.preserveParens) {
5569
+ const par = /** @type {Node & { expression?: Expression }} */ (
5570
+ this.startNodeAt(startPos, startLoc)
5571
+ );
5572
+ par.expression = val;
5573
+ return /** @type {Expression} */ (
5574
+ /** @type {unknown} */ (
5575
+ this.finishNode(/** @type {Node} */ (par), "ParenthesizedExpression")
5576
+ )
5577
+ );
5578
+ }
5579
+ return val;
2922
5580
  }
2923
5581
 
2924
5582
  /**
2925
- * `parseReturnStatement` without a started node.
2926
- * @param {number} start statement start offset
2927
- * @returns {Node} return statement
5583
+ * Owned `afterTrailingComma`, acorn's verbatim (mode-independent), so the
5584
+ * owned list parsers do not bounce through the dist for each element.
5585
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/parseutil.js
5586
+ * @param {TokenType} tokType closing token type
5587
+ * @param {boolean=} notNext whether to leave the closing token unconsumed
5588
+ * @returns {boolean} true when the list ended on a trailing comma
2928
5589
  * @this {ParserInternals}
2929
5590
  */
2930
- _parseReturnStatementAt(start) {
2931
- if (!this.allowReturn && !this._tryModuleFallback()) {
2932
- this.raise(this.start, "'return' outside of function");
2933
- }
2934
- this.next();
2935
- /** @type {Expression | null} */
2936
- let argument = null;
2937
- // `return` takes an optional argument, so eagerly look for a semicolon
2938
- if (!(this.eat(tokTypes.semi) || this.insertSemicolon())) {
2939
- argument = this.parseExpression();
2940
- this.semicolon();
5591
+ afterTrailingComma(tokType, notNext) {
5592
+ if (this.type === tokType) {
5593
+ const onTrailingComma =
5594
+ /** @type {Options & { onTrailingComma?: (pos: number, loc?: Position) => void }} */
5595
+ (this.options).onTrailingComma;
5596
+ if (onTrailingComma) {
5597
+ onTrailingComma(this.lastTokStart, this.lastTokStartLoc);
5598
+ }
5599
+ if (!notNext) this.next();
5600
+ return true;
2941
5601
  }
2942
- return /** @type {Node} */ (
2943
- /** @type {unknown} */ (
2944
- new ReturnStatementNode(start, this.lastTokEnd, argument)
2945
- )
2946
- );
5602
+ return false;
2947
5603
  }
2948
5604
 
2949
5605
  /**
2950
- * Owned `parseSpread` landing on `RestSpreadNode`'s single shape.
2951
- * Non-lazy mode delegates.
2952
- * @param {DestructuringErrorsShim | null=} refDestructuringErrors destructuring errors to fill
2953
- * @returns {Node} spread element
5606
+ * Owned `checkExpressionErrors`, acorn's verbatim (mode-independent): it
5607
+ * runs several times per expression from the owned spine.
5608
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/parseutil.js
5609
+ * @param {DestructuringErrorsShim | null=} refDestructuringErrors record to inspect
5610
+ * @param {boolean=} andThrow whether to throw on error
5611
+ * @returns {boolean} whether an error position was set
2954
5612
  * @this {ParserInternals}
2955
5613
  */
2956
- parseSpread(refDestructuringErrors) {
2957
- if (!this._lazy) {
2958
- return base.parseSpread.call(this, refDestructuringErrors);
5614
+ checkExpressionErrors(refDestructuringErrors, andThrow) {
5615
+ if (!refDestructuringErrors) return false;
5616
+ const shorthandAssign = refDestructuringErrors.shorthandAssign;
5617
+ const doubleProto = refDestructuringErrors.doubleProto;
5618
+ if (!andThrow) return shorthandAssign >= 0 || doubleProto >= 0;
5619
+ if (shorthandAssign >= 0) {
5620
+ this.raise(
5621
+ shorthandAssign,
5622
+ "Shorthand property assignments are valid only in destructuring patterns"
5623
+ );
2959
5624
  }
2960
- const start = this.start;
2961
- this.next();
2962
- const argument = this.parseMaybeAssign(false, refDestructuringErrors);
2963
- return /** @type {Node} */ (
2964
- /** @type {unknown} */ (
2965
- new RestSpreadNode(start, this.lastTokEnd, "SpreadElement", argument)
2966
- )
2967
- );
5625
+ if (doubleProto >= 0) {
5626
+ this.raiseRecoverable(doubleProto, "Redefinition of __proto__ property");
5627
+ }
5628
+ return false;
2968
5629
  }
2969
5630
 
2970
5631
  /**
2971
- * Owned `parseMaybeConditional` landing on
2972
- * `ConditionalExpressionNode`'s single shape. Non-lazy mode delegates.
5632
+ * Owned `parseExpression`: acorn wraps every call in `catchStackOverflow`,
5633
+ * allocating a fresh closure per expression; inline the try/catch instead
5634
+ * (same overflow-message translation, no closure). Non-lazy mode delegates.
5635
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/expression.js
2973
5636
  * @param {boolean | string=} forInit for-init context flag
2974
5637
  * @param {DestructuringErrorsShim | null=} refDestructuringErrors destructuring errors to fill
2975
5638
  * @returns {Expression} expression node
2976
5639
  * @this {ParserInternals}
2977
5640
  */
2978
- parseMaybeConditional(forInit, refDestructuringErrors) {
5641
+ parseExpression(forInit, refDestructuringErrors) {
2979
5642
  if (!this._lazy) {
2980
- return base.parseMaybeConditional.call(
2981
- this,
2982
- forInit,
2983
- refDestructuringErrors
2984
- );
5643
+ return base.parseExpression.call(this, forInit, refDestructuringErrors);
2985
5644
  }
2986
- const startPos = this.start;
2987
- const expr = this.parseExprOps(forInit, refDestructuringErrors);
2988
- if (this.checkExpressionErrors(refDestructuringErrors)) return expr;
2989
- if (
2990
- !(expr.type === "ArrowFunctionExpression" && expr.start === startPos) &&
2991
- this.eat(tokTypes.question)
2992
- ) {
2993
- const consequent = this.parseMaybeAssign();
2994
- this.expect(tokTypes.colon);
2995
- const alternate = this.parseMaybeAssign(forInit);
2996
- return /** @type {Expression} */ (
2997
- /** @type {unknown} */ (
2998
- new ConditionalExpressionNode(
2999
- startPos,
3000
- this.lastTokEnd,
3001
- expr,
3002
- consequent,
3003
- alternate
3004
- )
3005
- )
3006
- );
5645
+ try {
5646
+ const startPos = this.start;
5647
+ const startLoc = this.startLoc;
5648
+ const expr = this.parseMaybeAssign(forInit, refDestructuringErrors);
5649
+ if (this.type === tokTypes.comma) {
5650
+ const node =
5651
+ /** @type {Node & { expressions?: Expression[] }} */
5652
+ (this.startNodeAt(startPos, startLoc));
5653
+ node.expressions = [expr];
5654
+ while (this.eat(tokTypes.comma)) {
5655
+ node.expressions.push(
5656
+ this.parseMaybeAssign(forInit, refDestructuringErrors)
5657
+ );
5658
+ }
5659
+ return /** @type {Expression} */ (
5660
+ /** @type {unknown} */ (this.finishNode(node, "SequenceExpression"))
5661
+ );
5662
+ }
5663
+ return expr;
5664
+ } catch (err) {
5665
+ // acorn's catchStackOverflow: translate an engine stack-overflow error
5666
+ if (
5667
+ err instanceof Error &&
5668
+ (/\bstack\b.*\b(exceeded|overflow)\b/i.test(err.message) ||
5669
+ /\btoo much recursion\b/i.test(err.message))
5670
+ ) {
5671
+ this.raise(this.start, "Not enough stack space to parse input");
5672
+ }
5673
+ throw err;
3007
5674
  }
3008
- return expr;
3009
5675
  }
3010
5676
 
3011
5677
  /**
@@ -3013,6 +5679,7 @@ class WebpackParser extends BaseParser {
3013
5679
  * lands on `NewExpressionNode`'s single shape (zero-argument calls share
3014
5680
  * one empty array like acorn's `empty`); the rare `new.target` path keeps
3015
5681
  * the generic node. Non-lazy mode delegates.
5682
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/expression.js
3016
5683
  * @returns {Expression} new expression or meta property
3017
5684
  * @this {ParserInternals}
3018
5685
  */
@@ -3084,6 +5751,7 @@ class WebpackParser extends BaseParser {
3084
5751
  * Owned `parseTemplateElement` landing on `TemplateElementNode`'s single
3085
5752
  * shape; matches acorn's CRLF normalization and invalid-escape handling.
3086
5753
  * Non-lazy mode delegates.
5754
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/expression.js
3087
5755
  * @param {{ isTagged: boolean }} opts whether the template is tagged
3088
5756
  * @returns {Node} template element
3089
5757
  * @this {ParserInternals}
@@ -3134,6 +5802,7 @@ class WebpackParser extends BaseParser {
3134
5802
  /**
3135
5803
  * Owned `parseTemplate` landing on `TemplateLiteralNode`'s single shape.
3136
5804
  * Non-lazy mode delegates.
5805
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/expression.js
3137
5806
  * @param {{ isTagged?: boolean }=} opts whether the template is tagged
3138
5807
  * @returns {Node} template literal
3139
5808
  * @this {ParserInternals}
@@ -3148,27 +5817,31 @@ class WebpackParser extends BaseParser {
3148
5817
  const isTagged = (opts !== undefined && opts.isTagged) === true;
3149
5818
  const start = this.start;
3150
5819
  this.next();
3151
- /** @type {Expression[]} */
3152
- const expressions = [];
5820
+ const exprScratch = this._acquireScratch();
5821
+ const quasiScratch = this._acquireScratch();
5822
+ let exprCount = 0;
5823
+ let quasiCount = 0;
3153
5824
  // one options object for all chunks (only `isTagged` is ever read)
3154
5825
  const eltOpts = { isTagged };
3155
5826
  let curElt = /** @type {Node & { tail?: boolean }} */ (
3156
5827
  this.parseTemplateElement(eltOpts)
3157
5828
  );
3158
- const quasis = [/** @type {Node} */ (curElt)];
5829
+ quasiScratch[quasiCount++] = curElt;
3159
5830
  while (!curElt.tail) {
3160
5831
  if (this.type === tokTypes.eof) {
3161
5832
  this.raise(this.pos, "Unterminated template literal");
3162
5833
  }
3163
5834
  this.expect(tokTypes.dollarBraceL);
3164
- expressions.push(this.parseExpression());
5835
+ exprScratch[exprCount++] = this.parseExpression();
3165
5836
  this.expect(tokTypes.braceR);
3166
5837
  curElt = /** @type {Node & { tail?: boolean }} */ (
3167
5838
  this.parseTemplateElement(eltOpts)
3168
5839
  );
3169
- quasis.push(/** @type {Node} */ (curElt));
5840
+ quasiScratch[quasiCount++] = curElt;
3170
5841
  }
3171
5842
  this.next();
5843
+ const quasis = this._releaseScratch(quasiScratch, quasiCount);
5844
+ const expressions = this._releaseScratch(exprScratch, exprCount);
3172
5845
  return /** @type {Node} */ (
3173
5846
  /** @type {unknown} */ (
3174
5847
  new TemplateLiteralNode(start, this.lastTokEnd, expressions, quasis)
@@ -3180,6 +5853,7 @@ class WebpackParser extends BaseParser {
3180
5853
  * Owned `parseMaybeUnary`, an exact-semantics copy of acorn 8's: prefix
3181
5854
  * unary/update and postfix update nodes are built fully-formed on
3182
5855
  * `UnaryNode`'s shared single shape. Non-lazy mode delegates to acorn.
5856
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/expression.js
3183
5857
  * @param {DestructuringErrorsShim | null} refDestructuringErrors destructuring errors to fill
3184
5858
  * @param {boolean} sawUnary whether a unary operator was already consumed
3185
5859
  * @param {boolean} incDec whether the caller was an update operator
@@ -3292,11 +5966,63 @@ class WebpackParser extends BaseParser {
3292
5966
  return expr;
3293
5967
  }
3294
5968
 
5969
+ /**
5970
+ * Collapses the commonest expression shape — a name/number/string/keyword
5971
+ * literal or `this` whose next source character can only begin a token that
5972
+ * cannot continue an expression — into the atom, skipping the seven-layer
5973
+ * `parseMaybeAssign` → `parseExprAtom` descent and its destructuring-error
5974
+ * bookkeeping (which is a net no-op for a bare atom).
5975
+ * @param {boolean | string=} forInit for-init context flag
5976
+ * @returns {Expression | null} the atom, or `null` when the shape does not apply
5977
+ * @this {ParserInternals}
5978
+ */
5979
+ _parseTrivialAtom(forInit) {
5980
+ const code = this.input.charCodeAt(this.end);
5981
+ if (code >= 128 || EXPRESSION_END_CHAR[code] !== 1) return null;
5982
+ const type = this.type;
5983
+ if (type === tokTypes.name) {
5984
+ // both steer the chain themselves (`yield x`, `await x`)
5985
+ const name = /** @type {string} */ (this.value);
5986
+ if (name === "yield" || name === "await") return null;
5987
+ this.potentialArrowAt = this.start;
5988
+ this.potentialArrowInForAwait = forInit === "await";
5989
+ return /** @type {Expression} */ (
5990
+ /** @type {unknown} */ (this.parseIdent(false))
5991
+ );
5992
+ }
5993
+ if (type === tokTypes.num || type === tokTypes.string) {
5994
+ return /** @type {Expression} */ (
5995
+ /** @type {unknown} */ (this.parseLiteral(this.value))
5996
+ );
5997
+ }
5998
+ if (type === tokTypes._this) {
5999
+ const node = new ThisNode(this.start, this.end);
6000
+ this.next();
6001
+ return /** @type {Expression} */ (/** @type {unknown} */ (node));
6002
+ }
6003
+ if (
6004
+ type === tokTypes._null ||
6005
+ type === tokTypes._true ||
6006
+ type === tokTypes._false
6007
+ ) {
6008
+ const node = new LiteralNode(
6009
+ this.start,
6010
+ this.end,
6011
+ type === tokTypes._null ? null : type === tokTypes._true,
6012
+ /** @type {string} */ (type.keyword)
6013
+ );
6014
+ this.next();
6015
+ return /** @type {Expression} */ (/** @type {unknown} */ (node));
6016
+ }
6017
+ return null;
6018
+ }
6019
+
3295
6020
  /**
3296
6021
  * Owned `parseMaybeAssign`, an exact-semantics copy of acorn 8's: the
3297
6022
  * operator is captured before `next()` and the `AssignmentExpression` is
3298
6023
  * built fully-formed on `AssignmentNode`'s single shape after the
3299
6024
  * right-hand parse. Yield and non-lazy mode delegate to acorn.
6025
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/expression.js
3300
6026
  * @param {boolean | string=} forInit for-init context flag
3301
6027
  * @param {DestructuringErrorsShim | null=} refDestructuringErrors destructuring errors to fill
3302
6028
  * @param {((this: unknown, left: Expression, startPos: number, startLoc?: Position) => Expression)=} afterLeftParse hook applied to the parsed left side
@@ -3312,6 +6038,10 @@ class WebpackParser extends BaseParser {
3312
6038
  afterLeftParse
3313
6039
  );
3314
6040
  }
6041
+ if (this._exprFastPath && afterLeftParse === undefined) {
6042
+ const atom = this._parseTrivialAtom(forInit);
6043
+ if (atom !== null) return atom;
6044
+ }
3315
6045
  if (this.isContextual("yield")) {
3316
6046
  if (this.inGenerator) return this.parseYield(forInit);
3317
6047
  // the tokenizer assumed an expression follows `yield`, but this
@@ -3393,6 +6123,7 @@ class WebpackParser extends BaseParser {
3393
6123
  * Owned `parseExprOps`, an exact copy of acorn 8's, so the hot expression
3394
6124
  * spine (`parseMaybeAssign` → here → `parseMaybeUnary`) stays monomorphic
3395
6125
  * on owned code. Non-lazy mode delegates.
6126
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/expression.js
3396
6127
  * @param {boolean | string=} forInit for-init context flag
3397
6128
  * @param {DestructuringErrorsShim | null=} refDestructuringErrors destructuring errors to fill
3398
6129
  * @returns {Expression} expression node
@@ -3411,7 +6142,10 @@ class WebpackParser extends BaseParser {
3411
6142
  forInit
3412
6143
  );
3413
6144
  if (this.checkExpressionErrors(refDestructuringErrors)) return expr;
3414
- return expr.start === startPos && expr.type === "ArrowFunctionExpression"
6145
+ const isArrow = this._arrowFastPath
6146
+ ? expr === this._lastArrow && expr.start === startPos
6147
+ : expr.start === startPos && expr.type === "ArrowFunctionExpression";
6148
+ return isArrow
3415
6149
  ? expr
3416
6150
  : this.parseExprOp(expr, startPos, startLoc, -1, forInit);
3417
6151
  }
@@ -3421,6 +6155,7 @@ class WebpackParser extends BaseParser {
3421
6155
  * same-precedence continuation turned from tail recursion into a loop —
3422
6156
  * `a + b + c + d` runs one frame instead of one per operator. Non-lazy mode
3423
6157
  * delegates.
6158
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/expression.js
3424
6159
  * @param {Expression} left left operand
3425
6160
  * @param {number} leftStartPos expression start offset
3426
6161
  * @param {Position | undefined} leftStartLoc expression start position
@@ -3497,6 +6232,7 @@ class WebpackParser extends BaseParser {
3497
6232
  * Owned `buildBinary` (acorn calls it only from `parseExprOp`): binary and
3498
6233
  * logical nodes are built fully-formed on `BinaryNode`'s single shape.
3499
6234
  * Non-lazy mode delegates to acorn.
6235
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/expression.js
3500
6236
  * @param {number} startPos expression start offset
3501
6237
  * @param {Position | undefined} startLoc expression start position
3502
6238
  * @param {Expression} left left operand
@@ -3544,6 +6280,7 @@ class WebpackParser extends BaseParser {
3544
6280
  * async-function/async-arrow detection), number/string literals, keyword
3545
6281
  * literals (`true`/`false`/`null` — on the same `LiteralNode` shape as the
3546
6282
  * rest) and `this`. Everything else, and non-lazy mode, delegates to acorn.
6283
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/expression.js
3547
6284
  * @param {DestructuringErrorsShim | null=} refDestructuringErrors destructuring errors to fill
3548
6285
  * @param {boolean | string=} forInit for-init context flag
3549
6286
  * @param {boolean=} forNew whether parsed as a `new` callee
@@ -3670,6 +6407,7 @@ class WebpackParser extends BaseParser {
3670
6407
  * Owned `parseObj`, an exact-semantics copy of acorn 8's landing on
3671
6408
  * `ObjectNode`'s single shape (the ES5 trailing-comma gate is dropped —
3672
6409
  * the fast path requires ES11+). Non-lazy mode delegates.
6410
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/expression.js
3673
6411
  * @param {boolean} isPattern whether parsing a binding pattern
3674
6412
  * @param {DestructuringErrorsShim | null=} refDestructuringErrors destructuring errors to fill
3675
6413
  * @returns {Node} object expression or pattern
@@ -3701,8 +6439,8 @@ class WebpackParser extends BaseParser {
3701
6439
  } else {
3702
6440
  propHash = {};
3703
6441
  }
3704
- /** @type {Node[]} */
3705
- const properties = [];
6442
+ const scratch = this._acquireScratch();
6443
+ let count = 0;
3706
6444
  this.next();
3707
6445
  while (!this.eat(tokTypes.braceR)) {
3708
6446
  if (!first) {
@@ -3715,7 +6453,7 @@ class WebpackParser extends BaseParser {
3715
6453
  if (!isPattern) {
3716
6454
  this.checkPropClash(prop, propHash, refDestructuringErrors);
3717
6455
  }
3718
- properties.push(prop);
6456
+ scratch[count++] = prop;
3719
6457
  }
3720
6458
  if (pooled) this._propHashDepth--;
3721
6459
  return /** @type {Node} */ (
@@ -3724,7 +6462,7 @@ class WebpackParser extends BaseParser {
3724
6462
  start,
3725
6463
  this.lastTokEnd,
3726
6464
  isPattern ? "ObjectPattern" : "ObjectExpression",
3727
- properties
6465
+ this._releaseScratch(scratch, count)
3728
6466
  )
3729
6467
  )
3730
6468
  );
@@ -3736,6 +6474,7 @@ class WebpackParser extends BaseParser {
3736
6474
  * `PropertyNode`, which acorn's shared `parsePropertyName`/
3737
6475
  * `parsePropertyValue` then fill in place (ES9+ semantics assumed via the
3738
6476
  * ES11 fast-path gate). Non-lazy mode delegates.
6477
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/expression.js
3739
6478
  * @param {boolean} isPattern whether parsing a binding pattern
3740
6479
  * @param {DestructuringErrorsShim | null=} refDestructuringErrors destructuring errors to fill
3741
6480
  * @returns {Node} property, spread element or rest element
@@ -3829,6 +6568,7 @@ class WebpackParser extends BaseParser {
3829
6568
  /**
3830
6569
  * Owned `parseLiteral`: builds the finished `LiteralNode` directly; the
3831
6570
  * `bigint` branch matches acorn's. Non-lazy mode delegates to acorn.
6571
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/expression.js
3832
6572
  * @param {unknown} value literal value
3833
6573
  * @returns {Node} literal node
3834
6574
  * @this {ParserInternals}
@@ -3854,6 +6594,7 @@ class WebpackParser extends BaseParser {
3854
6594
  * then builds the value with a second `new RegExp`. This override scans
3855
6595
  * like acorn, keeps acorn's flag validation (for its exact messages) and
3856
6596
  * lets one `new RegExp` be both the V8-backed validation and the value.
6597
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/tokenize.js
3857
6598
  * @this {ParserInternals & { regexpState: unknown }}
3858
6599
  * @returns {void}
3859
6600
  */
@@ -3926,6 +6667,7 @@ class WebpackParser extends BaseParser {
3926
6667
  * that validation costs several percent of parse time. Raise from V8's
3927
6668
  * verdict instead — invalid patterns still fail the module build, only
3928
6669
  * exotic engine-specific message texts may differ.
6670
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/regexp.js
3929
6671
  * @param {{ start: number, source: string, flags: string }} state acorn regexp validation state
3930
6672
  * @this {ParserInternals}
3931
6673
  */
@@ -3941,6 +6683,7 @@ class WebpackParser extends BaseParser {
3941
6683
  // ----- scope tracking (Set-based, replaces acorn's array + indexOf) -----
3942
6684
 
3943
6685
  /**
6686
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/scope.js
3944
6687
  * @param {number} flags scope flags
3945
6688
  * @this {ParserInternals}
3946
6689
  */
@@ -3950,6 +6693,7 @@ class WebpackParser extends BaseParser {
3950
6693
 
3951
6694
  /**
3952
6695
  * Set-backed replacement for acorn's `declareName` on Set-backed scopes.
6696
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/scope.js
3953
6697
  * @param {string} name declared name
3954
6698
  * @param {number} bindingType acorn BIND_* binding type
3955
6699
  * @param {number} pos source offset for redeclaration errors
@@ -4020,6 +6764,7 @@ class WebpackParser extends BaseParser {
4020
6764
 
4021
6765
  /**
4022
6766
  * Set-backed replacement for acorn's `checkLocalExport` on Set-backed scopes.
6767
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/scope.js
4023
6768
  * @param {Identifier} id exported identifier
4024
6769
  * @this {ParserInternals}
4025
6770
  */
@@ -4095,6 +6840,7 @@ class WebpackParser extends BaseParser {
4095
6840
  // ----- import phases (`import defer/source`, `import.defer/source()`) -----
4096
6841
 
4097
6842
  /**
6843
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/statement.js
4098
6844
  * @param {Node & { phase?: ImportPhase }} node import declaration node
4099
6845
  * @returns {Node} finished node
4100
6846
  * @this {ParserInternals}
@@ -4110,6 +6856,7 @@ class WebpackParser extends BaseParser {
4110
6856
  /**
4111
6857
  * Owned `parseExport` only to flag module syntax for the auto-fallback guard;
4112
6858
  * parsing itself delegates to acorn.
6859
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/statement.js
4113
6860
  * @param {Node} node started export node
4114
6861
  * @param {unknown} exports export-name tracking object
4115
6862
  * @returns {Node} export declaration
@@ -4123,6 +6870,7 @@ class WebpackParser extends BaseParser {
4123
6870
  /**
4124
6871
  * Owned `parseAwait` only to flag top-level await (module-only) for the
4125
6872
  * auto-fallback guard; await inside a function is not module syntax.
6873
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/expression.js
4126
6874
  * @param {boolean | string=} forInit for-loop init context flag
4127
6875
  * @returns {Expression} await expression
4128
6876
  * @this {ParserInternals}
@@ -4135,6 +6883,7 @@ class WebpackParser extends BaseParser {
4135
6883
  }
4136
6884
 
4137
6885
  /**
6886
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/statement.js
4138
6887
  * @returns {AnyImportSpecifier[]} import specifiers
4139
6888
  * @this {ParserInternals}
4140
6889
  */
@@ -4211,6 +6960,7 @@ class WebpackParser extends BaseParser {
4211
6960
  }
4212
6961
 
4213
6962
  /**
6963
+ * acorn source: https://github.com/acornjs/acorn/blob/8.17.0/acorn/src/expression.js
4214
6964
  * @param {boolean} forNew whether parsed as the operand of `new`
4215
6965
  * @returns {Expression} expression node
4216
6966
  * @this {ParserInternals}
@@ -4299,7 +7049,7 @@ class WebpackParser extends BaseParser {
4299
7049
  }
4300
7050
  }
4301
7051
 
4302
- /** @typedef {import("../Dependency").SourcePosition} SourcePosition */
7052
+ /** @import { SourcePosition } from "../Dependency" */
4303
7053
 
4304
7054
  /**
4305
7055
  * Whether a raw string literal contains a legacy octal (`\47`, `\0` followed by
@@ -4333,13 +7083,13 @@ const hasOctalEscape = (raw) => {
4333
7083
  // these two helpers.
4334
7084
 
4335
7085
  /**
4336
- * Offset of each line's first character. Char-code scan matching acorn's
4337
- * `lineBreak` semantics (CRLF is one break): a regex `exec` loop here
4338
- * allocates a match array per line.
7086
+ * Exact char-code scan matching acorn's `lineBreak` semantics (CRLF is one
7087
+ * break). Kept as the fallback for the terminators `buildLineStarts` cannot
7088
+ * find with a native search.
4339
7089
  * @param {string} source source code
4340
7090
  * @returns {number[]} line start offsets
4341
7091
  */
4342
- const buildLineStarts = (source) => {
7092
+ const scanLineStarts = (source) => {
4343
7093
  const len = source.length;
4344
7094
  const lineStarts = [0];
4345
7095
  for (let i = 0; i < len; i++) {
@@ -4356,6 +7106,33 @@ const buildLineStarts = (source) => {
4356
7106
  return lineStarts;
4357
7107
  };
4358
7108
 
7109
+ /**
7110
+ * Offset of each line's first character. `indexOf` searches the source natively,
7111
+ * which beats a char-code loop by ~3x; CRLF needs no special case because it
7112
+ * starts the next line after its `\n` either way. A lone `\r` or a unicode
7113
+ * separator does break that equivalence, so those fall back to the char scan.
7114
+ * @param {string} source source code
7115
+ * @returns {number[]} line start offsets
7116
+ */
7117
+ const buildLineStarts = (source) => {
7118
+ if (!source.includes("\u2028") && !source.includes("\u2029")) {
7119
+ let cr = source.indexOf("\r");
7120
+ while (cr !== -1 && source.charCodeAt(cr + 1) === 10) {
7121
+ cr = source.indexOf("\r", cr + 2);
7122
+ }
7123
+ if (cr === -1) {
7124
+ const lineStarts = [0];
7125
+ let i = source.indexOf("\n");
7126
+ while (i !== -1) {
7127
+ lineStarts.push(i + 1);
7128
+ i = source.indexOf("\n", i + 1);
7129
+ }
7130
+ return lineStarts;
7131
+ }
7132
+ }
7133
+ return scanLineStarts(source);
7134
+ };
7135
+
4359
7136
  /**
4360
7137
  * Binary search for the line containing the offset.
4361
7138
  * @param {number[]} lineStarts line start offsets
@@ -4373,8 +7150,334 @@ const positionAt = (lineStarts, offset) => {
4373
7150
  return { line: lo + 1, column: offset - lineStarts[lo] };
4374
7151
  };
4375
7152
 
7153
+ const KEYWORDS_BEFORE_REGEXP = new Set([
7154
+ "await",
7155
+ "case",
7156
+ "delete",
7157
+ "do",
7158
+ "else",
7159
+ "in",
7160
+ "instanceof",
7161
+ "new",
7162
+ "of",
7163
+ "return",
7164
+ "throw",
7165
+ "typeof",
7166
+ "void",
7167
+ "yield"
7168
+ ]);
7169
+
7170
+ /**
7171
+ * Whether a `/` at this point starts a regexp rather than division, decided by
7172
+ * what precedes it: after a value (name, number, `)`, `]`) it divides, after an
7173
+ * operator or a keyword it opens a regexp. `)` and `]` are always read as
7174
+ * division — telling `if (a) /re/` from `(a) / b` needs paren-context tracking
7175
+ * this single pass does not carry.
7176
+ * @param {string} source source code
7177
+ * @param {number} slash offset of the `/`
7178
+ * @returns {boolean} true when a regexp literal starts here
7179
+ */
7180
+ const startsRegExp = (source, slash) => {
7181
+ let i = slash - 1;
7182
+ while (i >= 0) {
7183
+ const code = source.charCodeAt(i);
7184
+ if (code === 32 || code === 9 || code === 10 || code === 13) i--;
7185
+ else break;
7186
+ }
7187
+ if (i < 0) return true;
7188
+ const code = source.charCodeAt(i);
7189
+ if (code === 41 || code === 93) return false;
7190
+ if (!isIdentifierChar(code)) return true;
7191
+ // a keyword before `/` is an operator position (`return /re/`), a name is not
7192
+ let start = i;
7193
+ while (start >= 0 && isIdentifierChar(source.charCodeAt(start))) start--;
7194
+ return KEYWORDS_BEFORE_REGEXP.has(source.slice(start + 1, i + 1));
7195
+ };
7196
+
7197
+ /**
7198
+ * Skips whitespace and comments, returning the next code offset.
7199
+ * @param {string} source source code
7200
+ * @param {number} i offset to start at
7201
+ * @returns {number} offset of the next significant character
7202
+ */
7203
+ const skipTrivia = (source, i) => {
7204
+ while (i < source.length) {
7205
+ const code = source.charCodeAt(i);
7206
+ if (code === 32 || code === 9 || code === 10 || code === 13) {
7207
+ i++;
7208
+ } else if (code === 47 && source.charCodeAt(i + 1) === 47) {
7209
+ const end = source.indexOf("\n", i + 2);
7210
+ i = end === -1 ? source.length : end + 1;
7211
+ } else if (code === 47 && source.charCodeAt(i + 1) === 42) {
7212
+ const end = source.indexOf("*/", i + 2);
7213
+ i = end === -1 ? source.length : end + 2;
7214
+ } else {
7215
+ break;
7216
+ }
7217
+ }
7218
+ return i;
7219
+ };
7220
+
7221
+ /** @type {Map<number, string>} */
7222
+ const SINGLE_CHARACTER_ESCAPES = new Map([
7223
+ [110, "\n"],
7224
+ [114, "\r"],
7225
+ [116, "\t"],
7226
+ [98, "\b"],
7227
+ [118, "\u000B"],
7228
+ [102, "\f"]
7229
+ ]);
7230
+
7231
+ /**
7232
+ * Reads the hex digits of a `\x`/`\u` escape.
7233
+ * @param {string} source source code
7234
+ * @param {number} i offset of the first digit
7235
+ * @param {number} length how many digits to read
7236
+ * @returns {number} the code point, or `-1` when a digit is missing
7237
+ */
7238
+ const readHexEscapeValue = (source, i, length) => {
7239
+ let value = 0;
7240
+ for (let j = i; j < i + length; j++) {
7241
+ const digit = Number.parseInt(source[j], 16);
7242
+ if (Number.isNaN(digit)) return -1;
7243
+ value = value * 16 + digit;
7244
+ }
7245
+ return value;
7246
+ };
7247
+
7248
+ /**
7249
+ * Cooks one escape sequence, sloppy-mode script semantics (legacy octal included).
7250
+ * @param {string} source source code
7251
+ * @param {number} i offset just past the backslash
7252
+ * @returns {{ value: string, end: number } | undefined} the cooked text, or `undefined` when malformed
7253
+ */
7254
+ const readEscape = (source, i) => {
7255
+ if (i >= source.length) return;
7256
+ const code = source.charCodeAt(i);
7257
+ const single = SINGLE_CHARACTER_ESCAPES.get(code);
7258
+ if (single !== undefined) return { value: single, end: i + 1 };
7259
+ // a line continuation cooks to nothing; `\r\n` counts as one terminator
7260
+ if (code === 13) {
7261
+ return { value: "", end: source.charCodeAt(i + 1) === 10 ? i + 2 : i + 1 };
7262
+ }
7263
+ if (code === 10 || code === 0x2028 || code === 0x2029) {
7264
+ return { value: "", end: i + 1 };
7265
+ }
7266
+ if (code === 120) {
7267
+ const value = readHexEscapeValue(source, i + 1, 2);
7268
+ if (value === -1) return;
7269
+ return { value: String.fromCodePoint(value), end: i + 3 };
7270
+ }
7271
+ if (code === 117) {
7272
+ if (source.charCodeAt(i + 1) === 123) {
7273
+ const end = source.indexOf("}", i + 2);
7274
+ if (end === -1 || end === i + 2) return;
7275
+ const value = readHexEscapeValue(source, i + 2, end - i - 2);
7276
+ if (value === -1 || value > 0x10ffff) return;
7277
+ return { value: String.fromCodePoint(value), end: end + 1 };
7278
+ }
7279
+ const value = readHexEscapeValue(source, i + 1, 4);
7280
+ if (value === -1) return;
7281
+ return { value: String.fromCodePoint(value), end: i + 5 };
7282
+ }
7283
+ if (code >= 48 && code <= 55) {
7284
+ let digits = /** @type {RegExpMatchArray} */ (
7285
+ source.slice(i, i + 3).match(/^[0-7]+/)
7286
+ )[0];
7287
+ if (Number.parseInt(digits, 8) > 255) digits = digits.slice(0, -1);
7288
+ return {
7289
+ value: String.fromCharCode(Number.parseInt(digits, 8)),
7290
+ end: i + digits.length
7291
+ };
7292
+ }
7293
+ return { value: source[i], end: i + 1 };
7294
+ };
7295
+
7296
+ /**
7297
+ * Reads a `'`/`"` string starting at `i`, cooking its escapes the way the ast
7298
+ * this replaces did — a `Literal` node carries the cooked value, so a specifier
7299
+ * written `"\x2e/a"` is the same dependency as `"./a"`. Templates are not
7300
+ * specifiers here: the ast only matched `Literal` nodes.
7301
+ * @param {string} source source code
7302
+ * @param {number} i offset of the opening quote
7303
+ * @returns {{ value: string, end: number } | undefined} the literal
7304
+ */
7305
+ const readStringLiteral = (source, i) => {
7306
+ const quote = source.charCodeAt(i);
7307
+ if (quote !== 34 && quote !== 39) return;
7308
+ for (let j = i + 1; j < source.length; j++) {
7309
+ const code = source.charCodeAt(j);
7310
+ if (code === 10 || code === 13) return;
7311
+ if (code === 92) return readEscapedStringLiteral(source, i, j);
7312
+ if (code === quote) return { value: source.slice(i + 1, j), end: j + 1 };
7313
+ }
7314
+ };
7315
+
7316
+ /**
7317
+ * Continues `readStringLiteral` from its first escape, so the unescaped case
7318
+ * stays a single slice.
7319
+ * @param {string} source source code
7320
+ * @param {number} i offset of the opening quote
7321
+ * @param {number} firstEscape offset of the first backslash
7322
+ * @returns {{ value: string, end: number } | undefined} the literal
7323
+ */
7324
+ const readEscapedStringLiteral = (source, i, firstEscape) => {
7325
+ const quote = source.charCodeAt(i);
7326
+ let value = "";
7327
+ let segment = i + 1;
7328
+ for (let j = firstEscape; j < source.length; j++) {
7329
+ const code = source.charCodeAt(j);
7330
+ if (code === 10 || code === 13) return;
7331
+ if (code === quote) {
7332
+ return { value: value + source.slice(segment, j), end: j + 1 };
7333
+ }
7334
+ if (code !== 92) continue;
7335
+ const escape = readEscape(source, j + 1);
7336
+ if (escape === undefined) return;
7337
+ value += source.slice(segment, j) + escape.value;
7338
+ segment = escape.end;
7339
+ j = escape.end - 1;
7340
+ }
7341
+ };
7342
+
7343
+ /**
7344
+ * Scans a template body from `i` (just past a backtick or a `}` resuming one).
7345
+ * @param {string} source source code
7346
+ * @param {number} i offset inside the template
7347
+ * @returns {{ end: number, substitution: boolean }} where the body stops
7348
+ */
7349
+ const skipTemplateBody = (source, i) => {
7350
+ for (; i < source.length; i++) {
7351
+ const code = source.charCodeAt(i);
7352
+ if (code === 92) {
7353
+ i++;
7354
+ } else if (code === 96) {
7355
+ return { end: i, substitution: false };
7356
+ } else if (code === 36 && source.charCodeAt(i + 1) === 123) {
7357
+ return { end: i + 1, substitution: true };
7358
+ }
7359
+ }
7360
+ return { end: source.length, substitution: false };
7361
+ };
7362
+
7363
+ /**
7364
+ * Collects the specifiers of static `require("…")` calls. A scan rather than a
7365
+ * parse: it only has to skip what could hide or fake a call — comments, strings,
7366
+ * templates and regexps — so it costs one pass and builds no ast. Template
7367
+ * substitutions are scanned as the code they are, which is why the brace depth
7368
+ * of each open `${` is tracked.
7369
+ * @param {string} source source code
7370
+ * @returns {Set<string>} required specifiers
7371
+ */
7372
+ const collectCjsRequireSpecifiers = (source) => {
7373
+ /** @type {Set<string>} */
7374
+ const specifiers = new Set();
7375
+ /** @type {number[]} */
7376
+ const templateBraceDepths = [];
7377
+ let braceDepth = 0;
7378
+ for (let i = 0; i < source.length; i++) {
7379
+ const code = source.charCodeAt(i);
7380
+ if (code === 96) {
7381
+ const body = skipTemplateBody(source, i + 1);
7382
+ i = body.end;
7383
+ if (body.substitution) {
7384
+ templateBraceDepths.push(braceDepth);
7385
+ braceDepth++;
7386
+ }
7387
+ continue;
7388
+ }
7389
+ if (code === 123) {
7390
+ braceDepth++;
7391
+ continue;
7392
+ }
7393
+ if (code === 125) {
7394
+ braceDepth--;
7395
+ if (
7396
+ templateBraceDepths.length > 0 &&
7397
+ templateBraceDepths[templateBraceDepths.length - 1] === braceDepth
7398
+ ) {
7399
+ templateBraceDepths.pop();
7400
+ const body = skipTemplateBody(source, i + 1);
7401
+ i = body.end;
7402
+ if (body.substitution) {
7403
+ templateBraceDepths.push(braceDepth);
7404
+ braceDepth++;
7405
+ }
7406
+ }
7407
+ continue;
7408
+ }
7409
+ if (code === 34 || code === 39) {
7410
+ for (i++; i < source.length; i++) {
7411
+ const inner = source.charCodeAt(i);
7412
+ if (inner === 92) i++;
7413
+ else if (inner === code) break;
7414
+ }
7415
+ continue;
7416
+ }
7417
+ if (code === 47) {
7418
+ const next = source.charCodeAt(i + 1);
7419
+ if (next === 47) {
7420
+ const end = source.indexOf("\n", i + 2);
7421
+ i = end === -1 ? source.length : end;
7422
+ continue;
7423
+ }
7424
+ if (next === 42) {
7425
+ const end = source.indexOf("*/", i + 2);
7426
+ i = end === -1 ? source.length : end + 1;
7427
+ continue;
7428
+ }
7429
+ if (!startsRegExp(source, i)) continue;
7430
+ let inClass = false;
7431
+ for (i++; i < source.length; i++) {
7432
+ const inner = source.charCodeAt(i);
7433
+ if (inner === 92) i++;
7434
+ else if (inner === 91) inClass = true;
7435
+ else if (inner === 93) inClass = false;
7436
+ else if (inner === 10) break;
7437
+ else if (inner === 47 && !inClass) break;
7438
+ }
7439
+ continue;
7440
+ }
7441
+ // `r` of `require`, as a whole word that is not a member access
7442
+ if (code !== 114 || !source.startsWith("require", i)) continue;
7443
+ if (isIdentifierChar(source.charCodeAt(i + 7))) continue;
7444
+ if (i > 0) {
7445
+ const before = source.charCodeAt(i - 1);
7446
+ if (isIdentifierChar(before) || before === 46) continue;
7447
+ }
7448
+ let j = skipTrivia(source, i + 7);
7449
+ if (source.charCodeAt(j) !== 40) continue;
7450
+ j = skipTrivia(source, j + 1);
7451
+ // a cast wraps the specifier in its own parens: `require(/** … */ ("x"))`
7452
+ let parens = 0;
7453
+ while (source.charCodeAt(j) === 40) {
7454
+ parens++;
7455
+ j = skipTrivia(source, j + 1);
7456
+ }
7457
+ const literal = readStringLiteral(source, j);
7458
+ if (literal === undefined) {
7459
+ i += 6;
7460
+ continue;
7461
+ }
7462
+ j = skipTrivia(source, literal.end);
7463
+ while (parens > 0 && source.charCodeAt(j) === 41) {
7464
+ parens--;
7465
+ j = skipTrivia(source, j + 1);
7466
+ }
7467
+ if (parens !== 0 || source.charCodeAt(j) !== 41) {
7468
+ i += 6;
7469
+ continue;
7470
+ }
7471
+ specifiers.add(literal.value);
7472
+ i = j;
7473
+ }
7474
+ return specifiers;
7475
+ };
7476
+
4376
7477
  module.exports.LEGACY_ASSERT_ATTRIBUTES = LEGACY_ASSERT_ATTRIBUTES;
4377
7478
  module.exports.WebpackParser = WebpackParser;
4378
7479
  module.exports.buildLineStarts = buildLineStarts;
7480
+ module.exports.collectCjsRequireSpecifiers = collectCjsRequireSpecifiers;
4379
7481
  module.exports.hasOctalEscape = hasOctalEscape;
7482
+ module.exports.isIdentifierChar = isIdentifierChar;
4380
7483
  module.exports.positionAt = positionAt;