webpack 5.108.4 → 5.109.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 (186) hide show
  1. package/lib/APIPlugin.js +48 -29
  2. package/lib/CacheFacade.js +8 -0
  3. package/lib/ChunkGroup.js +1 -0
  4. package/lib/CompatibilityPlugin.js +31 -29
  5. package/lib/Compilation.js +49 -4
  6. package/lib/ConstPlugin.js +11 -11
  7. package/lib/ContextModule.js +239 -6
  8. package/lib/ContextModuleFactory.js +139 -14
  9. package/lib/DefinePlugin.js +396 -101
  10. package/lib/Entrypoint.js +1 -1
  11. package/lib/ExportsInfoApiPlugin.js +2 -2
  12. package/lib/ExternalModule.js +107 -5
  13. package/lib/ExternalModuleFactoryPlugin.js +20 -1
  14. package/lib/FileSystemInfo.js +130 -74
  15. package/lib/HotModuleReplacementPlugin.js +18 -15
  16. package/lib/Module.js +3 -0
  17. package/lib/ModuleFilenameHelpers.js +2 -2
  18. package/lib/ModuleTypeConstants.js +8 -1
  19. package/lib/MultiCompiler.js +13 -2
  20. package/lib/MultiWatching.js +1 -0
  21. package/lib/NodeStuffPlugin.js +51 -33
  22. package/lib/NormalModule.js +169 -401
  23. package/lib/NormalModuleFactory.js +76 -2
  24. package/lib/ProgressPlugin.js +211 -119
  25. package/lib/ProvidePlugin.js +2 -2
  26. package/lib/RuntimeGlobals.js +47 -0
  27. package/lib/RuntimePlugin.js +33 -0
  28. package/lib/RuntimeTemplate.js +304 -16
  29. package/lib/UseStrictPlugin.js +1 -1
  30. package/lib/WebpackIsIncludedPlugin.js +1 -1
  31. package/lib/WebpackOptionsApply.js +24 -19
  32. package/lib/asset/AssetGenerator.js +110 -28
  33. package/lib/asset/AssetModulesPlugin.js +92 -2
  34. package/lib/asset/WebManifestGenerator.js +164 -0
  35. package/lib/asset/WebManifestParser.js +130 -0
  36. package/lib/async-modules/AwaitDependenciesInitFragment.js +14 -4
  37. package/lib/async-modules/isGeneratorLowered.js +26 -0
  38. package/lib/cache/PackFileCacheStrategy.js +9 -3
  39. package/lib/config/browserslistTargetHandler.js +40 -0
  40. package/lib/config/defaults.js +219 -29
  41. package/lib/config/normalization.js +26 -0
  42. package/lib/config/target.js +9 -1
  43. package/lib/css/CssGenerator.js +40 -31
  44. package/lib/css/CssLoadingRuntimeModule.js +5 -1
  45. package/lib/css/CssModulesPlugin.js +13 -0
  46. package/lib/css/CssParser.js +594 -263
  47. package/lib/css/syntax.js +875 -462
  48. package/lib/dependencies/AMDDefineDependencyParserPlugin.js +6 -6
  49. package/lib/dependencies/AMDPlugin.js +2 -2
  50. package/lib/dependencies/AMDRequireDependenciesBlockParserPlugin.js +13 -13
  51. package/lib/dependencies/CommonJsDependencyHelpers.js +39 -0
  52. package/lib/dependencies/CommonJsExportsDependency.js +47 -2
  53. package/lib/dependencies/CommonJsExportsParserPlugin.js +10 -10
  54. package/lib/dependencies/CommonJsImportsParserPlugin.js +18 -18
  55. package/lib/dependencies/CommonJsPlugin.js +2 -2
  56. package/lib/dependencies/CommonJsRequireDependency.js +26 -1
  57. package/lib/dependencies/CommonJsSelfReferenceDependency.js +32 -1
  58. package/lib/dependencies/ContextDependencyHelpers.js +3 -3
  59. package/lib/dependencies/CreateRequireParserPlugin.js +6 -6
  60. package/lib/dependencies/CssIcssExportDependency.js +162 -71
  61. package/lib/dependencies/CssUrlDependency.js +35 -4
  62. package/lib/dependencies/ExternalModuleInitFragmentDependency.js +2 -2
  63. package/lib/dependencies/HarmonyCompatibilityDependency.js +32 -14
  64. package/lib/dependencies/HarmonyDetectionParserPlugin.js +43 -13
  65. package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +7 -19
  66. package/lib/dependencies/HarmonyImportDependency.js +12 -1
  67. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +11 -11
  68. package/lib/dependencies/HarmonyModulesPlugin.js +6 -0
  69. package/lib/dependencies/HarmonyTopLevelThisParserPlugin.js +1 -1
  70. package/lib/dependencies/HtmlEntryDependency.js +797 -172
  71. package/lib/dependencies/HtmlSourceDependency.js +26 -0
  72. package/lib/dependencies/ImportDependency.js +43 -1
  73. package/lib/dependencies/ImportMetaContextDependencyParserPlugin.js +31 -18
  74. package/lib/dependencies/ImportMetaContextPlugin.js +38 -7
  75. package/lib/dependencies/ImportMetaGlobDependency.js +81 -0
  76. package/lib/dependencies/ImportMetaGlobDependencyParserPlugin.js +78 -0
  77. package/lib/dependencies/ImportMetaGlobHelpers.js +660 -0
  78. package/lib/dependencies/ImportMetaPlugin.js +532 -180
  79. package/lib/dependencies/ImportMetaResolveDependency.js +90 -0
  80. package/lib/dependencies/ImportParserPlugin.js +80 -17
  81. package/lib/dependencies/ImportPhase.js +10 -8
  82. package/lib/dependencies/LoaderPlugin.js +5 -1
  83. package/lib/dependencies/RequireContextDependencyParserPlugin.js +1 -1
  84. package/lib/dependencies/RequireEnsureDependenciesBlockParserPlugin.js +3 -6
  85. package/lib/dependencies/RequireIncludeDependencyParserPlugin.js +4 -11
  86. package/lib/dependencies/SystemPlugin.js +2 -4
  87. package/lib/dependencies/TopLevelAwaitDependency.js +93 -0
  88. package/lib/dependencies/URLDependency.js +141 -21
  89. package/lib/dependencies/{WorkerPlugin.js → WorkerAndWorkletPlugin.js} +324 -112
  90. package/lib/dependencies/WorkerDependency.js +86 -18
  91. package/lib/dependencies/WorkletDependency.js +278 -0
  92. package/lib/dll/DelegatedModuleFactoryPlugin.js +19 -0
  93. package/lib/errors/EnvironmentNotSupportAsyncWarning.js +1 -1
  94. package/lib/errors/LoaderLoadingError.js +20 -0
  95. package/lib/errors/ModuleParseError.js +39 -22
  96. package/lib/errors/NodeStuffInWebError.js +1 -1
  97. package/lib/esm/ModuleChunkFormatPlugin.js +47 -14
  98. package/lib/esm/ModuleChunkLoadingPlugin.js +8 -1
  99. package/lib/esm/ModuleChunkLoadingRuntimeModule.js +29 -1
  100. package/lib/hmr/lazyCompilationBackend.js +32 -8
  101. package/lib/html/HtmlGenerator.js +887 -15
  102. package/lib/html/HtmlModule.js +2 -2
  103. package/lib/html/HtmlModulesPlugin.js +610 -166
  104. package/lib/html/HtmlParser.js +603 -247
  105. package/lib/html/favicon.svg +1 -0
  106. package/lib/html/syntax.js +1264 -661
  107. package/lib/ids/IdHelpers.js +1 -1
  108. package/lib/index.js +6 -0
  109. package/lib/javascript/ArrayPushCallbackChunkFormatPlugin.js +4 -5
  110. package/lib/javascript/BasicEvaluatedExpression.js +366 -48
  111. package/lib/javascript/JavascriptGenerator.js +197 -49
  112. package/lib/javascript/JavascriptModulesPlugin.js +178 -24
  113. package/lib/javascript/JavascriptParser.js +701 -355
  114. package/lib/javascript/JavascriptParserHelpers.js +3 -6
  115. package/lib/javascript/syntax.js +4380 -0
  116. package/lib/library/ModuleLibraryPlugin.js +38 -1
  117. package/lib/loaders/LoaderRunner.js +714 -0
  118. package/lib/loaders/loadLoader.js +100 -0
  119. package/lib/node/ReadFileCompileAsyncWasmPlugin.js +19 -0
  120. package/lib/node/ReadFileCompileWasmPlugin.js +14 -0
  121. package/lib/optimize/ConcatenatedModule.js +120 -16
  122. package/lib/optimize/FlagIncludedChunksPlugin.js +13 -3
  123. package/lib/optimize/InnerGraphPlugin.js +50 -30
  124. package/lib/optimize/ModuleConcatenationPlugin.js +69 -3
  125. package/lib/optimize/SideEffectsFlagPlugin.js +86 -43
  126. package/lib/prefetch/ChunkPrefetchPreloadPlugin.js +15 -0
  127. package/lib/prefetch/ChunkPreloadTriggerRuntimeModule.js +24 -13
  128. package/lib/prefetch/ResourceHintPlugin.js +663 -0
  129. package/lib/prefetch/ResourceHintRuntimeModule.js +149 -0
  130. package/lib/prefetch/StartupAssetHintRuntimeModule.js +210 -0
  131. package/lib/prefetch/parseResourceHintOptions.js +103 -0
  132. package/lib/rules/RuleSetCompiler.js +114 -0
  133. package/lib/runtime/AsyncModuleGeneratorRuntimeModule.js +56 -0
  134. package/lib/runtime/AsyncModuleRuntimeModule.js +24 -8
  135. package/lib/runtime/AutoPublicPathRuntimeModule.js +7 -1
  136. package/lib/runtime/CommonJsWrapRuntimeModule.js +37 -0
  137. package/lib/runtime/GetWorkletBootstrapRuntimeModule.js +59 -0
  138. package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +204 -8
  139. package/lib/runtime/SetAnonymousDefaultNameRuntimeModule.js +4 -1
  140. package/lib/schemes/HttpUriPlugin.js +40 -1
  141. package/lib/schemes/VirtualUrlPlugin.js +1 -2
  142. package/lib/serialization/FileMiddleware.js +29 -3
  143. package/lib/stats/DefaultStatsFactoryPlugin.js +32 -3
  144. package/lib/stats/DefaultStatsPresetPlugin.js +2 -0
  145. package/lib/stats/DefaultStatsPrinterPlugin.js +12 -2
  146. package/lib/stats/StatsFactory.js +15 -8
  147. package/lib/url/URLParserPlugin.js +66 -13
  148. package/lib/util/AsyncQueue.js +122 -72
  149. package/lib/util/SetHelpers.js +1 -1
  150. package/lib/util/StackedMap.js +122 -48
  151. package/lib/util/binarySearchBounds.js +2 -2
  152. package/lib/util/concatenate.js +40 -14
  153. package/lib/util/createHooksRegistry.js +13 -5
  154. package/lib/util/dataURL.js +2 -2
  155. package/lib/util/deterministicGrouping.js +7 -5
  156. package/lib/util/formatSize.js +6 -2
  157. package/lib/util/globUtils.js +710 -0
  158. package/lib/util/identifier.js +23 -6
  159. package/lib/util/implicitTypeLoaderFallback.js +62 -0
  160. package/lib/util/internalSerializables.js +88 -71
  161. package/lib/util/magicComment.js +121 -25
  162. package/lib/util/numberHash.js +2 -2
  163. package/lib/wasm-async/AsyncWasmCompileRuntimeModule.js +24 -18
  164. package/lib/wasm-async/AsyncWasmLoadingRuntimeModule.js +33 -22
  165. package/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js +79 -16
  166. package/lib/web/FetchCompileAsyncWasmPlugin.js +19 -0
  167. package/lib/web/FetchCompileWasmPlugin.js +14 -0
  168. package/lib/web/JsonpChunkLoadingRuntimeModule.js +2 -0
  169. package/lib/webpack.js +65 -0
  170. package/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js +3 -0
  171. package/module.d.ts +21 -0
  172. package/package.json +13 -12
  173. package/schemas/WebpackOptions.check.js +1 -1
  174. package/schemas/WebpackOptions.json +759 -36
  175. package/schemas/plugins/HtmlParserOptions.check.js +1 -1
  176. package/schemas/plugins/ProgressPlugin.check.js +1 -1
  177. package/schemas/plugins/ProgressPlugin.json +17 -1
  178. package/schemas/plugins/container/ContainerReferencePlugin.check.js +1 -1
  179. package/schemas/plugins/container/ContainerReferencePlugin.json +1 -0
  180. package/schemas/plugins/container/ExternalsType.check.js +1 -1
  181. package/schemas/plugins/container/ModuleFederationPlugin.check.js +1 -1
  182. package/schemas/plugins/container/ModuleFederationPlugin.json +1 -0
  183. package/schemas/plugins/css/CssAutoOrModuleParserOptions.check.js +1 -1
  184. package/schemas/plugins/css/CssModuleParserOptions.check.js +1 -1
  185. package/schemas/plugins/css/CssParserOptions.check.js +1 -1
  186. package/types.d.ts +1213 -96
@@ -5,20 +5,27 @@
5
5
 
6
6
  "use strict";
7
7
 
8
- const vm = require("vm");
9
- const { Parser: AcornParser, tokTypes } = require("acorn");
10
8
  const { HookMap, SyncBailHook } = require("tapable");
11
9
  const NormalModule = require("../NormalModule");
12
10
  const Parser = require("../Parser");
11
+ const WebpackError = require("../WebpackError");
13
12
  const StackedMap = require("../util/StackedMap");
14
- const binarySearchBounds = require("../util/binarySearchBounds");
15
13
  const {
16
14
  CompilerHintNotationRegExp,
17
15
  createMagicCommentContext,
18
- webpackCommentRegExp
16
+ getCommentsInRange,
17
+ parseCommentOptionsInRange
19
18
  } = require("../util/magicComment");
20
19
  const BasicEvaluatedExpression = require("./BasicEvaluatedExpression");
21
-
20
+ const {
21
+ LEGACY_ASSERT_ATTRIBUTES,
22
+ WebpackParser,
23
+ buildLineStarts,
24
+ hasOctalEscape,
25
+ positionAt
26
+ } = require("./syntax");
27
+
28
+ /** @typedef {typeof import("acorn").Parser} AcornParser */
22
29
  /** @typedef {import("acorn").Options} AcornOptions */
23
30
  /** @typedef {import("acorn").ecmaVersion} EcmaVersion */
24
31
  /** @typedef {import("estree").AssignmentExpression} AssignmentExpression */
@@ -32,7 +39,9 @@ const BasicEvaluatedExpression = require("./BasicEvaluatedExpression");
32
39
  /** @typedef {import("estree").SwitchStatement} SwitchStatement */
33
40
  /** @typedef {import("estree").ClassExpression} ClassExpression */
34
41
  /** @typedef {import("estree").SourceLocation} SourceLocation */
35
- /** @typedef {import("estree").Comment & { start: number, end: number, loc: SourceLocation }} Comment */
42
+ /** @typedef {import("../Dependency").DependencyLocation} DependencyLocation */
43
+ /** @typedef {import("../Dependency").SourcePosition} SourcePosition */
44
+ /** @typedef {import("estree").Comment & { start: number, end: number, loc?: SourceLocation | null }} Comment */
36
45
  /** @typedef {import("estree").ConditionalExpression} ConditionalExpression */
37
46
  /** @typedef {import("estree").Declaration} Declaration */
38
47
  /** @typedef {import("estree").PrivateIdentifier} PrivateIdentifier */
@@ -156,77 +165,21 @@ const ALLOWED_MEMBER_TYPES_CALL_EXPRESSION = 0b01;
156
165
  const ALLOWED_MEMBER_TYPES_EXPRESSION = 0b10;
157
166
  const ALLOWED_MEMBER_TYPES_ALL = 0b11;
158
167
 
159
- const LEGACY_ASSERT_ATTRIBUTES = Symbol("assert");
160
-
161
- /** @type {(BaseParser: typeof AcornParser) => typeof AcornParser} */
162
- const importAssertions = (Parser) =>
163
- class extends Parser {
164
- /**
165
- * Parses with clause.
166
- * @this {InstanceType<AcornParser>}
167
- * @returns {ImportAttribute[]} import attributes
168
- */
169
- parseWithClause() {
170
- /** @type {ImportAttribute[]} */
171
- const nodes = [];
172
-
173
- const isAssertLegacy = this.value === "assert";
174
-
175
- if (isAssertLegacy) {
176
- if (!this.eat(tokTypes.name)) {
177
- return nodes;
178
- }
179
- } else if (!this.eat(tokTypes._with)) {
180
- return nodes;
181
- }
182
-
183
- this.expect(tokTypes.braceL);
184
-
185
- /** @type {Record<string, boolean>} */
186
- const attributeKeys = {};
187
- let first = true;
188
-
189
- while (!this.eat(tokTypes.braceR)) {
190
- if (!first) {
191
- this.expect(tokTypes.comma);
192
- if (this.afterTrailingComma(tokTypes.braceR)) {
193
- break;
194
- }
195
- } else {
196
- first = false;
197
- }
198
-
199
- const attr =
200
- /** @type {ImportAttribute} */
201
- this.parseImportAttribute();
202
- const keyName =
203
- attr.key.type === "Identifier" ? attr.key.name : attr.key.value;
204
-
205
- if (Object.prototype.hasOwnProperty.call(attributeKeys, keyName)) {
206
- this.raiseRecoverable(
207
- attr.key.start,
208
- `Duplicate attribute key '${keyName}'`
209
- );
210
- }
211
-
212
- attributeKeys[keyName] = true;
213
- nodes.push(attr);
214
- }
168
+ // Shared `() => false` so the members-optionals fallback in walkCallExpression
169
+ // doesn't allocate a fresh closure per identifier-rooted call.
170
+ const RETURN_FALSE = () => false;
215
171
 
216
- if (isAssertLegacy) {
217
- /** @type {EXPECTED_ANY} */
218
- (nodes)[LEGACY_ASSERT_ATTRIBUTES] = true;
219
- }
220
-
221
- return nodes;
222
- }
223
- };
172
+ // Shared `() => []` for the member-less identifier/this evaluation results,
173
+ // replacing three fresh closures per evaluated free variable.
174
+ const RETURN_EMPTY_ARRAY = () => [];
224
175
 
225
176
  // Syntax: https://developer.mozilla.org/en/SpiderMonkey/Parser_API
226
- let parser = AcornParser.extend(importAssertions);
177
+ let parser = /** @type {AcornParser} */ (WebpackParser);
227
178
 
228
179
  /** @typedef {Record<string, string> & { _isLegacyAssert?: boolean }} ImportAttributes */
229
180
 
181
+ // Conceptually pairs with ./syntax's LEGACY_ASSERT_ATTRIBUTES marker, but is
182
+ // pinned here by its public `JavascriptParser.getImportAttributes` typing.
230
183
  /**
231
184
  * Gets import attributes.
232
185
  * @param {ImportDeclaration | ExportNamedDeclaration | ExportAllDeclaration | ImportExpression} node node with assertions
@@ -362,9 +315,12 @@ class VariableInfo {
362
315
  * @property {boolean=} locations
363
316
  * @property {boolean=} comments
364
317
  * @property {boolean=} ranges
365
- * @property {boolean=} semicolons
366
318
  * @property {boolean=} allowHashBang
367
319
  * @property {boolean=} allowReturnOutsideFunction
320
+ * @property {boolean=} lazyNodes internal: serve `range` lazily and skip acorn's location/range tracking
321
+ * @property {Comment[]=} lazyComments internal: collect comments here without slicing their text eagerly
322
+ * @property {boolean=} importPhases enable parsing of the import phase proposals (import defer / import source)
323
+ * @property {boolean=} moduleFallback internal: for `auto`, let the parser downgrade module->script in place instead of re-parsing
368
324
  */
369
325
 
370
326
  /**
@@ -372,7 +328,6 @@ class VariableInfo {
372
328
  * @typedef {object} ParseResult
373
329
  * @property {Program} ast
374
330
  * @property {Comment[]} comments
375
- * @property {Set<number>} semicolons
376
331
  */
377
332
 
378
333
  /**
@@ -424,10 +379,10 @@ const SCOPE_INFO_TERMINATED_THROW = 2;
424
379
 
425
380
  /**
426
381
  * Defines the destructuring assignment property type used by this module.
382
+ * Carries only the range — consumers derive line/column via `getLocation`.
427
383
  * @typedef {object} DestructuringAssignmentProperty
428
384
  * @property {string} id
429
385
  * @property {Range} range
430
- * @property {SourceLocation} loc
431
386
  * @property {Set<DestructuringAssignmentProperty> | undefined=} pattern
432
387
  * @property {boolean | string} shorthand
433
388
  */
@@ -527,10 +482,16 @@ const defaultParserOptions = {
527
482
  allowHashBang: true
528
483
  };
529
484
 
530
- const EMPTY_COMMENT_OPTIONS = {
531
- options: null,
532
- errors: null
533
- };
485
+ // reused by _parse for every non-custom parse — all fields are reset per parse
486
+ /** @type {ParseOptions} */
487
+ const REUSED_PARSER_OPTIONS = { ...defaultParserOptions };
488
+
489
+ // Read-only snippet ASTs for `evaluate()`, released with the compilation;
490
+ // DefinePlugin evaluates the same strings once per usage per module.
491
+ /** @type {WeakMap<Compilation, Map<string, Program>>} */
492
+ const evaluateAstCaches = new WeakMap();
493
+ // The inner map is strong while the compilation lives, so cap it
494
+ const EVALUATE_AST_CACHE_LIMIT = 4096;
534
495
 
535
496
  const CLASS_NAME = "JavascriptParser";
536
497
 
@@ -538,7 +499,7 @@ class JavascriptParser extends Parser {
538
499
  /**
539
500
  * Creates an instance of JavascriptParser.
540
501
  * @param {"module" | "script" | "auto"=} sourceType default source type
541
- * @param {{ parse?: ParseFunction, typescript?: boolean }=} options parser options
502
+ * @param {{ parse?: ParseFunction, typescript?: boolean, importPhases?: boolean, strictModeViolations?: "error" | "warn" | false }=} options parser options
542
503
  */
543
504
  constructor(sourceType = "auto", options = {}) {
544
505
  super();
@@ -571,9 +532,15 @@ class JavascriptParser extends Parser {
571
532
  ),
572
533
  /** @type {SyncBailHook<[Statement | ModuleDeclaration | MaybeNamedClassDeclaration | MaybeNamedFunctionDeclaration], boolean | void>} */
573
534
  preStatement: new SyncBailHook(["statement"]),
535
+ /** @type {HookMap<SyncBailHook<[Statement | ModuleDeclaration | MaybeNamedClassDeclaration | MaybeNamedFunctionDeclaration], boolean | void>>} */
536
+ preStatementByType: new HookMap(() => new SyncBailHook(["statement"])),
574
537
 
575
538
  /** @type {SyncBailHook<[Statement | ModuleDeclaration | MaybeNamedClassDeclaration | MaybeNamedFunctionDeclaration], boolean | void>} */
576
539
  blockPreStatement: new SyncBailHook(["declaration"]),
540
+ /** @type {HookMap<SyncBailHook<[Statement | ModuleDeclaration | MaybeNamedClassDeclaration | MaybeNamedFunctionDeclaration], boolean | void>>} */
541
+ blockPreStatementByType: new HookMap(
542
+ () => new SyncBailHook(["declaration"])
543
+ ),
577
544
  /** @type {SyncBailHook<[Statement | ModuleDeclaration | MaybeNamedFunctionDeclaration | MaybeNamedClassDeclaration], boolean | void>} */
578
545
  statement: new SyncBailHook(["statement"]),
579
546
  /** @type {SyncBailHook<[IfStatement], boolean | void>} */
@@ -744,7 +711,10 @@ class JavascriptParser extends Parser {
744
711
  this.state = /** @type {EXPECTED_ANY} */ (undefined);
745
712
  /** @type {Comment[] | undefined} */
746
713
  this.comments = undefined;
747
- /** @type {Set<number> | undefined} */
714
+ // ASI overrides keyed by statement-end offset: true forces an ASI
715
+ // position, false forces a real semicolon. Absent offsets are derived
716
+ // from the source text. Allocated lazily by set/unsetAsiPosition.
717
+ /** @type {Map<number, boolean> | undefined} */
748
718
  this.semicolons = undefined;
749
719
  /** @type {StatementPath | undefined} */
750
720
  this.statementPath = undefined;
@@ -754,11 +724,39 @@ class JavascriptParser extends Parser {
754
724
  this.destructuringAssignmentProperties = undefined;
755
725
  /** @type {TagData | undefined} */
756
726
  this.currentTagData = undefined;
727
+ // True while parsing a loose module whose code is emitted as strict ESM.
728
+ this._strictInModuleOutput = false;
729
+ // Source text of the current parse, for offset → line/column mapping.
730
+ /** @type {string | undefined} */
731
+ this._source = undefined;
732
+ // Offset of each line's first character, built on first getLocation use.
733
+ /** @type {number[] | undefined} */
734
+ this._lineStarts = undefined;
735
+ // True while only the parser's own taps sit on evaluate.for("Identifier");
736
+ // recomputed per parse, gates the defined-identifier fast paths.
737
+ this._evalIdentOwnTaps = false;
757
738
  this.magicCommentContext = createMagicCommentContext();
758
739
  // Reused for enterPatterns on every scope entry to avoid per-scope closures.
759
740
  /** @type {OnIdentString} */
760
741
  this._defineVariable = (ident) => this.defineVariable(ident);
761
742
  this._initializeEvaluating();
743
+ // keyed taps are free for every other member chain, and a shadowed
744
+ // `arguments` binding never dispatches the free-variable hooks
745
+ this.hooks.expressionMemberChain
746
+ .for("arguments")
747
+ .tap(CLASS_NAME, (expression, members) => {
748
+ if (this._strictInModuleOutput) {
749
+ this._checkStrictModeArgumentsMember(members[0], expression);
750
+ }
751
+ });
752
+ this.hooks.callMemberChain
753
+ .for("arguments")
754
+ .tap(CLASS_NAME, (expression, members) => {
755
+ // longer chains re-enter the member walk, which reports them
756
+ if (this._strictInModuleOutput && members.length === 1) {
757
+ this._checkStrictModeArgumentsMember(members[0], expression);
758
+ }
759
+ });
762
760
  }
763
761
 
764
762
  _initializeEvaluating() {
@@ -1607,9 +1605,9 @@ class JavascriptParser extends Parser {
1607
1605
  return {
1608
1606
  name: info,
1609
1607
  rootInfo: info,
1610
- getMembers: () => [],
1611
- getMembersOptionals: () => [],
1612
- getMemberRanges: () => []
1608
+ getMembers: RETURN_EMPTY_ARRAY,
1609
+ getMembersOptionals: RETURN_EMPTY_ARRAY,
1610
+ getMemberRanges: RETURN_EMPTY_ARRAY
1613
1611
  };
1614
1612
  }
1615
1613
  });
@@ -1622,9 +1620,9 @@ class JavascriptParser extends Parser {
1622
1620
  return {
1623
1621
  name: info,
1624
1622
  rootInfo: info,
1625
- getMembers: () => [],
1626
- getMembersOptionals: () => [],
1627
- getMemberRanges: () => []
1623
+ getMembers: RETURN_EMPTY_ARRAY,
1624
+ getMembersOptionals: RETURN_EMPTY_ARRAY,
1625
+ getMemberRanges: RETURN_EMPTY_ARRAY
1628
1626
  };
1629
1627
  }
1630
1628
  });
@@ -2073,6 +2071,15 @@ class JavascriptParser extends Parser {
2073
2071
  * @returns {string | VariableInfo | undefined} identifier
2074
2072
  */
2075
2073
  getRenameIdentifier(expr) {
2074
+ if (
2075
+ expr.type === "Identifier" &&
2076
+ this._evalIdentOwnTaps &&
2077
+ expr.name !== "undefined" &&
2078
+ this._isDefinedPlainVariable(expr.name)
2079
+ ) {
2080
+ // the evaluation could only fall through — never an identifier result
2081
+ return;
2082
+ }
2076
2083
  const result = this.evaluateExpression(expr);
2077
2084
  if (result.isIdentifier()) {
2078
2085
  return result.identifier;
@@ -2212,49 +2219,60 @@ class JavascriptParser extends Parser {
2212
2219
  preWalkStatement(statement) {
2213
2220
  /** @type {StatementPath} */
2214
2221
  (this.statementPath).push(statement);
2215
- if (this.hooks.preStatement.call(statement)) {
2222
+ const { preStatement, preStatementByType } = this.hooks;
2223
+ // skip the broadcast entirely when nothing taps it; type-keyed taps
2224
+ // only run for their statement type
2225
+ let bail =
2226
+ preStatement.taps.length !== 0 ? preStatement.call(statement) : undefined;
2227
+ if (!bail) {
2228
+ const typed = preStatementByType.get(statement.type);
2229
+ if (typed !== undefined) bail = typed.call(statement);
2230
+ }
2231
+ if (bail) {
2216
2232
  this.prevStatement =
2217
2233
  /** @type {StatementPath} */
2218
2234
  (this.statementPath).pop();
2219
2235
  return;
2220
2236
  }
2237
+ // cases ordered by measured statement frequency; the commonest statements
2238
+ // (expressions, returns) intentionally match nothing here
2221
2239
  switch (statement.type) {
2222
- case "BlockStatement":
2223
- this.preWalkBlockStatement(statement);
2224
- break;
2225
- case "DoWhileStatement":
2226
- this.preWalkDoWhileStatement(statement);
2227
- break;
2228
- case "ForInStatement":
2229
- this.preWalkForInStatement(statement);
2240
+ case "VariableDeclaration":
2241
+ this.preWalkVariableDeclaration(statement);
2230
2242
  break;
2231
- case "ForOfStatement":
2232
- this.preWalkForOfStatement(statement);
2243
+ case "IfStatement":
2244
+ this.preWalkIfStatement(statement);
2233
2245
  break;
2234
- case "ForStatement":
2235
- this.preWalkForStatement(statement);
2246
+ case "BlockStatement":
2247
+ this.preWalkBlockStatement(statement);
2236
2248
  break;
2237
2249
  case "FunctionDeclaration":
2238
2250
  this.preWalkFunctionDeclaration(statement);
2239
2251
  break;
2240
- case "IfStatement":
2241
- this.preWalkIfStatement(statement);
2242
- break;
2243
- case "LabeledStatement":
2244
- this.preWalkLabeledStatement(statement);
2245
- break;
2246
- case "SwitchStatement":
2247
- this.preWalkSwitchStatement(statement);
2252
+ case "ForStatement":
2253
+ this.preWalkForStatement(statement);
2248
2254
  break;
2249
2255
  case "TryStatement":
2250
2256
  this.preWalkTryStatement(statement);
2251
2257
  break;
2252
- case "VariableDeclaration":
2253
- this.preWalkVariableDeclaration(statement);
2258
+ case "SwitchStatement":
2259
+ this.preWalkSwitchStatement(statement);
2254
2260
  break;
2255
2261
  case "WhileStatement":
2256
2262
  this.preWalkWhileStatement(statement);
2257
2263
  break;
2264
+ case "ForInStatement":
2265
+ this.preWalkForInStatement(statement);
2266
+ break;
2267
+ case "ForOfStatement":
2268
+ this.preWalkForOfStatement(statement);
2269
+ break;
2270
+ case "DoWhileStatement":
2271
+ this.preWalkDoWhileStatement(statement);
2272
+ break;
2273
+ case "LabeledStatement":
2274
+ this.preWalkLabeledStatement(statement);
2275
+ break;
2258
2276
  case "WithStatement":
2259
2277
  this.preWalkWithStatement(statement);
2260
2278
  break;
@@ -2271,27 +2289,37 @@ class JavascriptParser extends Parser {
2271
2289
  blockPreWalkStatement(statement) {
2272
2290
  /** @type {StatementPath} */
2273
2291
  (this.statementPath).push(statement);
2274
- if (this.hooks.blockPreStatement.call(statement)) {
2292
+ const { blockPreStatement, blockPreStatementByType } = this.hooks;
2293
+ let bail =
2294
+ blockPreStatement.taps.length !== 0
2295
+ ? blockPreStatement.call(statement)
2296
+ : undefined;
2297
+ if (!bail) {
2298
+ const typed = blockPreStatementByType.get(statement.type);
2299
+ if (typed !== undefined) bail = typed.call(statement);
2300
+ }
2301
+ if (bail) {
2275
2302
  this.prevStatement =
2276
2303
  /** @type {StatementPath} */
2277
2304
  (this.statementPath).pop();
2278
2305
  return;
2279
2306
  }
2307
+ // cases ordered by measured statement frequency
2280
2308
  switch (statement.type) {
2281
- case "ExportDefaultDeclaration":
2282
- this.blockPreWalkExportDefaultDeclaration(statement);
2283
- break;
2284
- case "ExportNamedDeclaration":
2285
- this.blockPreWalkExportNamedDeclaration(statement);
2286
- break;
2287
2309
  case "VariableDeclaration":
2288
2310
  this.blockPreWalkVariableDeclaration(statement);
2289
2311
  break;
2312
+ case "ExpressionStatement":
2313
+ this.blockPreWalkExpressionStatement(statement);
2314
+ break;
2290
2315
  case "ClassDeclaration":
2291
2316
  this.blockPreWalkClassDeclaration(statement);
2292
2317
  break;
2293
- case "ExpressionStatement":
2294
- this.blockPreWalkExpressionStatement(statement);
2318
+ case "ExportNamedDeclaration":
2319
+ this.blockPreWalkExportNamedDeclaration(statement);
2320
+ break;
2321
+ case "ExportDefaultDeclaration":
2322
+ this.blockPreWalkExportDefaultDeclaration(statement);
2295
2323
  }
2296
2324
  this.prevStatement =
2297
2325
  /** @type {StatementPath} */
@@ -2305,51 +2333,42 @@ class JavascriptParser extends Parser {
2305
2333
  walkStatement(statement) {
2306
2334
  /** @type {StatementPath} */
2307
2335
  (this.statementPath).push(statement);
2308
- if (this.hooks.statement.call(statement) !== undefined) {
2336
+ const { statement: statementHook } = this.hooks;
2337
+ if (
2338
+ statementHook.taps.length !== 0 &&
2339
+ statementHook.call(statement) !== undefined
2340
+ ) {
2309
2341
  this.prevStatement =
2310
2342
  /** @type {StatementPath} */
2311
2343
  (this.statementPath).pop();
2312
2344
  return;
2313
2345
  }
2346
+ // cases ordered by measured statement frequency; V8 compiles this to
2347
+ // sequential compares, so hot types must come first
2314
2348
  switch (statement.type) {
2315
- case "BlockStatement":
2316
- this.walkBlockStatement(statement);
2317
- break;
2318
- case "ClassDeclaration":
2319
- this.walkClassDeclaration(statement);
2320
- break;
2321
- case "DoWhileStatement":
2322
- this.walkDoWhileStatement(statement);
2323
- break;
2324
- case "ExportDefaultDeclaration":
2325
- this.walkExportDefaultDeclaration(statement);
2326
- break;
2327
- case "ExportNamedDeclaration":
2328
- this.walkExportNamedDeclaration(statement);
2329
- break;
2330
2349
  case "ExpressionStatement":
2331
2350
  this.walkExpressionStatement(statement);
2332
2351
  break;
2333
- case "ForInStatement":
2334
- this.walkForInStatement(statement);
2352
+ case "VariableDeclaration":
2353
+ this.walkVariableDeclaration(statement);
2335
2354
  break;
2336
- case "ForOfStatement":
2337
- this.walkForOfStatement(statement);
2355
+ case "ReturnStatement":
2356
+ this.walkReturnStatement(statement);
2338
2357
  break;
2339
- case "ForStatement":
2340
- this.walkForStatement(statement);
2358
+ case "IfStatement":
2359
+ this.walkIfStatement(statement);
2360
+ break;
2361
+ case "BlockStatement":
2362
+ this.walkBlockStatement(statement);
2341
2363
  break;
2342
2364
  case "FunctionDeclaration":
2343
2365
  this.walkFunctionDeclaration(statement);
2344
2366
  break;
2345
- case "IfStatement":
2346
- this.walkIfStatement(statement);
2347
- break;
2348
- case "LabeledStatement":
2349
- this.walkLabeledStatement(statement);
2367
+ case "ForStatement":
2368
+ this.walkForStatement(statement);
2350
2369
  break;
2351
- case "ReturnStatement":
2352
- this.walkReturnStatement(statement);
2370
+ case "TryStatement":
2371
+ this.walkTryStatement(statement);
2353
2372
  break;
2354
2373
  case "SwitchStatement":
2355
2374
  this.walkSwitchStatement(statement);
@@ -2357,15 +2376,30 @@ class JavascriptParser extends Parser {
2357
2376
  case "ThrowStatement":
2358
2377
  this.walkThrowStatement(statement);
2359
2378
  break;
2360
- case "TryStatement":
2361
- this.walkTryStatement(statement);
2362
- break;
2363
- case "VariableDeclaration":
2364
- this.walkVariableDeclaration(statement);
2365
- break;
2366
2379
  case "WhileStatement":
2367
2380
  this.walkWhileStatement(statement);
2368
2381
  break;
2382
+ case "ClassDeclaration":
2383
+ this.walkClassDeclaration(statement);
2384
+ break;
2385
+ case "ForInStatement":
2386
+ this.walkForInStatement(statement);
2387
+ break;
2388
+ case "ForOfStatement":
2389
+ this.walkForOfStatement(statement);
2390
+ break;
2391
+ case "DoWhileStatement":
2392
+ this.walkDoWhileStatement(statement);
2393
+ break;
2394
+ case "ExportNamedDeclaration":
2395
+ this.walkExportNamedDeclaration(statement);
2396
+ break;
2397
+ case "ExportDefaultDeclaration":
2398
+ this.walkExportDefaultDeclaration(statement);
2399
+ break;
2400
+ case "LabeledStatement":
2401
+ this.walkLabeledStatement(statement);
2402
+ break;
2369
2403
  case "WithStatement":
2370
2404
  this.walkWithStatement(statement);
2371
2405
  break;
@@ -2521,6 +2555,12 @@ class JavascriptParser extends Parser {
2521
2555
  * @param {WithStatement} statement with statement
2522
2556
  */
2523
2557
  walkWithStatement(statement) {
2558
+ if (this._strictInModuleOutput) {
2559
+ this._reportStrictModeViolation(
2560
+ "`with` statements are not allowed",
2561
+ statement
2562
+ );
2563
+ }
2524
2564
  this.inBlockScope(() => {
2525
2565
  this.walkExpression(statement.object);
2526
2566
  this.walkNestedStatement(statement.body);
@@ -2809,6 +2849,9 @@ class JavascriptParser extends Parser {
2809
2849
  walkFunctionDeclaration(statement) {
2810
2850
  const wasTopLevel = this.scope.topLevelScope;
2811
2851
  this.scope.topLevelScope = false;
2852
+ if (this._strictInModuleOutput) {
2853
+ this._checkStrictModeParams(statement.params);
2854
+ }
2812
2855
  this.inFunctionScope(true, statement.params, () => {
2813
2856
  for (const param of statement.params) {
2814
2857
  this.walkPattern(param);
@@ -3253,7 +3296,6 @@ class JavascriptParser extends Parser {
3253
3296
  props.add({
3254
3297
  id: key.name,
3255
3298
  range: /** @type {Range} */ (key.range),
3256
- loc: /** @type {SourceLocation} */ (key.loc),
3257
3299
  pattern,
3258
3300
  shorthand: this.scope.inShorthand
3259
3301
  });
@@ -3270,7 +3312,6 @@ class JavascriptParser extends Parser {
3270
3312
  props.add({
3271
3313
  id: str,
3272
3314
  range: /** @type {Range} */ (key.range),
3273
- loc: /** @type {SourceLocation} */ (key.loc),
3274
3315
  pattern,
3275
3316
  shorthand: this.scope.inShorthand
3276
3317
  });
@@ -3307,7 +3348,6 @@ class JavascriptParser extends Parser {
3307
3348
  props.add({
3308
3349
  id: `${i}`,
3309
3350
  range: /** @type {Range} */ (element.range),
3310
- loc: /** @type {SourceLocation} */ (element.loc),
3311
3351
  pattern,
3312
3352
  shorthand: false
3313
3353
  });
@@ -3467,6 +3507,12 @@ class JavascriptParser extends Parser {
3467
3507
  */
3468
3508
  walkPattern(pattern) {
3469
3509
  switch (pattern.type) {
3510
+ // plain identifier bindings are the common case and walk nothing
3511
+ case "Identifier":
3512
+ break;
3513
+ case "ObjectPattern":
3514
+ this.walkObjectPattern(pattern);
3515
+ break;
3470
3516
  case "ArrayPattern":
3471
3517
  this.walkArrayPattern(pattern);
3472
3518
  break;
@@ -3476,9 +3522,6 @@ class JavascriptParser extends Parser {
3476
3522
  case "MemberExpression":
3477
3523
  this.walkMemberExpression(pattern);
3478
3524
  break;
3479
- case "ObjectPattern":
3480
- this.walkObjectPattern(pattern);
3481
- break;
3482
3525
  case "RestElement":
3483
3526
  this.walkRestElement(pattern);
3484
3527
  break;
@@ -3548,78 +3591,86 @@ class JavascriptParser extends Parser {
3548
3591
  * @param {Expression | SpreadElement | PrivateIdentifier | Super} expression expression
3549
3592
  */
3550
3593
  walkExpression(expression) {
3594
+ // cases ordered by measured node frequency (identifiers are ~40% of all
3595
+ // expressions) — V8 compiles this to sequential compares, so hot types
3596
+ // must come first
3551
3597
  switch (expression.type) {
3552
- case "ArrayExpression":
3553
- this.walkArrayExpression(expression);
3598
+ case "Identifier":
3599
+ this.walkIdentifier(expression);
3554
3600
  break;
3555
- case "ArrowFunctionExpression":
3556
- this.walkArrowFunctionExpression(expression);
3601
+ case "MemberExpression":
3602
+ this.walkMemberExpression(expression);
3557
3603
  break;
3558
- case "AssignmentExpression":
3559
- this.walkAssignmentExpression(expression);
3604
+ case "Literal":
3605
+ if (this._strictInModuleOutput) {
3606
+ this._checkStrictModeLiteral(expression);
3607
+ }
3560
3608
  break;
3561
- case "AwaitExpression":
3562
- this.walkAwaitExpression(expression);
3609
+ case "CallExpression":
3610
+ this.walkCallExpression(expression);
3563
3611
  break;
3564
3612
  case "BinaryExpression":
3565
3613
  this.walkBinaryExpression(expression);
3566
3614
  break;
3567
- case "CallExpression":
3568
- this.walkCallExpression(expression);
3569
- break;
3570
- case "ChainExpression":
3571
- this.walkChainExpression(expression);
3615
+ case "AssignmentExpression":
3616
+ this.walkAssignmentExpression(expression);
3572
3617
  break;
3573
- case "ClassExpression":
3574
- this.walkClassExpression(expression);
3618
+ case "UnaryExpression":
3619
+ this.walkUnaryExpression(expression);
3575
3620
  break;
3576
- case "ConditionalExpression":
3577
- this.walkConditionalExpression(expression);
3621
+ case "LogicalExpression":
3622
+ this.walkLogicalExpression(expression);
3578
3623
  break;
3579
- case "FunctionExpression":
3580
- this.walkFunctionExpression(expression);
3624
+ case "ArrowFunctionExpression":
3625
+ this.walkArrowFunctionExpression(expression);
3581
3626
  break;
3582
- case "Identifier":
3583
- this.walkIdentifier(expression);
3627
+ case "ThisExpression":
3628
+ this.walkThisExpression(expression);
3584
3629
  break;
3585
- case "ImportExpression":
3586
- this.walkImportExpression(expression);
3630
+ case "ConditionalExpression":
3631
+ this.walkConditionalExpression(expression);
3587
3632
  break;
3588
- case "LogicalExpression":
3589
- this.walkLogicalExpression(expression);
3633
+ case "ArrayExpression":
3634
+ this.walkArrayExpression(expression);
3590
3635
  break;
3591
- case "MetaProperty":
3592
- this.walkMetaProperty(expression);
3636
+ case "ObjectExpression":
3637
+ this.walkObjectExpression(expression);
3593
3638
  break;
3594
- case "MemberExpression":
3595
- this.walkMemberExpression(expression);
3639
+ case "FunctionExpression":
3640
+ this.walkFunctionExpression(expression);
3596
3641
  break;
3597
3642
  case "NewExpression":
3598
3643
  this.walkNewExpression(expression);
3599
3644
  break;
3600
- case "ObjectExpression":
3601
- this.walkObjectExpression(expression);
3645
+ case "TemplateLiteral":
3646
+ this.walkTemplateLiteral(expression);
3647
+ break;
3648
+ case "SpreadElement":
3649
+ this.walkSpreadElement(expression);
3602
3650
  break;
3603
3651
  case "SequenceExpression":
3604
3652
  this.walkSequenceExpression(expression);
3605
3653
  break;
3606
- case "SpreadElement":
3607
- this.walkSpreadElement(expression);
3654
+ case "UpdateExpression":
3655
+ this.walkUpdateExpression(expression);
3608
3656
  break;
3609
- case "TaggedTemplateExpression":
3610
- this.walkTaggedTemplateExpression(expression);
3657
+ case "AwaitExpression":
3658
+ this.walkAwaitExpression(expression);
3611
3659
  break;
3612
- case "TemplateLiteral":
3613
- this.walkTemplateLiteral(expression);
3660
+ case "ChainExpression":
3661
+ this.walkChainExpression(expression);
3614
3662
  break;
3615
- case "ThisExpression":
3616
- this.walkThisExpression(expression);
3663
+ case "ClassExpression":
3664
+ this.walkClassExpression(expression);
3617
3665
  break;
3618
- case "UnaryExpression":
3619
- this.walkUnaryExpression(expression);
3666
+ case "ImportExpression":
3667
+ this.walkImportExpression(expression);
3620
3668
  break;
3621
- case "UpdateExpression":
3622
- this.walkUpdateExpression(expression);
3669
+ case "MetaProperty":
3670
+ this.walkMetaProperty(expression);
3671
+ break;
3672
+ case "TaggedTemplateExpression":
3673
+ this.walkTaggedTemplateExpression(expression);
3623
3674
  break;
3624
3675
  case "YieldExpression":
3625
3676
  this.walkYieldExpression(expression);
@@ -3627,6 +3678,35 @@ class JavascriptParser extends Parser {
3627
3678
  }
3628
3679
  }
3629
3680
 
3681
+ /**
3682
+ * Reports octal number literals (`0777`) and octal string escapes (`"\47"`),
3683
+ * both SyntaxErrors in strict-mode ESM output.
3684
+ * @param {Literal} expression literal
3685
+ */
3686
+ _checkStrictModeLiteral(expression) {
3687
+ const raw = /** @type {string | undefined} */ (expression.raw);
3688
+ if (raw === undefined) return;
3689
+ if (typeof expression.value === "number") {
3690
+ // Legacy octal (`0777`) or non-octal decimal (`08`) integer literal.
3691
+ if (
3692
+ raw.length > 1 &&
3693
+ raw.charCodeAt(0) === 48 &&
3694
+ raw.charCodeAt(1) >= 48 &&
3695
+ raw.charCodeAt(1) <= 57
3696
+ ) {
3697
+ this._reportStrictModeViolation(
3698
+ "Octal literals are not allowed",
3699
+ expression
3700
+ );
3701
+ }
3702
+ } else if (typeof expression.value === "string" && hasOctalEscape(raw)) {
3703
+ this._reportStrictModeViolation(
3704
+ "Octal escape sequences are not allowed",
3705
+ expression
3706
+ );
3707
+ }
3708
+ }
3709
+
3630
3710
  /**
3631
3711
  * Walk await expression.
3632
3712
  * @param {AwaitExpression} expression await expression
@@ -3710,6 +3790,9 @@ class JavascriptParser extends Parser {
3710
3790
  ? [...expression.params, expression.id]
3711
3791
  : expression.params;
3712
3792
 
3793
+ if (this._strictInModuleOutput) {
3794
+ this._checkStrictModeParams(expression.params);
3795
+ }
3713
3796
  this.inFunctionScope(true, scopeParams, () => {
3714
3797
  for (const param of expression.params) {
3715
3798
  this.walkPattern(param);
@@ -3733,6 +3816,9 @@ class JavascriptParser extends Parser {
3733
3816
  walkArrowFunctionExpression(expression) {
3734
3817
  const wasTopLevel = this.scope.topLevelScope;
3735
3818
  this.scope.topLevelScope = wasTopLevel ? "arrow" : false;
3819
+ if (this._strictInModuleOutput) {
3820
+ this._checkStrictModeParams(expression.params);
3821
+ }
3736
3822
  this.inFunctionScope(false, expression.params, () => {
3737
3823
  for (const param of expression.params) {
3738
3824
  this.walkPattern(param);
@@ -3794,9 +3880,84 @@ class JavascriptParser extends Parser {
3794
3880
  * @param {UpdateExpression} expression the update expression
3795
3881
  */
3796
3882
  walkUpdateExpression(expression) {
3883
+ if (
3884
+ this._strictInModuleOutput &&
3885
+ expression.argument.type === "Identifier"
3886
+ ) {
3887
+ this._checkStrictModeAssignTarget(expression.argument.name, expression);
3888
+ }
3797
3889
  this.walkExpression(expression.argument);
3798
3890
  }
3799
3891
 
3892
+ /**
3893
+ * Reports `arguments.callee` / `arguments.caller` — both throw a TypeError
3894
+ * once the module runs in strict-mode ESM output.
3895
+ * @param {string} name accessed member name
3896
+ * @param {Expression} node node to report the violation at
3897
+ */
3898
+ _checkStrictModeArgumentsMember(name, node) {
3899
+ if ((name === "callee" || name === "caller") && !this.scope.isStrict) {
3900
+ this._reportStrictModeViolation(
3901
+ `Accessing "arguments.${name}" is not allowed`,
3902
+ node
3903
+ );
3904
+ }
3905
+ }
3906
+
3907
+ /**
3908
+ * Reports an assignment or update target that breaks in strict-mode ESM
3909
+ * output: `eval` / `arguments` are a SyntaxError, the read-only globals
3910
+ * (`undefined`, `NaN`, `Infinity`) a TypeError instead of a silent no-op.
3911
+ * @param {string} name target identifier name
3912
+ * @param {AssignmentExpression | UpdateExpression} node node to report the violation at
3913
+ */
3914
+ _checkStrictModeAssignTarget(name, node) {
3915
+ if (name === "eval" || name === "arguments") {
3916
+ this._reportStrictModeViolation(
3917
+ `Assigning to "${name}" is not allowed`,
3918
+ node
3919
+ );
3920
+ } else if (
3921
+ (name === "undefined" || name === "NaN" || name === "Infinity") &&
3922
+ !this.scope.isStrict &&
3923
+ !this.isVariableDefined(name)
3924
+ ) {
3925
+ this._reportStrictModeViolation(
3926
+ `Assigning to the read-only global "${name}" is not allowed`,
3927
+ node
3928
+ );
3929
+ }
3930
+ }
3931
+
3932
+ /**
3933
+ * Reports strict-mode-only errors in a function's parameter list: a duplicate
3934
+ * simple parameter, or a parameter named `eval` / `arguments`.
3935
+ * @param {(import("estree").Pattern)[]} params function parameters
3936
+ */
3937
+ _checkStrictModeParams(params) {
3938
+ /** @type {Set<string> | undefined} */
3939
+ let seen;
3940
+ for (const param of params) {
3941
+ if (param.type !== "Identifier") continue;
3942
+ const name = param.name;
3943
+ if (name === "eval" || name === "arguments") {
3944
+ this._reportStrictModeViolation(
3945
+ `"${name}" is not allowed as a parameter name`,
3946
+ param
3947
+ );
3948
+ }
3949
+ if (seen === undefined) seen = new Set();
3950
+ if (seen.has(name)) {
3951
+ this._reportStrictModeViolation(
3952
+ `Duplicate parameter name "${name}" is not allowed`,
3953
+ param
3954
+ );
3955
+ } else {
3956
+ seen.add(name);
3957
+ }
3958
+ }
3959
+ }
3960
+
3800
3961
  /**
3801
3962
  * Walk unary expression.
3802
3963
  * @param {UnaryExpression} expression the unary expression
@@ -3817,10 +3978,39 @@ class JavascriptParser extends Parser {
3817
3978
  );
3818
3979
  if (result === true) return;
3819
3980
  }
3981
+ } else if (
3982
+ this._strictInModuleOutput &&
3983
+ expression.operator === "delete" &&
3984
+ expression.argument.type === "Identifier"
3985
+ ) {
3986
+ this._reportStrictModeViolation(
3987
+ `Deleting the unqualified identifier "${expression.argument.name}" is not allowed`,
3988
+ expression
3989
+ );
3820
3990
  }
3821
3991
  this.walkExpression(expression.argument);
3822
3992
  }
3823
3993
 
3994
+ /**
3995
+ * Reports a construct that a loose script allows but that breaks once the
3996
+ * module is emitted as strict-mode ESM output. Severity comes from the
3997
+ * `strictModeViolations` parser option (`false` disables the checks upfront).
3998
+ * The location is computed here, on the rare violation path only.
3999
+ * @param {string} reason what is not allowed
4000
+ * @param {Statement | Expression | Identifier} node node to report the violation at
4001
+ */
4002
+ _reportStrictModeViolation(reason, node) {
4003
+ const diagnostic = new WebpackError(
4004
+ `${reason}. The output is an ES module, which runs in strict mode.`
4005
+ );
4006
+ diagnostic.loc = this.getLocation(node);
4007
+ if (this.options.strictModeViolations === "error") {
4008
+ this.state.module.addError(diagnostic);
4009
+ } else {
4010
+ this.state.module.addWarning(diagnostic);
4011
+ }
4012
+ }
4013
+
3824
4014
  /**
3825
4015
  * Walk left right expression.
3826
4016
  * @param {LogicalExpression | BinaryExpression} expression the expression
@@ -3859,6 +4049,9 @@ class JavascriptParser extends Parser {
3859
4049
  */
3860
4050
  walkAssignmentExpression(expression) {
3861
4051
  if (expression.left.type === "Identifier") {
4052
+ if (this._strictInModuleOutput) {
4053
+ this._checkStrictModeAssignTarget(expression.left.name, expression);
4054
+ }
3862
4055
  const renameIdentifier = this.getRenameIdentifier(expression.right);
3863
4056
  if (
3864
4057
  renameIdentifier &&
@@ -3960,38 +4153,6 @@ class JavascriptParser extends Parser {
3960
4153
  * @param {NewExpression} expression new expression
3961
4154
  */
3962
4155
  walkNewExpression(expression) {
3963
- // TODO acorn-import-phases bug: it omits acorn's `!forNew` guard, so
3964
- // `import.defer(...)`/`import.source(...)` are wrongly accepted as `new`
3965
- // operands; drop once fixed. Parenthesized forms stay valid (check `(`).
3966
- if (typeof this.state.source === "string") {
3967
- let base = expression.callee;
3968
- while (
3969
- base.type === "MemberExpression" ||
3970
- base.type === "CallExpression"
3971
- ) {
3972
- base = base.type === "MemberExpression" ? base.object : base.callee;
3973
- }
3974
- if (base.type === "ImportExpression") {
3975
- const newStart = /** @type {Range} */ (expression.range)[0];
3976
- const importStart = /** @type {Range} */ (base.range)[0];
3977
- const between = this.state.source
3978
- .slice(newStart, importStart)
3979
- .replace(/\/\*[\s\S]*?\*\//g, "")
3980
- .replace(/\/\/[^\n]*/g, "");
3981
- if (!between.includes("(")) {
3982
- const err =
3983
- /** @type {SyntaxError & { loc?: { line: number, column: number } }} */
3984
- (new SyntaxError("import call cannot be the target of `new`"));
3985
- if (expression.loc) {
3986
- err.loc = {
3987
- line: expression.loc.start.line,
3988
- column: expression.loc.start.column
3989
- };
3990
- }
3991
- throw err;
3992
- }
3993
- }
3994
- }
3995
4156
  const result = this.callHooksForExpression(
3996
4157
  this.hooks.new,
3997
4158
  expression.callee,
@@ -4233,19 +4394,33 @@ class JavascriptParser extends Parser {
4233
4394
  if (result === true) return;
4234
4395
  }
4235
4396
  }
4397
+ if (
4398
+ expression.callee.type === "Identifier" &&
4399
+ this._evalIdentOwnTaps &&
4400
+ expression.callee.name !== "undefined" &&
4401
+ this._isDefinedPlainVariable(expression.callee.name)
4402
+ ) {
4403
+ // a defined, untagged callee can only evaluate to the fallthrough
4404
+ // result, so no call hooks apply — walk callee and arguments
4405
+ // directly, skipping the hook dispatch and its result object
4406
+ this.walkExpression(expression.callee);
4407
+ if (expression.arguments) this.walkExpressions(expression.arguments);
4408
+ return;
4409
+ }
4236
4410
  const callee = this.evaluateExpression(expression.callee);
4237
4411
  if (callee.isIdentifier()) {
4412
+ const members =
4413
+ /** @type {NonNullable<BasicEvaluatedExpression["getMembers"]>} */
4414
+ (callee.getMembers)();
4238
4415
  const result1 = this.callHooksForInfo(
4239
4416
  this.hooks.callMemberChain,
4240
4417
  /** @type {NonNullable<BasicEvaluatedExpression["rootInfo"]>} */
4241
4418
  (callee.rootInfo),
4242
4419
  expression,
4243
- /** @type {NonNullable<BasicEvaluatedExpression["getMembers"]>} */
4244
- (callee.getMembers)(),
4420
+ members,
4245
4421
  callee.getMembersOptionals
4246
4422
  ? callee.getMembersOptionals()
4247
- : /** @type {NonNullable<BasicEvaluatedExpression["getMembers"]>} */
4248
- (callee.getMembers)().map(() => false),
4423
+ : members.map(RETURN_FALSE),
4249
4424
  callee.getMemberRanges ? callee.getMemberRanges() : []
4250
4425
  );
4251
4426
  if (result1 === true) return;
@@ -4749,22 +4924,14 @@ class JavascriptParser extends Parser {
4749
4924
  */
4750
4925
  enterPattern(pattern, onIdent) {
4751
4926
  if (!pattern) return;
4927
+ // plain identifiers dominate bindings, so match them first
4752
4928
  switch (pattern.type) {
4753
- case "ArrayPattern":
4754
- this.enterArrayPattern(pattern, onIdent);
4755
- break;
4756
- case "AssignmentPattern":
4757
- this.enterAssignmentPattern(pattern, onIdent);
4758
- break;
4759
4929
  case "Identifier":
4760
4930
  this.enterIdentifier(pattern, onIdent);
4761
4931
  break;
4762
4932
  case "ObjectPattern":
4763
4933
  this.enterObjectPattern(pattern, onIdent);
4764
4934
  break;
4765
- case "RestElement":
4766
- this.enterRestElement(pattern, onIdent);
4767
- break;
4768
4935
  case "Property":
4769
4936
  if (pattern.shorthand && pattern.value.type === "Identifier") {
4770
4937
  this.scope.inShorthand = pattern.value.name;
@@ -4774,6 +4941,15 @@ class JavascriptParser extends Parser {
4774
4941
  this.enterPattern(/** @type {Pattern} */ (pattern.value), onIdent);
4775
4942
  }
4776
4943
  break;
4944
+ case "ArrayPattern":
4945
+ this.enterArrayPattern(pattern, onIdent);
4946
+ break;
4947
+ case "AssignmentPattern":
4948
+ this.enterAssignmentPattern(pattern, onIdent);
4949
+ break;
4950
+ case "RestElement":
4951
+ this.enterRestElement(pattern, onIdent);
4952
+ break;
4777
4953
  }
4778
4954
  }
4779
4955
 
@@ -4861,9 +5037,9 @@ class JavascriptParser extends Parser {
4861
5037
  console.warn(err);
4862
5038
  // ignore error
4863
5039
  }
4864
- return new BasicEvaluatedExpression()
4865
- .setRange(/** @type {Range} */ (expression.range))
4866
- .setExpression(expression);
5040
+ // no setRange: the range is served lazily from the expression on first
5041
+ // read, so plain results don't materialize the node's range array at all
5042
+ return new BasicEvaluatedExpression().setExpression(expression);
4867
5043
  }
4868
5044
 
4869
5045
  /**
@@ -5002,30 +5178,21 @@ class JavascriptParser extends Parser {
5002
5178
  let ast;
5003
5179
  /** @type {Comment[]} */
5004
5180
  let comments;
5005
- /** @type {Set<number>} */
5006
- let semicolons;
5007
5181
 
5008
5182
  if (typeof source === "object") {
5009
- semicolons = new Set();
5010
-
5011
5183
  ast = /** @type {Program} */ (source);
5012
5184
  comments = source.comments;
5013
- if (source.semicolons) {
5014
- // Forward semicolon information from the preparsed AST if present
5015
- // This ensures the output is consistent with that of a fresh AST
5016
- for (const pos of source.semicolons) {
5017
- semicolons.add(pos);
5018
- }
5019
- }
5020
5185
  } else {
5021
- ({ ast, comments, semicolons } = JavascriptParser._parse(
5186
+ ({ ast, comments } = JavascriptParser._parse(
5022
5187
  source,
5023
5188
  {
5024
5189
  sourceType: this.sourceType,
5025
- locations: true,
5190
+ // line/column locations are derived from node offsets via
5191
+ // getLocation — parsers never need to track them
5192
+ locations: false,
5026
5193
  ranges: true,
5027
5194
  comments: true,
5028
- semicolons: true
5195
+ importPhases: this.options.importPhases === true
5029
5196
  },
5030
5197
  this.options.parse
5031
5198
  ));
@@ -5037,6 +5204,18 @@ class JavascriptParser extends Parser {
5037
5204
  const oldSemicolons = this.semicolons;
5038
5205
  const oldStatementPath = this.statementPath;
5039
5206
  const oldPrevStatement = this.prevStatement;
5207
+ const oldStrictInModuleOutput = this._strictInModuleOutput;
5208
+ const oldSource = this._source;
5209
+ const oldLineStarts = this._lineStarts;
5210
+ // a preparsed AST carries no text, but the build passes the module
5211
+ // source in the state — offsets still map for it
5212
+ this._source =
5213
+ typeof source === "string"
5214
+ ? source
5215
+ : typeof state.source === "string"
5216
+ ? state.source
5217
+ : undefined;
5218
+ this._lineStarts = undefined;
5040
5219
  this.scope = {
5041
5220
  topLevelScope: true,
5042
5221
  inTry: false,
@@ -5049,12 +5228,32 @@ class JavascriptParser extends Parser {
5049
5228
  };
5050
5229
  this.state = state;
5051
5230
  this.comments = comments;
5052
- this.semicolons = semicolons;
5231
+ this.semicolons = undefined;
5232
+ // evaluating a defined, untagged identifier can only fall through when
5233
+ // every `evaluate.for("Identifier")` tap is the parser's own — plugins
5234
+ // tapping or intercepting it disable the callee/rename fast paths
5235
+ const identifierEvalHook = this.hooks.evaluate.get("Identifier");
5236
+ this._evalIdentOwnTaps =
5237
+ identifierEvalHook !== undefined &&
5238
+ identifierEvalHook.interceptors.length === 0 &&
5239
+ identifierEvalHook.taps.every((tap) => tap.name === CLASS_NAME);
5053
5240
  this.statementPath = [];
5054
5241
  this.prevStatement = undefined;
5242
+ // The output is emitted as strict-mode ESM, but this module was parsed as
5243
+ // a loose script — acorn skipped the strict early errors that will break
5244
+ // the bundle at runtime. Enables the strict-mode diagnostics below.
5245
+ this._strictInModuleOutput =
5246
+ this.options.strictModeViolations !== false &&
5247
+ state.compilation !== undefined &&
5248
+ state.compilation.runtimeTemplate.isModule();
5055
5249
  if (this.hooks.program.call(ast, comments) === undefined) {
5056
5250
  this.destructuringAssignmentProperties = new WeakMap();
5057
5251
  this.detectMode(ast.body);
5252
+ // a strict program (ESM source, `"use strict"`) keeps its behavior in
5253
+ // strict output — drop the flag so the walk pays no per-node checks
5254
+ if (this._strictInModuleOutput && this.scope.isStrict) {
5255
+ this._strictInModuleOutput = false;
5256
+ }
5058
5257
  this.modulePreWalkStatements(ast.body);
5059
5258
  this.prevStatement = undefined;
5060
5259
  this.preWalkStatements(ast.body);
@@ -5071,6 +5270,10 @@ class JavascriptParser extends Parser {
5071
5270
  this.semicolons = oldSemicolons;
5072
5271
  this.statementPath = oldStatementPath;
5073
5272
  this.prevStatement = oldPrevStatement;
5273
+ this._strictInModuleOutput = oldStrictInModuleOutput;
5274
+ // release — these retain the whole source string and its line table
5275
+ this._source = oldSource;
5276
+ this._lineStarts = oldLineStarts;
5074
5277
  return state;
5075
5278
  }
5076
5279
 
@@ -5080,11 +5283,41 @@ class JavascriptParser extends Parser {
5080
5283
  * @returns {BasicEvaluatedExpression} evaluation result
5081
5284
  */
5082
5285
  evaluate(source) {
5083
- const { ast } = JavascriptParser._parse(
5084
- `(${source})`,
5085
- { sourceType: this.sourceType },
5086
- this.options.parse
5087
- );
5286
+ const importPhases = this.options.importPhases === true;
5287
+ const compilation = this.state && this.state.compilation;
5288
+ /** @type {Map<string, Program> | undefined} */
5289
+ let cache;
5290
+ // custom parse functions are not necessarily pure, do not cache for them
5291
+ if (compilation !== undefined && typeof this.options.parse !== "function") {
5292
+ cache = evaluateAstCaches.get(compilation);
5293
+ if (cache === undefined) {
5294
+ cache = new Map();
5295
+ evaluateAstCaches.set(compilation, cache);
5296
+ }
5297
+ }
5298
+ const cacheKey =
5299
+ cache === undefined
5300
+ ? undefined
5301
+ : `${this.sourceType}|${importPhases}|${source}`;
5302
+ let ast =
5303
+ cacheKey === undefined
5304
+ ? undefined
5305
+ : /** @type {Map<string, Program>} */ (cache).get(cacheKey);
5306
+ if (ast === undefined) {
5307
+ ({ ast } = JavascriptParser._parse(
5308
+ `(${source})`,
5309
+ {
5310
+ sourceType: this.sourceType,
5311
+ importPhases
5312
+ },
5313
+ this.options.parse
5314
+ ));
5315
+ if (cacheKey !== undefined) {
5316
+ const map = /** @type {Map<string, Program>} */ (cache);
5317
+ if (map.size >= EVALUATE_AST_CACHE_LIMIT) map.clear();
5318
+ map.set(cacheKey, ast);
5319
+ }
5320
+ }
5088
5321
  if (ast.body.length !== 1 || ast.body[0].type !== "ExpressionStatement") {
5089
5322
  throw new Error("evaluate: Source is not a expression");
5090
5323
  }
@@ -5343,34 +5576,127 @@ class JavascriptParser extends Parser {
5343
5576
  return !evaluated.couldHaveSideEffects();
5344
5577
  }
5345
5578
 
5579
+ /**
5580
+ * Returns the location of a node or comment, computed from its offsets so
5581
+ * ASTs from parsers without location support work too. Falls back to the
5582
+ * node's own `loc` only when no source text is available (preparsed ASTs).
5583
+ * Offsets can only be mapped while parsing — callbacks deferred past the
5584
+ * `parse()` call must take the location upfront.
5585
+ * @param {{ start?: number, end?: number, range?: Range, loc?: SourceLocation | null }} node node or comment
5586
+ * @returns {DependencyLocation} location of the node
5587
+ */
5588
+ getLocation(node) {
5589
+ const source = this._source;
5590
+ if (source !== undefined) {
5591
+ const start =
5592
+ typeof node.start === "number"
5593
+ ? node.start
5594
+ : node.range
5595
+ ? node.range[0]
5596
+ : undefined;
5597
+ if (start !== undefined) {
5598
+ const end =
5599
+ typeof node.end === "number"
5600
+ ? node.end
5601
+ : node.range
5602
+ ? node.range[1]
5603
+ : start;
5604
+ const lineStarts =
5605
+ this._lineStarts || (this._lineStarts = buildLineStarts(source));
5606
+ return {
5607
+ start: positionAt(lineStarts, start),
5608
+ end: positionAt(lineStarts, end)
5609
+ };
5610
+ }
5611
+ }
5612
+ return /** @type {DependencyLocation} */ (node.loc);
5613
+ }
5614
+
5346
5615
  /**
5347
5616
  * Returns comments in the range.
5348
5617
  * @param {Range} range range
5349
5618
  * @returns {Comment[]} comments in the range
5350
5619
  */
5351
5620
  getComments(range) {
5352
- const [rangeStart, rangeEnd] = range;
5353
- /**
5354
- * Returns compared.
5355
- * @param {Comment} comment comment
5356
- * @param {number} needle needle
5357
- * @returns {number} compared
5358
- */
5359
- const compare = (comment, needle) =>
5360
- /** @type {Range} */ (comment.range)[0] - needle;
5361
- const comments = /** @type {Comment[]} */ (this.comments);
5362
- let idx = binarySearchBounds.ge(comments, rangeStart, compare);
5363
- /** @type {Comment[]} */
5364
- const commentsInRange = [];
5365
- while (
5366
- comments[idx] &&
5367
- /** @type {Range} */ (comments[idx].range)[1] <= rangeEnd
5368
- ) {
5369
- commentsInRange.push(comments[idx]);
5370
- idx++;
5621
+ return getCommentsInRange(
5622
+ /** @type {(Comment & { range: [number, number] })[]} */ (this.comments),
5623
+ range
5624
+ );
5625
+ }
5626
+
5627
+ /**
5628
+ * First significant char code at or after `pos`, skipping whitespace and
5629
+ * comments, or -1 at end of source.
5630
+ * @param {string} source source text
5631
+ * @param {number} pos start offset
5632
+ * @returns {number} char code of the next token, or -1
5633
+ */
5634
+ _nextTokenCharCode(source, pos) {
5635
+ const len = source.length;
5636
+ for (let i = pos; i < len; i++) {
5637
+ const c = source.charCodeAt(i);
5638
+ // whitespace and line terminators acorn skips between tokens
5639
+ if (
5640
+ c === 32 ||
5641
+ c === 9 ||
5642
+ c === 10 ||
5643
+ c === 13 ||
5644
+ c === 11 ||
5645
+ c === 12 ||
5646
+ c === 0xa0 ||
5647
+ c === 0xfeff ||
5648
+ c === 0x2028 ||
5649
+ c === 0x2029
5650
+ ) {
5651
+ continue;
5652
+ }
5653
+ if (c === 47 /* / */) {
5654
+ const n = source.charCodeAt(i + 1);
5655
+ if (n === 47 /* / */) {
5656
+ i += 2;
5657
+ while (i < len) {
5658
+ const cc = source.charCodeAt(i);
5659
+ if (cc === 10 || cc === 13 || cc === 0x2028 || cc === 0x2029) break;
5660
+ i++;
5661
+ }
5662
+ continue;
5663
+ }
5664
+ if (n === 42 /* * */) {
5665
+ i += 2;
5666
+ while (
5667
+ i < len &&
5668
+ !(source.charCodeAt(i) === 42 && source.charCodeAt(i + 1) === 47)
5669
+ ) {
5670
+ i++;
5671
+ }
5672
+ i++;
5673
+ continue;
5674
+ }
5675
+ }
5676
+ return c;
5371
5677
  }
5678
+ return -1;
5679
+ }
5372
5680
 
5373
- return commentsInRange;
5681
+ /**
5682
+ * Whether a statement ending at `pos` relies on ASI (no real separator).
5683
+ * Derived from the source text unless overridden by set/unsetAsiPosition.
5684
+ * A statement terminated by a real `;` or continued by a `,` (a sequence
5685
+ * element) is not an ASI position; anything else (newline, `}`, eof) is.
5686
+ * @param {number} pos statement end offset
5687
+ * @returns {boolean} true when ASI inserts a semicolon at this position
5688
+ */
5689
+ _isAsiPosition(pos) {
5690
+ if (this.semicolons !== undefined) {
5691
+ const override = this.semicolons.get(pos);
5692
+ if (override !== undefined) return override;
5693
+ }
5694
+ const source = this._source;
5695
+ if (source === undefined) return true;
5696
+ // a real semicolon is the statement's last char (exclusive end at pos)
5697
+ if (source.charCodeAt(pos - 1) === 59 /* ; */) return false;
5698
+ const next = this._nextTokenCharCode(source, pos);
5699
+ return next !== 44 /* , */ && next !== 59; /* ; */
5374
5700
  }
5375
5701
 
5376
5702
  /**
@@ -5390,17 +5716,14 @@ class JavascriptParser extends Parser {
5390
5716
 
5391
5717
  return (
5392
5718
  // Either asking directly for the end position of the current statement
5393
- (range[1] === pos &&
5394
- /** @type {Set<number>} */ (this.semicolons).has(pos)) ||
5719
+ (range[1] === pos && this._isAsiPosition(pos)) ||
5395
5720
  // Or asking for the start position of the current statement,
5396
5721
  // here we have to check multiple things
5397
5722
  (range[0] === pos &&
5398
5723
  // is there a previous statement which might be relevant?
5399
5724
  this.prevStatement !== undefined &&
5400
5725
  // is the end position of the previous statement an ASI position?
5401
- /** @type {Set<number>} */ (this.semicolons).has(
5402
- /** @type {Range} */ (this.prevStatement.range)[1]
5403
- ))
5726
+ this._isAsiPosition(/** @type {Range} */ (this.prevStatement.range)[1]))
5404
5727
  );
5405
5728
  }
5406
5729
 
@@ -5410,7 +5733,7 @@ class JavascriptParser extends Parser {
5410
5733
  * @returns {void}
5411
5734
  */
5412
5735
  setAsiPosition(pos) {
5413
- /** @type {Set<number>} */ (this.semicolons).add(pos);
5736
+ (this.semicolons || (this.semicolons = new Map())).set(pos, true);
5414
5737
  }
5415
5738
 
5416
5739
  /**
@@ -5419,7 +5742,7 @@ class JavascriptParser extends Parser {
5419
5742
  * @returns {void}
5420
5743
  */
5421
5744
  unsetAsiPosition(pos) {
5422
- /** @type {Set<number>} */ (this.semicolons).delete(pos);
5745
+ (this.semicolons || (this.semicolons = new Map())).set(pos, false);
5423
5746
  }
5424
5747
 
5425
5748
  /**
@@ -5534,6 +5857,22 @@ class JavascriptParser extends Parser {
5534
5857
  return true;
5535
5858
  }
5536
5859
 
5860
+ /**
5861
+ * Whether evaluating an identifier of this name can only fall through:
5862
+ * the exact negative of the identifier evaluator's `getInfo` — defined in
5863
+ * some scope and neither free nor tagged nor carrying tag info.
5864
+ * @param {string} name variable name
5865
+ * @returns {boolean} true when the identifier evaluates to nothing
5866
+ */
5867
+ _isDefinedPlainVariable(name) {
5868
+ const info = this.scope.definitions.get(name);
5869
+ if (info === undefined) return false;
5870
+ if (info instanceof VariableInfo) {
5871
+ return !(info.isFree() || info.isTagged() || info.tagInfo !== undefined);
5872
+ }
5873
+ return true;
5874
+ }
5875
+
5537
5876
  /**
5538
5877
  * Gets variable info.
5539
5878
  * @param {string} name variable name
@@ -5593,42 +5932,13 @@ class JavascriptParser extends Parser {
5593
5932
  * @returns {{ options: Record<string, EXPECTED_ANY> | null, errors: (Error & { comment: Comment })[] | null }} result
5594
5933
  */
5595
5934
  parseCommentOptions(range) {
5596
- const comments = this.getComments(range);
5597
- if (comments.length === 0) {
5598
- return EMPTY_COMMENT_OPTIONS;
5599
- }
5600
- /** @type {Record<string, EXPECTED_ANY>} */
5601
- const options = {};
5602
- /** @type {(Error & { comment: Comment })[]} */
5603
- const errors = [];
5604
- for (const comment of comments) {
5605
- const { value } = comment;
5606
- if (value && webpackCommentRegExp.test(value)) {
5607
- // try compile only if webpack options comment is present
5608
- try {
5609
- for (let [key, val] of Object.entries(
5610
- vm.runInContext(
5611
- `(function(){return {${value}};})()`,
5612
- this.magicCommentContext
5613
- )
5614
- )) {
5615
- if (typeof val === "object" && val !== null) {
5616
- val =
5617
- val.constructor.name === "RegExp"
5618
- ? new RegExp(val)
5619
- : JSON.parse(JSON.stringify(val));
5620
- }
5621
- options[key] = val;
5622
- }
5623
- } catch (err) {
5624
- const newErr = new Error(String(/** @type {Error} */ (err).message));
5625
- newErr.stack = String(/** @type {Error} */ (err).stack);
5626
- Object.assign(newErr, { comment });
5627
- errors.push(/** @type {(Error & { comment: Comment })} */ (newErr));
5628
- }
5629
- }
5630
- }
5631
- return { options, errors };
5935
+ return parseCommentOptionsInRange(
5936
+ /** @type {(Comment & { range: [number, number], value: string })[]} */ (
5937
+ this.comments
5938
+ ),
5939
+ range,
5940
+ this.magicCommentContext
5941
+ );
5632
5942
  }
5633
5943
 
5634
5944
  /**
@@ -5862,12 +6172,30 @@ class JavascriptParser extends Parser {
5862
6172
  static _parse(code, options, customParse) {
5863
6173
  const type = options ? options.sourceType : "module";
5864
6174
  /** @type {ParseOptions} */
5865
- const parserOptions = {
5866
- ...defaultParserOptions,
5867
- allowReturnOutsideFunction: type === "script",
5868
- ...options,
5869
- sourceType: type === "auto" ? "module" : type
5870
- };
6175
+ let parserOptions;
6176
+ if (typeof customParse === "function") {
6177
+ // a user parse function may retain the options object — keep it fresh
6178
+ parserOptions = {
6179
+ ...defaultParserOptions,
6180
+ allowReturnOutsideFunction: type === "script",
6181
+ ...options,
6182
+ sourceType: type === "auto" ? "module" : type
6183
+ };
6184
+ } else {
6185
+ // reuse one options object across parses (parsing is synchronous) to
6186
+ // skip re-materializing it per module; acorn copies it via getOptions.
6187
+ // Every non-default key a caller may pass must be reset here.
6188
+ parserOptions = REUSED_PARSER_OPTIONS;
6189
+ parserOptions.importPhases = false;
6190
+ Object.assign(parserOptions, defaultParserOptions);
6191
+ parserOptions.allowReturnOutsideFunction = type === "script";
6192
+ if (options) Object.assign(parserOptions, options);
6193
+ parserOptions.sourceType = type === "auto" ? "module" : type;
6194
+ // let WebpackParser downgrade module->script in place on script-only
6195
+ // syntax so `auto` avoids a second full parse in the common case
6196
+ parserOptions.moduleFallback = type === "auto";
6197
+ }
6198
+ const wantRanges = parserOptions.ranges === true;
5871
6199
  /**
5872
6200
  * Returns parse result.
5873
6201
  * @param {string} code source code
@@ -5882,36 +6210,46 @@ class JavascriptParser extends Parser {
5882
6210
  /** @type {Comment[]} */
5883
6211
  const comments = [];
5884
6212
 
5885
- if (options.comments) {
5886
- /** @type {AcornOptions} */
5887
- (options).onComment = comments;
6213
+ // Whenever ranges are wanted (the main parse path and the concatenation
6214
+ // re-parse), have the parser plugin serve `range` on demand instead of
6215
+ // eagerly. `wantRanges` is hoisted since the auto-fallback re-enters
6216
+ // with the same (mutated) options object.
6217
+ options.lazyNodes = wantRanges;
6218
+ if (wantRanges) {
6219
+ // the lazy plugin replaces acorn's native tracking — disabling it
6220
+ // here lets WebpackParser skip its defensive options copy
6221
+ options.locations = false;
6222
+ options.ranges = false;
5888
6223
  }
5889
6224
 
5890
- /** @type {Set<number>} */
5891
- const semicolons = new Set();
5892
-
5893
- if (options.semicolons) {
5894
- /** @type {AcornOptions} */
5895
- (options).onInsertedSemicolon = (pos) => semicolons.add(pos);
6225
+ if (options.comments) {
6226
+ if (wantRanges) {
6227
+ // the parser plugin collects the comments itself, deferring the
6228
+ // text slice until some hook actually reads `value`
6229
+ options.lazyComments = comments;
6230
+ } else {
6231
+ /** @type {AcornOptions} */
6232
+ (options).onComment =
6233
+ /** @type {import("acorn").Comment[]} */
6234
+ (/** @type {unknown} */ (comments));
6235
+ }
5896
6236
  }
5897
6237
 
5898
6238
  const ast =
5899
6239
  /** @type {Program} */
5900
6240
  (parser.parse(code, /** @type {AcornOptions} */ (options)));
5901
6241
 
5902
- return { ast, comments, semicolons };
6242
+ return { ast, comments };
5903
6243
  };
5904
6244
 
5905
6245
  /** @type {Program | undefined} */
5906
6246
  let ast;
5907
6247
  /** @type {Comment[] | undefined} */
5908
6248
  let comments;
5909
- /** @type {Set<number> | undefined} */
5910
- let semicolons;
5911
6249
  let error;
5912
6250
  let threw = false;
5913
6251
  try {
5914
- ({ ast, comments, semicolons } = internalParse(code, parserOptions));
6252
+ ({ ast, comments } = internalParse(code, parserOptions));
5915
6253
  } catch (err) {
5916
6254
  error = err;
5917
6255
  threw = true;
@@ -5920,9 +6258,11 @@ class JavascriptParser extends Parser {
5920
6258
  if (threw && type === "auto") {
5921
6259
  parserOptions.sourceType = "script";
5922
6260
  parserOptions.allowReturnOutsideFunction = true;
6261
+ // the retry is already a full script parse — no in-place downgrade
6262
+ parserOptions.moduleFallback = false;
5923
6263
 
5924
6264
  try {
5925
- ({ ast, comments, semicolons } = internalParse(code, parserOptions));
6265
+ ({ ast, comments } = internalParse(code, parserOptions));
5926
6266
  threw = false;
5927
6267
  } catch (_err) {
5928
6268
  // we use the error from first parse try
@@ -5930,16 +6270,22 @@ class JavascriptParser extends Parser {
5930
6270
  }
5931
6271
  }
5932
6272
 
6273
+ // release per-parse state retained on the (possibly reused) options
6274
+ // object — the comments array retains source text slices
6275
+ parserOptions.lazyComments = undefined;
6276
+ /** @type {AcornOptions} */
6277
+ (parserOptions).onComment = undefined;
6278
+
5933
6279
  if (threw) {
5934
6280
  throw error;
5935
6281
  }
5936
6282
 
5937
- return /** @type {ParseResult} */ ({ ast, comments, semicolons });
6283
+ return /** @type {ParseResult} */ ({ ast, comments });
5938
6284
  }
5939
6285
 
5940
6286
  /**
5941
6287
  * Returns parser.
5942
- * @param {((BaseParser: typeof AcornParser) => typeof AcornParser)[]} plugins parser plugin
6288
+ * @param {((BaseParser: AcornParser) => AcornParser)[]} plugins parser plugin
5943
6289
  * @returns {typeof JavascriptParser} parser
5944
6290
  */
5945
6291
  static extend(...plugins) {