webpack 5.108.3 → 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 (191) 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 +50 -8
  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/FlagDependencyExportsPlugin.js +55 -8
  16. package/lib/HotModuleReplacementPlugin.js +18 -15
  17. package/lib/LazyBarrel.js +30 -3
  18. package/lib/Module.js +3 -0
  19. package/lib/ModuleFilenameHelpers.js +2 -2
  20. package/lib/ModuleTypeConstants.js +8 -1
  21. package/lib/MultiCompiler.js +13 -2
  22. package/lib/MultiWatching.js +1 -0
  23. package/lib/NodeStuffPlugin.js +51 -33
  24. package/lib/NormalModule.js +169 -405
  25. package/lib/NormalModuleFactory.js +76 -2
  26. package/lib/ProgressPlugin.js +211 -119
  27. package/lib/ProvidePlugin.js +2 -2
  28. package/lib/RuntimeGlobals.js +47 -0
  29. package/lib/RuntimePlugin.js +33 -0
  30. package/lib/RuntimeTemplate.js +304 -16
  31. package/lib/UseStrictPlugin.js +1 -1
  32. package/lib/WebpackIsIncludedPlugin.js +1 -1
  33. package/lib/WebpackOptionsApply.js +24 -19
  34. package/lib/asset/AssetGenerator.js +110 -28
  35. package/lib/asset/AssetModulesPlugin.js +92 -2
  36. package/lib/asset/WebManifestGenerator.js +164 -0
  37. package/lib/asset/WebManifestParser.js +130 -0
  38. package/lib/async-modules/AwaitDependenciesInitFragment.js +14 -4
  39. package/lib/async-modules/isGeneratorLowered.js +26 -0
  40. package/lib/cache/PackFileCacheStrategy.js +9 -3
  41. package/lib/config/browserslistTargetHandler.js +40 -0
  42. package/lib/config/defaults.js +222 -32
  43. package/lib/config/normalization.js +26 -0
  44. package/lib/config/target.js +9 -1
  45. package/lib/css/CssGenerator.js +73 -73
  46. package/lib/css/CssLoadingRuntimeModule.js +5 -1
  47. package/lib/css/CssModulesPlugin.js +92 -53
  48. package/lib/css/CssParser.js +843 -302
  49. package/lib/css/syntax.js +1219 -483
  50. package/lib/dependencies/AMDDefineDependencyParserPlugin.js +6 -6
  51. package/lib/dependencies/AMDPlugin.js +2 -2
  52. package/lib/dependencies/AMDRequireDependenciesBlockParserPlugin.js +13 -13
  53. package/lib/dependencies/CommonJsDependencyHelpers.js +39 -0
  54. package/lib/dependencies/CommonJsExportsDependency.js +47 -2
  55. package/lib/dependencies/CommonJsExportsParserPlugin.js +10 -10
  56. package/lib/dependencies/CommonJsImportsParserPlugin.js +18 -18
  57. package/lib/dependencies/CommonJsPlugin.js +2 -2
  58. package/lib/dependencies/CommonJsRequireDependency.js +26 -1
  59. package/lib/dependencies/CommonJsSelfReferenceDependency.js +32 -1
  60. package/lib/dependencies/ContextDependencyHelpers.js +3 -3
  61. package/lib/dependencies/CreateRequireParserPlugin.js +6 -6
  62. package/lib/dependencies/CssIcssExportDependency.js +190 -87
  63. package/lib/dependencies/CssUrlDependency.js +35 -4
  64. package/lib/dependencies/ExternalModuleInitFragmentDependency.js +2 -2
  65. package/lib/dependencies/HarmonyCompatibilityDependency.js +32 -14
  66. package/lib/dependencies/HarmonyDetectionParserPlugin.js +43 -13
  67. package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +7 -19
  68. package/lib/dependencies/HarmonyImportDependency.js +12 -1
  69. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +34 -107
  70. package/lib/dependencies/HarmonyImportGuard.js +192 -20
  71. package/lib/dependencies/HarmonyModulesPlugin.js +6 -0
  72. package/lib/dependencies/HarmonyTopLevelThisParserPlugin.js +1 -1
  73. package/lib/dependencies/HtmlEntryDependency.js +797 -172
  74. package/lib/dependencies/HtmlSourceDependency.js +26 -0
  75. package/lib/dependencies/ImportDependency.js +43 -1
  76. package/lib/dependencies/ImportMetaContextDependencyParserPlugin.js +31 -18
  77. package/lib/dependencies/ImportMetaContextPlugin.js +38 -7
  78. package/lib/dependencies/ImportMetaGlobDependency.js +81 -0
  79. package/lib/dependencies/ImportMetaGlobDependencyParserPlugin.js +78 -0
  80. package/lib/dependencies/ImportMetaGlobHelpers.js +660 -0
  81. package/lib/dependencies/ImportMetaPlugin.js +532 -180
  82. package/lib/dependencies/ImportMetaResolveDependency.js +90 -0
  83. package/lib/dependencies/ImportParserPlugin.js +87 -26
  84. package/lib/dependencies/ImportPhase.js +10 -8
  85. package/lib/dependencies/LoaderPlugin.js +5 -1
  86. package/lib/dependencies/RequireContextDependencyParserPlugin.js +1 -1
  87. package/lib/dependencies/RequireEnsureDependenciesBlockParserPlugin.js +3 -6
  88. package/lib/dependencies/RequireIncludeDependencyParserPlugin.js +4 -11
  89. package/lib/dependencies/SystemPlugin.js +2 -4
  90. package/lib/dependencies/TopLevelAwaitDependency.js +93 -0
  91. package/lib/dependencies/URLDependency.js +141 -21
  92. package/lib/dependencies/{WorkerPlugin.js → WorkerAndWorkletPlugin.js} +324 -112
  93. package/lib/dependencies/WorkerDependency.js +86 -18
  94. package/lib/dependencies/WorkletDependency.js +278 -0
  95. package/lib/dll/DelegatedModuleFactoryPlugin.js +19 -0
  96. package/lib/errors/EnvironmentNotSupportAsyncWarning.js +1 -1
  97. package/lib/errors/LoaderLoadingError.js +20 -0
  98. package/lib/errors/ModuleParseError.js +39 -22
  99. package/lib/errors/NodeStuffInWebError.js +1 -1
  100. package/lib/esm/ModuleChunkFormatPlugin.js +47 -14
  101. package/lib/esm/ModuleChunkLoadingPlugin.js +8 -1
  102. package/lib/esm/ModuleChunkLoadingRuntimeModule.js +29 -1
  103. package/lib/hmr/lazyCompilationBackend.js +32 -8
  104. package/lib/html/HtmlGenerator.js +940 -91
  105. package/lib/html/HtmlModule.js +3 -2
  106. package/lib/html/HtmlModulesPlugin.js +625 -170
  107. package/lib/html/HtmlParser.js +812 -340
  108. package/lib/html/favicon.svg +1 -0
  109. package/lib/html/syntax.js +2116 -834
  110. package/lib/ids/IdHelpers.js +1 -1
  111. package/lib/index.js +6 -0
  112. package/lib/javascript/ArrayPushCallbackChunkFormatPlugin.js +4 -5
  113. package/lib/javascript/BasicEvaluatedExpression.js +366 -48
  114. package/lib/javascript/JavascriptGenerator.js +197 -49
  115. package/lib/javascript/JavascriptModulesPlugin.js +178 -24
  116. package/lib/javascript/JavascriptParser.js +711 -355
  117. package/lib/javascript/JavascriptParserHelpers.js +3 -6
  118. package/lib/javascript/syntax.js +4380 -0
  119. package/lib/library/ModuleLibraryPlugin.js +38 -1
  120. package/lib/loaders/LoaderRunner.js +714 -0
  121. package/lib/loaders/loadLoader.js +100 -0
  122. package/lib/node/ReadFileCompileAsyncWasmPlugin.js +19 -0
  123. package/lib/node/ReadFileCompileWasmPlugin.js +14 -0
  124. package/lib/optimize/ConcatenatedModule.js +120 -16
  125. package/lib/optimize/FlagIncludedChunksPlugin.js +13 -3
  126. package/lib/optimize/InnerGraphPlugin.js +50 -30
  127. package/lib/optimize/ModuleConcatenationPlugin.js +69 -3
  128. package/lib/optimize/SideEffectsFlagPlugin.js +86 -43
  129. package/lib/prefetch/ChunkPrefetchPreloadPlugin.js +15 -0
  130. package/lib/prefetch/ChunkPreloadTriggerRuntimeModule.js +24 -13
  131. package/lib/prefetch/ResourceHintPlugin.js +663 -0
  132. package/lib/prefetch/ResourceHintRuntimeModule.js +149 -0
  133. package/lib/prefetch/StartupAssetHintRuntimeModule.js +210 -0
  134. package/lib/prefetch/parseResourceHintOptions.js +103 -0
  135. package/lib/rules/RuleSetCompiler.js +114 -0
  136. package/lib/runtime/AsyncModuleGeneratorRuntimeModule.js +56 -0
  137. package/lib/runtime/AsyncModuleRuntimeModule.js +24 -8
  138. package/lib/runtime/AutoPublicPathRuntimeModule.js +7 -1
  139. package/lib/runtime/CommonJsWrapRuntimeModule.js +37 -0
  140. package/lib/runtime/GetWorkletBootstrapRuntimeModule.js +59 -0
  141. package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +204 -8
  142. package/lib/runtime/SetAnonymousDefaultNameRuntimeModule.js +4 -1
  143. package/lib/schemes/HttpUriPlugin.js +40 -1
  144. package/lib/schemes/VirtualUrlPlugin.js +1 -2
  145. package/lib/serialization/FileMiddleware.js +29 -3
  146. package/lib/stats/DefaultStatsFactoryPlugin.js +32 -3
  147. package/lib/stats/DefaultStatsPresetPlugin.js +2 -0
  148. package/lib/stats/DefaultStatsPrinterPlugin.js +12 -2
  149. package/lib/stats/StatsFactory.js +15 -8
  150. package/lib/url/URLParserPlugin.js +98 -19
  151. package/lib/util/AsyncQueue.js +122 -72
  152. package/lib/util/LocConverter.js +9 -8
  153. package/lib/util/SetHelpers.js +1 -1
  154. package/lib/util/SourceProcessor.js +41 -28
  155. package/lib/util/StackedMap.js +122 -48
  156. package/lib/util/binarySearchBounds.js +2 -2
  157. package/lib/util/concatenate.js +40 -14
  158. package/lib/util/createHooksRegistry.js +13 -5
  159. package/lib/util/dataURL.js +2 -2
  160. package/lib/util/deterministicGrouping.js +7 -5
  161. package/lib/util/formatSize.js +6 -2
  162. package/lib/util/globUtils.js +710 -0
  163. package/lib/util/identifier.js +23 -6
  164. package/lib/util/implicitTypeLoaderFallback.js +62 -0
  165. package/lib/util/internalSerializables.js +88 -71
  166. package/lib/util/magicComment.js +121 -25
  167. package/lib/util/numberHash.js +2 -2
  168. package/lib/wasm-async/AsyncWasmCompileRuntimeModule.js +24 -18
  169. package/lib/wasm-async/AsyncWasmLoadingRuntimeModule.js +33 -22
  170. package/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js +79 -16
  171. package/lib/web/FetchCompileAsyncWasmPlugin.js +19 -0
  172. package/lib/web/FetchCompileWasmPlugin.js +14 -0
  173. package/lib/web/JsonpChunkLoadingRuntimeModule.js +2 -0
  174. package/lib/webpack.js +65 -0
  175. package/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js +3 -0
  176. package/module.d.ts +21 -0
  177. package/package.json +14 -13
  178. package/schemas/WebpackOptions.check.js +1 -1
  179. package/schemas/WebpackOptions.json +759 -36
  180. package/schemas/plugins/HtmlParserOptions.check.js +1 -1
  181. package/schemas/plugins/ProgressPlugin.check.js +1 -1
  182. package/schemas/plugins/ProgressPlugin.json +17 -1
  183. package/schemas/plugins/container/ContainerReferencePlugin.check.js +1 -1
  184. package/schemas/plugins/container/ContainerReferencePlugin.json +1 -0
  185. package/schemas/plugins/container/ExternalsType.check.js +1 -1
  186. package/schemas/plugins/container/ModuleFederationPlugin.check.js +1 -1
  187. package/schemas/plugins/container/ModuleFederationPlugin.json +1 -0
  188. package/schemas/plugins/css/CssAutoOrModuleParserOptions.check.js +1 -1
  189. package/schemas/plugins/css/CssModuleParserOptions.check.js +1 -1
  190. package/schemas/plugins/css/CssParserOptions.check.js +1 -1
  191. package/types.d.ts +1300 -325
@@ -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() {
@@ -1383,6 +1381,16 @@ class JavascriptParser extends Parser {
1383
1381
  return handleConstOperation((l, r) => l <= r);
1384
1382
  } else if (expr.operator === ">=") {
1385
1383
  return handleConstOperation((l, r) => l >= r);
1384
+ } else if (expr.operator === "in") {
1385
+ // `x in y` always evaluates to a boolean, so it is never nullish
1386
+ const left = this.evaluateExpression(expr.left);
1387
+ const right = this.evaluateExpression(expr.right);
1388
+ return new BasicEvaluatedExpression()
1389
+ .setNullish(false)
1390
+ .setSideEffects(
1391
+ left.couldHaveSideEffects() || right.couldHaveSideEffects()
1392
+ )
1393
+ .setRange(/** @type {Range} */ (expr.range));
1386
1394
  }
1387
1395
  });
1388
1396
  this.hooks.evaluate.for("UnaryExpression").tap(CLASS_NAME, (_expr) => {
@@ -1597,9 +1605,9 @@ class JavascriptParser extends Parser {
1597
1605
  return {
1598
1606
  name: info,
1599
1607
  rootInfo: info,
1600
- getMembers: () => [],
1601
- getMembersOptionals: () => [],
1602
- getMemberRanges: () => []
1608
+ getMembers: RETURN_EMPTY_ARRAY,
1609
+ getMembersOptionals: RETURN_EMPTY_ARRAY,
1610
+ getMemberRanges: RETURN_EMPTY_ARRAY
1603
1611
  };
1604
1612
  }
1605
1613
  });
@@ -1612,9 +1620,9 @@ class JavascriptParser extends Parser {
1612
1620
  return {
1613
1621
  name: info,
1614
1622
  rootInfo: info,
1615
- getMembers: () => [],
1616
- getMembersOptionals: () => [],
1617
- getMemberRanges: () => []
1623
+ getMembers: RETURN_EMPTY_ARRAY,
1624
+ getMembersOptionals: RETURN_EMPTY_ARRAY,
1625
+ getMemberRanges: RETURN_EMPTY_ARRAY
1618
1626
  };
1619
1627
  }
1620
1628
  });
@@ -2063,6 +2071,15 @@ class JavascriptParser extends Parser {
2063
2071
  * @returns {string | VariableInfo | undefined} identifier
2064
2072
  */
2065
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
+ }
2066
2083
  const result = this.evaluateExpression(expr);
2067
2084
  if (result.isIdentifier()) {
2068
2085
  return result.identifier;
@@ -2202,49 +2219,60 @@ class JavascriptParser extends Parser {
2202
2219
  preWalkStatement(statement) {
2203
2220
  /** @type {StatementPath} */
2204
2221
  (this.statementPath).push(statement);
2205
- 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) {
2206
2232
  this.prevStatement =
2207
2233
  /** @type {StatementPath} */
2208
2234
  (this.statementPath).pop();
2209
2235
  return;
2210
2236
  }
2237
+ // cases ordered by measured statement frequency; the commonest statements
2238
+ // (expressions, returns) intentionally match nothing here
2211
2239
  switch (statement.type) {
2212
- case "BlockStatement":
2213
- this.preWalkBlockStatement(statement);
2214
- break;
2215
- case "DoWhileStatement":
2216
- this.preWalkDoWhileStatement(statement);
2217
- break;
2218
- case "ForInStatement":
2219
- this.preWalkForInStatement(statement);
2240
+ case "VariableDeclaration":
2241
+ this.preWalkVariableDeclaration(statement);
2220
2242
  break;
2221
- case "ForOfStatement":
2222
- this.preWalkForOfStatement(statement);
2243
+ case "IfStatement":
2244
+ this.preWalkIfStatement(statement);
2223
2245
  break;
2224
- case "ForStatement":
2225
- this.preWalkForStatement(statement);
2246
+ case "BlockStatement":
2247
+ this.preWalkBlockStatement(statement);
2226
2248
  break;
2227
2249
  case "FunctionDeclaration":
2228
2250
  this.preWalkFunctionDeclaration(statement);
2229
2251
  break;
2230
- case "IfStatement":
2231
- this.preWalkIfStatement(statement);
2232
- break;
2233
- case "LabeledStatement":
2234
- this.preWalkLabeledStatement(statement);
2235
- break;
2236
- case "SwitchStatement":
2237
- this.preWalkSwitchStatement(statement);
2252
+ case "ForStatement":
2253
+ this.preWalkForStatement(statement);
2238
2254
  break;
2239
2255
  case "TryStatement":
2240
2256
  this.preWalkTryStatement(statement);
2241
2257
  break;
2242
- case "VariableDeclaration":
2243
- this.preWalkVariableDeclaration(statement);
2258
+ case "SwitchStatement":
2259
+ this.preWalkSwitchStatement(statement);
2244
2260
  break;
2245
2261
  case "WhileStatement":
2246
2262
  this.preWalkWhileStatement(statement);
2247
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;
2248
2276
  case "WithStatement":
2249
2277
  this.preWalkWithStatement(statement);
2250
2278
  break;
@@ -2261,27 +2289,37 @@ class JavascriptParser extends Parser {
2261
2289
  blockPreWalkStatement(statement) {
2262
2290
  /** @type {StatementPath} */
2263
2291
  (this.statementPath).push(statement);
2264
- 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) {
2265
2302
  this.prevStatement =
2266
2303
  /** @type {StatementPath} */
2267
2304
  (this.statementPath).pop();
2268
2305
  return;
2269
2306
  }
2307
+ // cases ordered by measured statement frequency
2270
2308
  switch (statement.type) {
2271
- case "ExportDefaultDeclaration":
2272
- this.blockPreWalkExportDefaultDeclaration(statement);
2273
- break;
2274
- case "ExportNamedDeclaration":
2275
- this.blockPreWalkExportNamedDeclaration(statement);
2276
- break;
2277
2309
  case "VariableDeclaration":
2278
2310
  this.blockPreWalkVariableDeclaration(statement);
2279
2311
  break;
2312
+ case "ExpressionStatement":
2313
+ this.blockPreWalkExpressionStatement(statement);
2314
+ break;
2280
2315
  case "ClassDeclaration":
2281
2316
  this.blockPreWalkClassDeclaration(statement);
2282
2317
  break;
2283
- case "ExpressionStatement":
2284
- this.blockPreWalkExpressionStatement(statement);
2318
+ case "ExportNamedDeclaration":
2319
+ this.blockPreWalkExportNamedDeclaration(statement);
2320
+ break;
2321
+ case "ExportDefaultDeclaration":
2322
+ this.blockPreWalkExportDefaultDeclaration(statement);
2285
2323
  }
2286
2324
  this.prevStatement =
2287
2325
  /** @type {StatementPath} */
@@ -2295,51 +2333,42 @@ class JavascriptParser extends Parser {
2295
2333
  walkStatement(statement) {
2296
2334
  /** @type {StatementPath} */
2297
2335
  (this.statementPath).push(statement);
2298
- 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
+ ) {
2299
2341
  this.prevStatement =
2300
2342
  /** @type {StatementPath} */
2301
2343
  (this.statementPath).pop();
2302
2344
  return;
2303
2345
  }
2346
+ // cases ordered by measured statement frequency; V8 compiles this to
2347
+ // sequential compares, so hot types must come first
2304
2348
  switch (statement.type) {
2305
- case "BlockStatement":
2306
- this.walkBlockStatement(statement);
2307
- break;
2308
- case "ClassDeclaration":
2309
- this.walkClassDeclaration(statement);
2310
- break;
2311
- case "DoWhileStatement":
2312
- this.walkDoWhileStatement(statement);
2313
- break;
2314
- case "ExportDefaultDeclaration":
2315
- this.walkExportDefaultDeclaration(statement);
2316
- break;
2317
- case "ExportNamedDeclaration":
2318
- this.walkExportNamedDeclaration(statement);
2319
- break;
2320
2349
  case "ExpressionStatement":
2321
2350
  this.walkExpressionStatement(statement);
2322
2351
  break;
2323
- case "ForInStatement":
2324
- this.walkForInStatement(statement);
2352
+ case "VariableDeclaration":
2353
+ this.walkVariableDeclaration(statement);
2325
2354
  break;
2326
- case "ForOfStatement":
2327
- this.walkForOfStatement(statement);
2355
+ case "ReturnStatement":
2356
+ this.walkReturnStatement(statement);
2328
2357
  break;
2329
- case "ForStatement":
2330
- this.walkForStatement(statement);
2358
+ case "IfStatement":
2359
+ this.walkIfStatement(statement);
2360
+ break;
2361
+ case "BlockStatement":
2362
+ this.walkBlockStatement(statement);
2331
2363
  break;
2332
2364
  case "FunctionDeclaration":
2333
2365
  this.walkFunctionDeclaration(statement);
2334
2366
  break;
2335
- case "IfStatement":
2336
- this.walkIfStatement(statement);
2337
- break;
2338
- case "LabeledStatement":
2339
- this.walkLabeledStatement(statement);
2367
+ case "ForStatement":
2368
+ this.walkForStatement(statement);
2340
2369
  break;
2341
- case "ReturnStatement":
2342
- this.walkReturnStatement(statement);
2370
+ case "TryStatement":
2371
+ this.walkTryStatement(statement);
2343
2372
  break;
2344
2373
  case "SwitchStatement":
2345
2374
  this.walkSwitchStatement(statement);
@@ -2347,15 +2376,30 @@ class JavascriptParser extends Parser {
2347
2376
  case "ThrowStatement":
2348
2377
  this.walkThrowStatement(statement);
2349
2378
  break;
2350
- case "TryStatement":
2351
- this.walkTryStatement(statement);
2352
- break;
2353
- case "VariableDeclaration":
2354
- this.walkVariableDeclaration(statement);
2355
- break;
2356
2379
  case "WhileStatement":
2357
2380
  this.walkWhileStatement(statement);
2358
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;
2359
2403
  case "WithStatement":
2360
2404
  this.walkWithStatement(statement);
2361
2405
  break;
@@ -2511,6 +2555,12 @@ class JavascriptParser extends Parser {
2511
2555
  * @param {WithStatement} statement with statement
2512
2556
  */
2513
2557
  walkWithStatement(statement) {
2558
+ if (this._strictInModuleOutput) {
2559
+ this._reportStrictModeViolation(
2560
+ "`with` statements are not allowed",
2561
+ statement
2562
+ );
2563
+ }
2514
2564
  this.inBlockScope(() => {
2515
2565
  this.walkExpression(statement.object);
2516
2566
  this.walkNestedStatement(statement.body);
@@ -2799,6 +2849,9 @@ class JavascriptParser extends Parser {
2799
2849
  walkFunctionDeclaration(statement) {
2800
2850
  const wasTopLevel = this.scope.topLevelScope;
2801
2851
  this.scope.topLevelScope = false;
2852
+ if (this._strictInModuleOutput) {
2853
+ this._checkStrictModeParams(statement.params);
2854
+ }
2802
2855
  this.inFunctionScope(true, statement.params, () => {
2803
2856
  for (const param of statement.params) {
2804
2857
  this.walkPattern(param);
@@ -3243,7 +3296,6 @@ class JavascriptParser extends Parser {
3243
3296
  props.add({
3244
3297
  id: key.name,
3245
3298
  range: /** @type {Range} */ (key.range),
3246
- loc: /** @type {SourceLocation} */ (key.loc),
3247
3299
  pattern,
3248
3300
  shorthand: this.scope.inShorthand
3249
3301
  });
@@ -3260,7 +3312,6 @@ class JavascriptParser extends Parser {
3260
3312
  props.add({
3261
3313
  id: str,
3262
3314
  range: /** @type {Range} */ (key.range),
3263
- loc: /** @type {SourceLocation} */ (key.loc),
3264
3315
  pattern,
3265
3316
  shorthand: this.scope.inShorthand
3266
3317
  });
@@ -3297,7 +3348,6 @@ class JavascriptParser extends Parser {
3297
3348
  props.add({
3298
3349
  id: `${i}`,
3299
3350
  range: /** @type {Range} */ (element.range),
3300
- loc: /** @type {SourceLocation} */ (element.loc),
3301
3351
  pattern,
3302
3352
  shorthand: false
3303
3353
  });
@@ -3457,6 +3507,12 @@ class JavascriptParser extends Parser {
3457
3507
  */
3458
3508
  walkPattern(pattern) {
3459
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;
3460
3516
  case "ArrayPattern":
3461
3517
  this.walkArrayPattern(pattern);
3462
3518
  break;
@@ -3466,9 +3522,6 @@ class JavascriptParser extends Parser {
3466
3522
  case "MemberExpression":
3467
3523
  this.walkMemberExpression(pattern);
3468
3524
  break;
3469
- case "ObjectPattern":
3470
- this.walkObjectPattern(pattern);
3471
- break;
3472
3525
  case "RestElement":
3473
3526
  this.walkRestElement(pattern);
3474
3527
  break;
@@ -3538,78 +3591,86 @@ class JavascriptParser extends Parser {
3538
3591
  * @param {Expression | SpreadElement | PrivateIdentifier | Super} expression expression
3539
3592
  */
3540
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
3541
3597
  switch (expression.type) {
3542
- case "ArrayExpression":
3543
- this.walkArrayExpression(expression);
3598
+ case "Identifier":
3599
+ this.walkIdentifier(expression);
3544
3600
  break;
3545
- case "ArrowFunctionExpression":
3546
- this.walkArrowFunctionExpression(expression);
3601
+ case "MemberExpression":
3602
+ this.walkMemberExpression(expression);
3547
3603
  break;
3548
- case "AssignmentExpression":
3549
- this.walkAssignmentExpression(expression);
3604
+ case "Literal":
3605
+ if (this._strictInModuleOutput) {
3606
+ this._checkStrictModeLiteral(expression);
3607
+ }
3550
3608
  break;
3551
- case "AwaitExpression":
3552
- this.walkAwaitExpression(expression);
3609
+ case "CallExpression":
3610
+ this.walkCallExpression(expression);
3553
3611
  break;
3554
3612
  case "BinaryExpression":
3555
3613
  this.walkBinaryExpression(expression);
3556
3614
  break;
3557
- case "CallExpression":
3558
- this.walkCallExpression(expression);
3559
- break;
3560
- case "ChainExpression":
3561
- this.walkChainExpression(expression);
3615
+ case "AssignmentExpression":
3616
+ this.walkAssignmentExpression(expression);
3562
3617
  break;
3563
- case "ClassExpression":
3564
- this.walkClassExpression(expression);
3618
+ case "UnaryExpression":
3619
+ this.walkUnaryExpression(expression);
3565
3620
  break;
3566
- case "ConditionalExpression":
3567
- this.walkConditionalExpression(expression);
3621
+ case "LogicalExpression":
3622
+ this.walkLogicalExpression(expression);
3568
3623
  break;
3569
- case "FunctionExpression":
3570
- this.walkFunctionExpression(expression);
3624
+ case "ArrowFunctionExpression":
3625
+ this.walkArrowFunctionExpression(expression);
3571
3626
  break;
3572
- case "Identifier":
3573
- this.walkIdentifier(expression);
3627
+ case "ThisExpression":
3628
+ this.walkThisExpression(expression);
3574
3629
  break;
3575
- case "ImportExpression":
3576
- this.walkImportExpression(expression);
3630
+ case "ConditionalExpression":
3631
+ this.walkConditionalExpression(expression);
3577
3632
  break;
3578
- case "LogicalExpression":
3579
- this.walkLogicalExpression(expression);
3633
+ case "ArrayExpression":
3634
+ this.walkArrayExpression(expression);
3580
3635
  break;
3581
- case "MetaProperty":
3582
- this.walkMetaProperty(expression);
3636
+ case "ObjectExpression":
3637
+ this.walkObjectExpression(expression);
3583
3638
  break;
3584
- case "MemberExpression":
3585
- this.walkMemberExpression(expression);
3639
+ case "FunctionExpression":
3640
+ this.walkFunctionExpression(expression);
3586
3641
  break;
3587
3642
  case "NewExpression":
3588
3643
  this.walkNewExpression(expression);
3589
3644
  break;
3590
- case "ObjectExpression":
3591
- this.walkObjectExpression(expression);
3645
+ case "TemplateLiteral":
3646
+ this.walkTemplateLiteral(expression);
3647
+ break;
3648
+ case "SpreadElement":
3649
+ this.walkSpreadElement(expression);
3592
3650
  break;
3593
3651
  case "SequenceExpression":
3594
3652
  this.walkSequenceExpression(expression);
3595
3653
  break;
3596
- case "SpreadElement":
3597
- this.walkSpreadElement(expression);
3654
+ case "UpdateExpression":
3655
+ this.walkUpdateExpression(expression);
3598
3656
  break;
3599
- case "TaggedTemplateExpression":
3600
- this.walkTaggedTemplateExpression(expression);
3657
+ case "AwaitExpression":
3658
+ this.walkAwaitExpression(expression);
3601
3659
  break;
3602
- case "TemplateLiteral":
3603
- this.walkTemplateLiteral(expression);
3660
+ case "ChainExpression":
3661
+ this.walkChainExpression(expression);
3604
3662
  break;
3605
- case "ThisExpression":
3606
- this.walkThisExpression(expression);
3663
+ case "ClassExpression":
3664
+ this.walkClassExpression(expression);
3607
3665
  break;
3608
- case "UnaryExpression":
3609
- this.walkUnaryExpression(expression);
3666
+ case "ImportExpression":
3667
+ this.walkImportExpression(expression);
3610
3668
  break;
3611
- case "UpdateExpression":
3612
- this.walkUpdateExpression(expression);
3669
+ case "MetaProperty":
3670
+ this.walkMetaProperty(expression);
3671
+ break;
3672
+ case "TaggedTemplateExpression":
3673
+ this.walkTaggedTemplateExpression(expression);
3613
3674
  break;
3614
3675
  case "YieldExpression":
3615
3676
  this.walkYieldExpression(expression);
@@ -3617,6 +3678,35 @@ class JavascriptParser extends Parser {
3617
3678
  }
3618
3679
  }
3619
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
+
3620
3710
  /**
3621
3711
  * Walk await expression.
3622
3712
  * @param {AwaitExpression} expression await expression
@@ -3700,6 +3790,9 @@ class JavascriptParser extends Parser {
3700
3790
  ? [...expression.params, expression.id]
3701
3791
  : expression.params;
3702
3792
 
3793
+ if (this._strictInModuleOutput) {
3794
+ this._checkStrictModeParams(expression.params);
3795
+ }
3703
3796
  this.inFunctionScope(true, scopeParams, () => {
3704
3797
  for (const param of expression.params) {
3705
3798
  this.walkPattern(param);
@@ -3723,6 +3816,9 @@ class JavascriptParser extends Parser {
3723
3816
  walkArrowFunctionExpression(expression) {
3724
3817
  const wasTopLevel = this.scope.topLevelScope;
3725
3818
  this.scope.topLevelScope = wasTopLevel ? "arrow" : false;
3819
+ if (this._strictInModuleOutput) {
3820
+ this._checkStrictModeParams(expression.params);
3821
+ }
3726
3822
  this.inFunctionScope(false, expression.params, () => {
3727
3823
  for (const param of expression.params) {
3728
3824
  this.walkPattern(param);
@@ -3784,9 +3880,84 @@ class JavascriptParser extends Parser {
3784
3880
  * @param {UpdateExpression} expression the update expression
3785
3881
  */
3786
3882
  walkUpdateExpression(expression) {
3883
+ if (
3884
+ this._strictInModuleOutput &&
3885
+ expression.argument.type === "Identifier"
3886
+ ) {
3887
+ this._checkStrictModeAssignTarget(expression.argument.name, expression);
3888
+ }
3787
3889
  this.walkExpression(expression.argument);
3788
3890
  }
3789
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
+
3790
3961
  /**
3791
3962
  * Walk unary expression.
3792
3963
  * @param {UnaryExpression} expression the unary expression
@@ -3807,10 +3978,39 @@ class JavascriptParser extends Parser {
3807
3978
  );
3808
3979
  if (result === true) return;
3809
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
+ );
3810
3990
  }
3811
3991
  this.walkExpression(expression.argument);
3812
3992
  }
3813
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
+
3814
4014
  /**
3815
4015
  * Walk left right expression.
3816
4016
  * @param {LogicalExpression | BinaryExpression} expression the expression
@@ -3849,6 +4049,9 @@ class JavascriptParser extends Parser {
3849
4049
  */
3850
4050
  walkAssignmentExpression(expression) {
3851
4051
  if (expression.left.type === "Identifier") {
4052
+ if (this._strictInModuleOutput) {
4053
+ this._checkStrictModeAssignTarget(expression.left.name, expression);
4054
+ }
3852
4055
  const renameIdentifier = this.getRenameIdentifier(expression.right);
3853
4056
  if (
3854
4057
  renameIdentifier &&
@@ -3950,38 +4153,6 @@ class JavascriptParser extends Parser {
3950
4153
  * @param {NewExpression} expression new expression
3951
4154
  */
3952
4155
  walkNewExpression(expression) {
3953
- // TODO acorn-import-phases bug: it omits acorn's `!forNew` guard, so
3954
- // `import.defer(...)`/`import.source(...)` are wrongly accepted as `new`
3955
- // operands; drop once fixed. Parenthesized forms stay valid (check `(`).
3956
- if (typeof this.state.source === "string") {
3957
- let base = expression.callee;
3958
- while (
3959
- base.type === "MemberExpression" ||
3960
- base.type === "CallExpression"
3961
- ) {
3962
- base = base.type === "MemberExpression" ? base.object : base.callee;
3963
- }
3964
- if (base.type === "ImportExpression") {
3965
- const newStart = /** @type {Range} */ (expression.range)[0];
3966
- const importStart = /** @type {Range} */ (base.range)[0];
3967
- const between = this.state.source
3968
- .slice(newStart, importStart)
3969
- .replace(/\/\*[\s\S]*?\*\//g, "")
3970
- .replace(/\/\/[^\n]*/g, "");
3971
- if (!between.includes("(")) {
3972
- const err =
3973
- /** @type {SyntaxError & { loc?: { line: number, column: number } }} */
3974
- (new SyntaxError("import call cannot be the target of `new`"));
3975
- if (expression.loc) {
3976
- err.loc = {
3977
- line: expression.loc.start.line,
3978
- column: expression.loc.start.column
3979
- };
3980
- }
3981
- throw err;
3982
- }
3983
- }
3984
- }
3985
4156
  const result = this.callHooksForExpression(
3986
4157
  this.hooks.new,
3987
4158
  expression.callee,
@@ -4223,19 +4394,33 @@ class JavascriptParser extends Parser {
4223
4394
  if (result === true) return;
4224
4395
  }
4225
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
+ }
4226
4410
  const callee = this.evaluateExpression(expression.callee);
4227
4411
  if (callee.isIdentifier()) {
4412
+ const members =
4413
+ /** @type {NonNullable<BasicEvaluatedExpression["getMembers"]>} */
4414
+ (callee.getMembers)();
4228
4415
  const result1 = this.callHooksForInfo(
4229
4416
  this.hooks.callMemberChain,
4230
4417
  /** @type {NonNullable<BasicEvaluatedExpression["rootInfo"]>} */
4231
4418
  (callee.rootInfo),
4232
4419
  expression,
4233
- /** @type {NonNullable<BasicEvaluatedExpression["getMembers"]>} */
4234
- (callee.getMembers)(),
4420
+ members,
4235
4421
  callee.getMembersOptionals
4236
4422
  ? callee.getMembersOptionals()
4237
- : /** @type {NonNullable<BasicEvaluatedExpression["getMembers"]>} */
4238
- (callee.getMembers)().map(() => false),
4423
+ : members.map(RETURN_FALSE),
4239
4424
  callee.getMemberRanges ? callee.getMemberRanges() : []
4240
4425
  );
4241
4426
  if (result1 === true) return;
@@ -4739,22 +4924,14 @@ class JavascriptParser extends Parser {
4739
4924
  */
4740
4925
  enterPattern(pattern, onIdent) {
4741
4926
  if (!pattern) return;
4927
+ // plain identifiers dominate bindings, so match them first
4742
4928
  switch (pattern.type) {
4743
- case "ArrayPattern":
4744
- this.enterArrayPattern(pattern, onIdent);
4745
- break;
4746
- case "AssignmentPattern":
4747
- this.enterAssignmentPattern(pattern, onIdent);
4748
- break;
4749
4929
  case "Identifier":
4750
4930
  this.enterIdentifier(pattern, onIdent);
4751
4931
  break;
4752
4932
  case "ObjectPattern":
4753
4933
  this.enterObjectPattern(pattern, onIdent);
4754
4934
  break;
4755
- case "RestElement":
4756
- this.enterRestElement(pattern, onIdent);
4757
- break;
4758
4935
  case "Property":
4759
4936
  if (pattern.shorthand && pattern.value.type === "Identifier") {
4760
4937
  this.scope.inShorthand = pattern.value.name;
@@ -4764,6 +4941,15 @@ class JavascriptParser extends Parser {
4764
4941
  this.enterPattern(/** @type {Pattern} */ (pattern.value), onIdent);
4765
4942
  }
4766
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;
4767
4953
  }
4768
4954
  }
4769
4955
 
@@ -4851,9 +5037,9 @@ class JavascriptParser extends Parser {
4851
5037
  console.warn(err);
4852
5038
  // ignore error
4853
5039
  }
4854
- return new BasicEvaluatedExpression()
4855
- .setRange(/** @type {Range} */ (expression.range))
4856
- .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);
4857
5043
  }
4858
5044
 
4859
5045
  /**
@@ -4992,30 +5178,21 @@ class JavascriptParser extends Parser {
4992
5178
  let ast;
4993
5179
  /** @type {Comment[]} */
4994
5180
  let comments;
4995
- /** @type {Set<number>} */
4996
- let semicolons;
4997
5181
 
4998
5182
  if (typeof source === "object") {
4999
- semicolons = new Set();
5000
-
5001
5183
  ast = /** @type {Program} */ (source);
5002
5184
  comments = source.comments;
5003
- if (source.semicolons) {
5004
- // Forward semicolon information from the preparsed AST if present
5005
- // This ensures the output is consistent with that of a fresh AST
5006
- for (const pos of source.semicolons) {
5007
- semicolons.add(pos);
5008
- }
5009
- }
5010
5185
  } else {
5011
- ({ ast, comments, semicolons } = JavascriptParser._parse(
5186
+ ({ ast, comments } = JavascriptParser._parse(
5012
5187
  source,
5013
5188
  {
5014
5189
  sourceType: this.sourceType,
5015
- locations: true,
5190
+ // line/column locations are derived from node offsets via
5191
+ // getLocation — parsers never need to track them
5192
+ locations: false,
5016
5193
  ranges: true,
5017
5194
  comments: true,
5018
- semicolons: true
5195
+ importPhases: this.options.importPhases === true
5019
5196
  },
5020
5197
  this.options.parse
5021
5198
  ));
@@ -5027,6 +5204,18 @@ class JavascriptParser extends Parser {
5027
5204
  const oldSemicolons = this.semicolons;
5028
5205
  const oldStatementPath = this.statementPath;
5029
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;
5030
5219
  this.scope = {
5031
5220
  topLevelScope: true,
5032
5221
  inTry: false,
@@ -5039,12 +5228,32 @@ class JavascriptParser extends Parser {
5039
5228
  };
5040
5229
  this.state = state;
5041
5230
  this.comments = comments;
5042
- 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);
5043
5240
  this.statementPath = [];
5044
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();
5045
5249
  if (this.hooks.program.call(ast, comments) === undefined) {
5046
5250
  this.destructuringAssignmentProperties = new WeakMap();
5047
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
+ }
5048
5257
  this.modulePreWalkStatements(ast.body);
5049
5258
  this.prevStatement = undefined;
5050
5259
  this.preWalkStatements(ast.body);
@@ -5061,6 +5270,10 @@ class JavascriptParser extends Parser {
5061
5270
  this.semicolons = oldSemicolons;
5062
5271
  this.statementPath = oldStatementPath;
5063
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;
5064
5277
  return state;
5065
5278
  }
5066
5279
 
@@ -5070,11 +5283,41 @@ class JavascriptParser extends Parser {
5070
5283
  * @returns {BasicEvaluatedExpression} evaluation result
5071
5284
  */
5072
5285
  evaluate(source) {
5073
- const { ast } = JavascriptParser._parse(
5074
- `(${source})`,
5075
- { sourceType: this.sourceType },
5076
- this.options.parse
5077
- );
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
+ }
5078
5321
  if (ast.body.length !== 1 || ast.body[0].type !== "ExpressionStatement") {
5079
5322
  throw new Error("evaluate: Source is not a expression");
5080
5323
  }
@@ -5333,34 +5576,127 @@ class JavascriptParser extends Parser {
5333
5576
  return !evaluated.couldHaveSideEffects();
5334
5577
  }
5335
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
+
5336
5615
  /**
5337
5616
  * Returns comments in the range.
5338
5617
  * @param {Range} range range
5339
5618
  * @returns {Comment[]} comments in the range
5340
5619
  */
5341
5620
  getComments(range) {
5342
- const [rangeStart, rangeEnd] = range;
5343
- /**
5344
- * Returns compared.
5345
- * @param {Comment} comment comment
5346
- * @param {number} needle needle
5347
- * @returns {number} compared
5348
- */
5349
- const compare = (comment, needle) =>
5350
- /** @type {Range} */ (comment.range)[0] - needle;
5351
- const comments = /** @type {Comment[]} */ (this.comments);
5352
- let idx = binarySearchBounds.ge(comments, rangeStart, compare);
5353
- /** @type {Comment[]} */
5354
- const commentsInRange = [];
5355
- while (
5356
- comments[idx] &&
5357
- /** @type {Range} */ (comments[idx].range)[1] <= rangeEnd
5358
- ) {
5359
- commentsInRange.push(comments[idx]);
5360
- 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;
5361
5677
  }
5678
+ return -1;
5679
+ }
5362
5680
 
5363
- 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; /* ; */
5364
5700
  }
5365
5701
 
5366
5702
  /**
@@ -5380,17 +5716,14 @@ class JavascriptParser extends Parser {
5380
5716
 
5381
5717
  return (
5382
5718
  // Either asking directly for the end position of the current statement
5383
- (range[1] === pos &&
5384
- /** @type {Set<number>} */ (this.semicolons).has(pos)) ||
5719
+ (range[1] === pos && this._isAsiPosition(pos)) ||
5385
5720
  // Or asking for the start position of the current statement,
5386
5721
  // here we have to check multiple things
5387
5722
  (range[0] === pos &&
5388
5723
  // is there a previous statement which might be relevant?
5389
5724
  this.prevStatement !== undefined &&
5390
5725
  // is the end position of the previous statement an ASI position?
5391
- /** @type {Set<number>} */ (this.semicolons).has(
5392
- /** @type {Range} */ (this.prevStatement.range)[1]
5393
- ))
5726
+ this._isAsiPosition(/** @type {Range} */ (this.prevStatement.range)[1]))
5394
5727
  );
5395
5728
  }
5396
5729
 
@@ -5400,7 +5733,7 @@ class JavascriptParser extends Parser {
5400
5733
  * @returns {void}
5401
5734
  */
5402
5735
  setAsiPosition(pos) {
5403
- /** @type {Set<number>} */ (this.semicolons).add(pos);
5736
+ (this.semicolons || (this.semicolons = new Map())).set(pos, true);
5404
5737
  }
5405
5738
 
5406
5739
  /**
@@ -5409,7 +5742,7 @@ class JavascriptParser extends Parser {
5409
5742
  * @returns {void}
5410
5743
  */
5411
5744
  unsetAsiPosition(pos) {
5412
- /** @type {Set<number>} */ (this.semicolons).delete(pos);
5745
+ (this.semicolons || (this.semicolons = new Map())).set(pos, false);
5413
5746
  }
5414
5747
 
5415
5748
  /**
@@ -5524,6 +5857,22 @@ class JavascriptParser extends Parser {
5524
5857
  return true;
5525
5858
  }
5526
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
+
5527
5876
  /**
5528
5877
  * Gets variable info.
5529
5878
  * @param {string} name variable name
@@ -5583,42 +5932,13 @@ class JavascriptParser extends Parser {
5583
5932
  * @returns {{ options: Record<string, EXPECTED_ANY> | null, errors: (Error & { comment: Comment })[] | null }} result
5584
5933
  */
5585
5934
  parseCommentOptions(range) {
5586
- const comments = this.getComments(range);
5587
- if (comments.length === 0) {
5588
- return EMPTY_COMMENT_OPTIONS;
5589
- }
5590
- /** @type {Record<string, EXPECTED_ANY>} */
5591
- const options = {};
5592
- /** @type {(Error & { comment: Comment })[]} */
5593
- const errors = [];
5594
- for (const comment of comments) {
5595
- const { value } = comment;
5596
- if (value && webpackCommentRegExp.test(value)) {
5597
- // try compile only if webpack options comment is present
5598
- try {
5599
- for (let [key, val] of Object.entries(
5600
- vm.runInContext(
5601
- `(function(){return {${value}};})()`,
5602
- this.magicCommentContext
5603
- )
5604
- )) {
5605
- if (typeof val === "object" && val !== null) {
5606
- val =
5607
- val.constructor.name === "RegExp"
5608
- ? new RegExp(val)
5609
- : JSON.parse(JSON.stringify(val));
5610
- }
5611
- options[key] = val;
5612
- }
5613
- } catch (err) {
5614
- const newErr = new Error(String(/** @type {Error} */ (err).message));
5615
- newErr.stack = String(/** @type {Error} */ (err).stack);
5616
- Object.assign(newErr, { comment });
5617
- errors.push(/** @type {(Error & { comment: Comment })} */ (newErr));
5618
- }
5619
- }
5620
- }
5621
- return { options, errors };
5935
+ return parseCommentOptionsInRange(
5936
+ /** @type {(Comment & { range: [number, number], value: string })[]} */ (
5937
+ this.comments
5938
+ ),
5939
+ range,
5940
+ this.magicCommentContext
5941
+ );
5622
5942
  }
5623
5943
 
5624
5944
  /**
@@ -5852,12 +6172,30 @@ class JavascriptParser extends Parser {
5852
6172
  static _parse(code, options, customParse) {
5853
6173
  const type = options ? options.sourceType : "module";
5854
6174
  /** @type {ParseOptions} */
5855
- const parserOptions = {
5856
- ...defaultParserOptions,
5857
- allowReturnOutsideFunction: type === "script",
5858
- ...options,
5859
- sourceType: type === "auto" ? "module" : type
5860
- };
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;
5861
6199
  /**
5862
6200
  * Returns parse result.
5863
6201
  * @param {string} code source code
@@ -5872,36 +6210,46 @@ class JavascriptParser extends Parser {
5872
6210
  /** @type {Comment[]} */
5873
6211
  const comments = [];
5874
6212
 
5875
- if (options.comments) {
5876
- /** @type {AcornOptions} */
5877
- (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;
5878
6223
  }
5879
6224
 
5880
- /** @type {Set<number>} */
5881
- const semicolons = new Set();
5882
-
5883
- if (options.semicolons) {
5884
- /** @type {AcornOptions} */
5885
- (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
+ }
5886
6236
  }
5887
6237
 
5888
6238
  const ast =
5889
6239
  /** @type {Program} */
5890
6240
  (parser.parse(code, /** @type {AcornOptions} */ (options)));
5891
6241
 
5892
- return { ast, comments, semicolons };
6242
+ return { ast, comments };
5893
6243
  };
5894
6244
 
5895
6245
  /** @type {Program | undefined} */
5896
6246
  let ast;
5897
6247
  /** @type {Comment[] | undefined} */
5898
6248
  let comments;
5899
- /** @type {Set<number> | undefined} */
5900
- let semicolons;
5901
6249
  let error;
5902
6250
  let threw = false;
5903
6251
  try {
5904
- ({ ast, comments, semicolons } = internalParse(code, parserOptions));
6252
+ ({ ast, comments } = internalParse(code, parserOptions));
5905
6253
  } catch (err) {
5906
6254
  error = err;
5907
6255
  threw = true;
@@ -5910,9 +6258,11 @@ class JavascriptParser extends Parser {
5910
6258
  if (threw && type === "auto") {
5911
6259
  parserOptions.sourceType = "script";
5912
6260
  parserOptions.allowReturnOutsideFunction = true;
6261
+ // the retry is already a full script parse — no in-place downgrade
6262
+ parserOptions.moduleFallback = false;
5913
6263
 
5914
6264
  try {
5915
- ({ ast, comments, semicolons } = internalParse(code, parserOptions));
6265
+ ({ ast, comments } = internalParse(code, parserOptions));
5916
6266
  threw = false;
5917
6267
  } catch (_err) {
5918
6268
  // we use the error from first parse try
@@ -5920,16 +6270,22 @@ class JavascriptParser extends Parser {
5920
6270
  }
5921
6271
  }
5922
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
+
5923
6279
  if (threw) {
5924
6280
  throw error;
5925
6281
  }
5926
6282
 
5927
- return /** @type {ParseResult} */ ({ ast, comments, semicolons });
6283
+ return /** @type {ParseResult} */ ({ ast, comments });
5928
6284
  }
5929
6285
 
5930
6286
  /**
5931
6287
  * Returns parser.
5932
- * @param {((BaseParser: typeof AcornParser) => typeof AcornParser)[]} plugins parser plugin
6288
+ * @param {((BaseParser: AcornParser) => AcornParser)[]} plugins parser plugin
5933
6289
  * @returns {typeof JavascriptParser} parser
5934
6290
  */
5935
6291
  static extend(...plugins) {