webpack 5.111.0 → 5.111.1

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 (144) hide show
  1. package/lib/APIPlugin.js +3 -4
  2. package/lib/CompatibilityPlugin.js +3 -4
  3. package/lib/Compilation.js +16 -35
  4. package/lib/Compiler.js +4 -5
  5. package/lib/ConstPlugin.js +16 -132
  6. package/lib/DependencyTemplate.js +2 -1
  7. package/lib/DotenvPlugin.js +24 -28
  8. package/lib/ErrorHelpers.js +113 -0
  9. package/lib/ExportsInfo.js +20 -16
  10. package/lib/ExternalModule.js +6 -8
  11. package/lib/FlagDependencyExportsPlugin.js +8 -13
  12. package/lib/FlagDependencyUsagePlugin.js +9 -13
  13. package/lib/Generator.js +24 -2
  14. package/lib/HotModuleReplacementPlugin.js +2 -0
  15. package/lib/IgnorePlugin.js +1 -1
  16. package/lib/LoaderOptionsPlugin.js +1 -1
  17. package/lib/Module.js +3 -3
  18. package/lib/ModuleGraph.js +6 -9
  19. package/lib/MultiCompiler.js +6 -16
  20. package/lib/NormalModule.js +96 -44
  21. package/lib/RuntimeTemplate.js +33 -43
  22. package/lib/SourceMapDevToolPlugin.js +53 -103
  23. package/lib/TemplatedPathPlugin.js +16 -59
  24. package/lib/asset/AssetBytesGenerator.js +7 -1
  25. package/lib/asset/AssetGenerator.js +25 -16
  26. package/lib/asset/AssetSourceGenerator.js +7 -1
  27. package/lib/buildChunkGraph.js +11 -15
  28. package/lib/cache/MemoryCachePlugin.js +4 -4
  29. package/lib/cache/MemoryWithGcCachePlugin.js +7 -8
  30. package/lib/cache/PackFileCacheStrategy.js +3 -18
  31. package/lib/cache/getLazyHashedEtag.js +3 -5
  32. package/lib/config/browserslistTargetHandler.js +2 -8
  33. package/lib/config/defaults.js +52 -78
  34. package/lib/config/getClaimedAssetTypes.js +10 -11
  35. package/lib/config/target.js +3 -4
  36. package/lib/{ContextExclusionPlugin.js → context/ContextExclusionPlugin.js} +1 -1
  37. package/lib/{ContextModule.js → context/ContextModule.js} +37 -34
  38. package/lib/{ContextModuleFactory.js → context/ContextModuleFactory.js} +19 -21
  39. package/lib/{ContextReplacementPlugin.js → context/ContextReplacementPlugin.js} +4 -4
  40. package/lib/css/CssGenerator.js +19 -11
  41. package/lib/css/CssParser.js +18 -20
  42. package/lib/css/cssMinify.js +32 -29
  43. package/lib/css/data.js +797 -288
  44. package/lib/css/syntax.js +2250 -934
  45. package/lib/debug/ProfilingPlugin.js +3 -4
  46. package/lib/dependencies/CommonJsExportRequireDependency.js +10 -11
  47. package/lib/dependencies/CommonJsImportsParserPlugin.js +4 -5
  48. package/lib/dependencies/CommonJsSelfReferenceDependency.js +2 -4
  49. package/lib/dependencies/ContextDependency.js +1 -1
  50. package/lib/dependencies/ContextDependencyHelpers.js +3 -9
  51. package/lib/dependencies/ContextElementDependency.js +1 -1
  52. package/lib/dependencies/CssIcssExportDependency.js +6 -8
  53. package/lib/dependencies/CssIcssImportDependency.js +3 -6
  54. package/lib/dependencies/CssUrlDependency.js +3 -5
  55. package/lib/dependencies/HarmonyCompatibilityDependency.js +11 -20
  56. package/lib/dependencies/HarmonyDetectionParserPlugin.js +3 -4
  57. package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +2 -4
  58. package/lib/dependencies/HarmonyExportExpressionDependency.js +3 -4
  59. package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +3 -4
  60. package/lib/dependencies/HarmonyExportInitFragment.js +26 -19
  61. package/lib/dependencies/HarmonyExportSpecifierDependency.js +7 -28
  62. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +103 -16
  63. package/lib/dependencies/HarmonyImportSideEffectDependency.js +3 -4
  64. package/lib/dependencies/HarmonyImportSpecifierDependency.js +16 -2
  65. package/lib/dependencies/HtmlEntryDependency.js +74 -102
  66. package/lib/dependencies/HtmlInlineScriptDependency.js +3 -4
  67. package/lib/dependencies/HtmlInlineStyleDependency.js +3 -4
  68. package/lib/dependencies/ImportMetaContextDependencyParserPlugin.js +1 -1
  69. package/lib/dependencies/ImportMetaGlobHelpers.js +1 -1
  70. package/lib/dependencies/ImportMetaPlugin.js +8 -10
  71. package/lib/dependencies/ImportParserPlugin.js +1 -1
  72. package/lib/dependencies/LoaderPlugin.js +26 -1
  73. package/lib/dependencies/RequireContextDependencyParserPlugin.js +1 -1
  74. package/lib/dependencies/URLContextDependency.js +1 -1
  75. package/lib/dependencies/URLDependency.js +6 -5
  76. package/lib/dependencies/WorkerAndWorkletPlugin.js +15 -21
  77. package/lib/dependencies/WorkerDependency.js +6 -5
  78. package/lib/dependencies/WorkletDependency.js +6 -9
  79. package/lib/errors/HookWebpackError.js +3 -2
  80. package/lib/hmr/LazyCompilationPlugin.js +3 -5
  81. package/lib/html/HtmlGenerator.js +61 -82
  82. package/lib/html/HtmlModulesPlugin.js +88 -139
  83. package/lib/html/HtmlParser.js +97 -141
  84. package/lib/html/data.js +21 -0
  85. package/lib/html/htmlMinify.js +10 -11
  86. package/lib/html/syntax.js +883 -993
  87. package/lib/index.js +46 -3
  88. package/lib/javascript/ChunkFormatHelpers.js +4 -3
  89. package/lib/javascript/JavascriptGenerator.js +8 -1
  90. package/lib/javascript/JavascriptModulesPlugin.js +21 -48
  91. package/lib/javascript/JavascriptParser.js +721 -178
  92. package/lib/javascript/parser.js +6 -0
  93. package/lib/javascript/syntax.js +420 -92
  94. package/lib/json/JsonGenerator.js +7 -1
  95. package/lib/library/ModuleLibraryPlugin.js +64 -136
  96. package/lib/loaders/LoaderRunner.js +137 -9
  97. package/lib/node/NodeWatchFileSystem.js +3 -9
  98. package/lib/optimize/AggressiveMergingPlugin.js +3 -5
  99. package/lib/optimize/ConcatenatedModule.js +81 -100
  100. package/lib/optimize/FlagIncludedChunksPlugin.js +3 -11
  101. package/lib/optimize/InnerGraphPlugin.js +7 -13
  102. package/lib/optimize/LimitChunkCountPlugin.js +5 -8
  103. package/lib/optimize/MangleExportsPlugin.js +3 -4
  104. package/lib/optimize/ModuleConcatenationPlugin.js +3 -4
  105. package/lib/optimize/SideEffectsFlagPlugin.js +7 -19
  106. package/lib/optimize/SplitChunksPlugin.js +2 -4
  107. package/lib/performance/BroadContextsPlugin.js +1 -1
  108. package/lib/performance/CacheEffectivenessPlugin.js +3 -4
  109. package/lib/performance/SizeLimitsPlugin.js +2 -4
  110. package/lib/prefetch/ChunkPrefetchPreloadPlugin.js +6 -8
  111. package/lib/prefetch/ResourceHintPlugin.js +17 -21
  112. package/lib/prefetch/StartupAssetHintRuntimeModule.js +7 -10
  113. package/lib/runtime/AsyncModuleRuntimeModule.js +3 -5
  114. package/lib/runtime/GetChunkFilenameRuntimeModule.js +3 -4
  115. package/lib/runtime/GlobalRuntimeModule.js +2 -4
  116. package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +55 -85
  117. package/lib/schemes/DataUriPlugin.js +3 -6
  118. package/lib/schemes/VirtualUrlPlugin.js +1 -1
  119. package/lib/serialization/BinaryMiddleware.js +9 -9
  120. package/lib/serialization/FileMiddleware.js +16 -16
  121. package/lib/serialization/ObjectMiddleware.js +30 -33
  122. package/lib/stats/DefaultStatsFactoryPlugin.js +3 -4
  123. package/lib/typescript/TypeScriptPlugin.js +2 -3
  124. package/lib/url/URLParserPlugin.js +6 -8
  125. package/lib/util/LocConverter.js +4 -7
  126. package/lib/util/SourceProcessor.js +15 -22
  127. package/lib/util/chainedImports.js +3 -4
  128. package/lib/util/concatenate.js +4 -4
  129. package/lib/util/createHooksRegistry.js +1 -2
  130. package/lib/util/dataURL.js +2 -3
  131. package/lib/util/deterministicGrouping.js +8 -13
  132. package/lib/util/findGraphRoots.js +6 -13
  133. package/lib/util/globUtils.js +1 -1
  134. package/lib/util/hash/wasm-hash.js +3 -4
  135. package/lib/util/internalSerializables.js +1 -1
  136. package/lib/util/semver.js +5 -3
  137. package/lib/wasm-async/AsyncWebAssemblyGenerator.js +6 -1
  138. package/lib/wasm-async/AsyncWebAssemblyJavascriptGenerator.js +5 -1
  139. package/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js +3 -4
  140. package/lib/wasm-sync/WebAssemblyGenerator.js +6 -1
  141. package/lib/wasm-sync/WebAssemblyJavascriptGenerator.js +5 -1
  142. package/lib/wasm-sync/WebAssemblyUtils.js +2 -4
  143. package/package.json +30 -23
  144. package/types.d.ts +299 -280
@@ -17,12 +17,19 @@ const {
17
17
  parseCommentOptionsInRange
18
18
  } = require("../util/magicComment");
19
19
  const BasicEvaluatedExpression = require("./BasicEvaluatedExpression");
20
+ const { nonASCIIwhitespace } = require("./parser");
20
21
  const {
22
+ BLOCK_DECLARATIONS,
23
+ HOISTED_DECLARATIONS,
21
24
  LEGACY_ASSERT_ATTRIBUTES,
25
+ MAX_MASKED_INDEX,
26
+ MODULE_DECLARATIONS,
22
27
  WebpackParser,
23
28
  buildLineStarts,
29
+ declaresIntoBlock,
24
30
  hasOctalEscape,
25
31
  isIdentifierChar,
32
+ overflowFrom,
26
33
  positionAt
27
34
  } = require("./syntax");
28
35
 
@@ -137,6 +144,9 @@ const {
137
144
  /** @typedef {(ident: string) => void} OnIdentString */
138
145
  /** @typedef {(ident: string, identifier: Identifier) => void} OnIdent */
139
146
  /** @typedef {StatementPathItem[]} StatementPath */
147
+ /** @typedef {(Statement | ModuleDeclaration)[]} HoistedDeclarations the declarations one scope hoists, in source order */
148
+ /** @typedef {(ModuleDeclaration | Statement | undefined)[]} ModuleDeclarations the program's import and re-export declarations in source order, each followed by the statement written before it */
149
+ /** @typedef {number | number[]} BlockDeclarations which statements of one list declare into its block scope: a mask of their indices, or the indices themselves once one is too large to mask */
140
150
 
141
151
  /** @typedef {Set<DestructuringAssignmentProperty>} DestructuringAssignmentProperties */
142
152
 
@@ -145,6 +155,32 @@ const {
145
155
 
146
156
  /** @type {string[]} */
147
157
  const EMPTY_ARRAY = [];
158
+ /** @type {HoistedDeclarations} */
159
+ const EMPTY_HOISTED_DECLARATIONS = [];
160
+ // what the module pass reports; `export default` is the block pass's
161
+ const MODULE_STATEMENT_TYPES = new Set([
162
+ "ImportDeclaration",
163
+ "ExportNamedDeclaration",
164
+ "ExportAllDeclaration"
165
+ ]);
166
+ // what the parser records per scope, and so what the hoisting pass reaches
167
+ const RECORDED_STATEMENT_TYPES = new Set([
168
+ "VariableDeclaration",
169
+ "FunctionDeclaration",
170
+ "ClassDeclaration"
171
+ ]);
172
+ /** @type {ModuleDeclarations} */
173
+ const EMPTY_MODULE_DECLARATIONS = [];
174
+ // what the parser records per statement list, and so what the block pass
175
+ // reaches. An expression statement is recorded only when it assigns to an
176
+ // object pattern, so a tap on that type is not served from the list.
177
+ const BLOCK_DECLARATION_TYPES = new Set([
178
+ "VariableDeclaration",
179
+ "ClassDeclaration",
180
+ "ExportNamedDeclaration",
181
+ "ExportDefaultDeclaration"
182
+ ]);
183
+
148
184
  /** @type {MembersOptionals} */
149
185
  const EMPTY_MEMBERS_OPTIONALS = [];
150
186
 
@@ -354,6 +390,7 @@ class VariableInfo {
354
390
 
355
391
  /** @typedef {string[]} CalleeMembers */
356
392
  /** @typedef {string[]} Members */
393
+ /** @typedef {UpdateExpression | ForInStatement | ForOfStatement | AssignmentExpression} WriteStatement a construct that writes to the reference it carries */
357
394
  /** @typedef {boolean[]} MembersOptionals */
358
395
  /** @typedef {Range[]} MemberRanges */
359
396
 
@@ -922,6 +959,183 @@ const evaluateAstCaches = new WeakMap();
922
959
  // The inner map is strong while the compilation lives, so cap it
923
960
  const EVALUATE_AST_CACHE_LIMIT = 4096;
924
961
 
962
+ /**
963
+ * Add one declaration, starting the list at the first — most scopes declare
964
+ * nothing, and the shared empty list spares them an array.
965
+ * @param {HoistedDeclarations} hoisted the list so far
966
+ * @param {Statement | ModuleDeclaration} declaration what to add
967
+ * @returns {HoistedDeclarations} the list holding it
968
+ */
969
+ const addHoistedDeclaration = (hoisted, declaration) => {
970
+ if (hoisted === EMPTY_HOISTED_DECLARATIONS) return [declaration];
971
+ hoisted.push(declaration);
972
+ return hoisted;
973
+ };
974
+
975
+ /**
976
+ * Collect the declarations that hoist to one scope, for an AST that did not
977
+ * come from webpack's parser and so carries no record of its own.
978
+ * @param {(Statement | ModuleDeclaration)[]} statements the scope's statements
979
+ * @param {HoistedDeclarations} hoisted the list so far
980
+ * @returns {HoistedDeclarations} the list holding what they declare
981
+ */
982
+ const collectHoistedDeclarations = (statements, hoisted) => {
983
+ for (let index = 0, len = statements.length; index < len; index++) {
984
+ hoisted = collectHoistedDeclaration(statements[index], hoisted);
985
+ }
986
+ return hoisted;
987
+ };
988
+
989
+ /**
990
+ * Collect from one statement, descending exactly as far as a `var` reaches:
991
+ * through blocks and loop bodies, never into another function.
992
+ * @param {Statement | ModuleDeclaration} statement the statement
993
+ * @param {HoistedDeclarations} hoisted the list so far
994
+ * @returns {HoistedDeclarations} the list holding what it declares
995
+ */
996
+ const collectHoistedDeclaration = (statement, hoisted) => {
997
+ switch (statement.type) {
998
+ case "VariableDeclaration":
999
+ if (statement.kind === "var") {
1000
+ hoisted = addHoistedDeclaration(hoisted, statement);
1001
+ }
1002
+ break;
1003
+ case "FunctionDeclaration":
1004
+ case "ClassDeclaration":
1005
+ hoisted = addHoistedDeclaration(hoisted, statement);
1006
+ break;
1007
+ case "BlockStatement":
1008
+ hoisted = collectHoistedDeclarations(statement.body, hoisted);
1009
+ break;
1010
+ case "IfStatement":
1011
+ hoisted = collectHoistedDeclaration(statement.consequent, hoisted);
1012
+ if (statement.alternate) {
1013
+ hoisted = collectHoistedDeclaration(statement.alternate, hoisted);
1014
+ }
1015
+ break;
1016
+ case "ForStatement":
1017
+ if (statement.init && statement.init.type === "VariableDeclaration") {
1018
+ hoisted = collectHoistedDeclaration(statement.init, hoisted);
1019
+ }
1020
+ hoisted = collectHoistedDeclaration(statement.body, hoisted);
1021
+ break;
1022
+ case "ForInStatement":
1023
+ case "ForOfStatement":
1024
+ if (statement.left.type === "VariableDeclaration") {
1025
+ hoisted = collectHoistedDeclaration(statement.left, hoisted);
1026
+ }
1027
+ hoisted = collectHoistedDeclaration(statement.body, hoisted);
1028
+ break;
1029
+ case "WhileStatement":
1030
+ case "DoWhileStatement":
1031
+ case "LabeledStatement":
1032
+ case "WithStatement":
1033
+ hoisted = collectHoistedDeclaration(statement.body, hoisted);
1034
+ break;
1035
+ case "TryStatement":
1036
+ hoisted = collectHoistedDeclaration(statement.block, hoisted);
1037
+ if (statement.handler) {
1038
+ hoisted = collectHoistedDeclaration(statement.handler.body, hoisted);
1039
+ }
1040
+ if (statement.finalizer) {
1041
+ hoisted = collectHoistedDeclaration(statement.finalizer, hoisted);
1042
+ }
1043
+ break;
1044
+ case "SwitchStatement": {
1045
+ const cases = statement.cases;
1046
+ for (let index = 0, len = cases.length; index < len; index++) {
1047
+ hoisted = collectHoistedDeclarations(cases[index].consequent, hoisted);
1048
+ }
1049
+ break;
1050
+ }
1051
+ }
1052
+ return hoisted;
1053
+ };
1054
+
1055
+ /**
1056
+ * What the program imports and re-exports: the parser's own record, or a scan
1057
+ * of the top-level statements for an AST some other parser built.
1058
+ * @param {Program} ast the program
1059
+ * @param {boolean} trusted whether the parser's own record may be read
1060
+ * @returns {ModuleDeclarations} the declarations, in source order
1061
+ */
1062
+ const moduleDeclarationsOf = (ast, trusted) => {
1063
+ if (trusted) {
1064
+ const recorded = /** @type {EXPECTED_ANY} */ (ast)[MODULE_DECLARATIONS];
1065
+ if (recorded !== undefined) return recorded;
1066
+ }
1067
+ /** @type {ModuleDeclarations} */
1068
+ let declarations = EMPTY_MODULE_DECLARATIONS;
1069
+ const body = ast.body;
1070
+ for (let index = 0, len = body.length; index < len; index++) {
1071
+ const statement = body[index];
1072
+ if (MODULE_STATEMENT_TYPES.has(statement.type)) {
1073
+ if (declarations === EMPTY_MODULE_DECLARATIONS) declarations = [];
1074
+ declarations.push(statement, index === 0 ? undefined : body[index - 1]);
1075
+ }
1076
+ }
1077
+ return declarations;
1078
+ };
1079
+
1080
+ /**
1081
+ * What hoists to the scope the node opens: the parser's own record, or a walk
1082
+ * of the body for an AST some other parser built.
1083
+ * @param {Program | BlockStatement} node the node owning the scope
1084
+ * @param {boolean} trusted whether the parser's own record may be read
1085
+ * @returns {HoistedDeclarations} the declarations, in source order
1086
+ */
1087
+ const hoistedDeclarationsOf = (node, trusted) => {
1088
+ if (trusted) {
1089
+ const recorded = /** @type {EXPECTED_ANY} */ (node)[HOISTED_DECLARATIONS];
1090
+ if (recorded !== undefined) return recorded;
1091
+ }
1092
+ return collectHoistedDeclarations(node.body, EMPTY_HOISTED_DECLARATIONS);
1093
+ };
1094
+
1095
+ /**
1096
+ * Which statements of a list declare into its block scope, scanned for an AST
1097
+ * some other parser built. The parser's own record is read instead when there
1098
+ * is one.
1099
+ * @param {(Statement | ModuleDeclaration)[]} statements the list
1100
+ * @returns {BlockDeclarations} their indices, in source order
1101
+ */
1102
+ const collectBlockDeclarations = (statements) => {
1103
+ let mask = 0;
1104
+ /** @type {number[] | undefined} */
1105
+ let overflow;
1106
+ for (let index = 0, len = statements.length; index < len; index++) {
1107
+ if (!declaresIntoBlock(statements[index])) continue;
1108
+ if (overflow !== undefined) overflow.push(index);
1109
+ else if (index <= MAX_MASKED_INDEX) mask |= 1 << index;
1110
+ else overflow = overflowFrom(mask, index);
1111
+ }
1112
+ return overflow === undefined ? mask : overflow;
1113
+ };
1114
+
1115
+ /**
1116
+ * Whether the source between two offsets is whitespace only.
1117
+ * @param {string} source the module source
1118
+ * @param {number} start where to start
1119
+ * @param {number} end where to stop
1120
+ * @returns {boolean} whether nothing but whitespace lies between them
1121
+ */
1122
+ const onlyWhitespaceBetween = (source, start, end) => {
1123
+ for (let index = start; index < end; index++) {
1124
+ const code = source.charCodeAt(index);
1125
+ // the classification `skipSpace` uses: anything else is a token, and a
1126
+ // token means the run of comments before the statement has ended
1127
+ if (
1128
+ code !== 32 &&
1129
+ code !== 160 &&
1130
+ !(code > 8 && code < 14) &&
1131
+ !(code >= 5760 && nonASCIIwhitespace.test(source[index]))
1132
+ ) {
1133
+ return false;
1134
+ }
1135
+ }
1136
+ return true;
1137
+ };
1138
+
925
1139
  const CLASS_NAME = "JavascriptParser";
926
1140
 
927
1141
  // Reserved words in strict mode, so unusable as binding names there (12.7.2).
@@ -984,17 +1198,33 @@ class JavascriptParser extends Parser {
984
1198
  isPure: new HookMap(
985
1199
  () => new SyncBailHook(["expression", "commentsStartPosition"])
986
1200
  ),
987
- /** @type {SyncBailHook<[Statement | ModuleDeclaration | MaybeNamedClassDeclaration | MaybeNamedFunctionDeclaration], boolean | void>} */
1201
+ /**
1202
+ * Every statement the hoisting pass reads, which a tap on this turns
1203
+ * back into a walk of them all. Tap `preStatementByType` instead.
1204
+ * @type {SyncBailHook<[Statement | ModuleDeclaration | MaybeNamedClassDeclaration | MaybeNamedFunctionDeclaration], boolean | void>}
1205
+ * @deprecated TODO webpack 6: remove, with the statement walk it turns on
1206
+ */
988
1207
  preStatement: new SyncBailHook(["statement"]),
989
1208
  /**
1209
+ * Each declaration the parser records: a variable, function or class
1210
+ * one. Any other type turns the pass back into a walk of every
1211
+ * statement, and goes away with that walk in webpack 6.
990
1212
  * @type {HookMap<SyncBailHook<[Statement | ModuleDeclaration | MaybeNamedClassDeclaration | MaybeNamedFunctionDeclaration], boolean | void>>}
991
1213
  * @since 5.109.0
992
1214
  */
993
1215
  preStatementByType: new HookMap(() => new SyncBailHook(["statement"])),
994
1216
 
995
- /** @type {SyncBailHook<[Statement | ModuleDeclaration | MaybeNamedClassDeclaration | MaybeNamedFunctionDeclaration], boolean | void>} */
1217
+ /**
1218
+ * Every statement the block pass reads, which a tap on this turns back
1219
+ * into a walk of them all. Tap `blockPreStatementByType` instead.
1220
+ * @type {SyncBailHook<[Statement | ModuleDeclaration | MaybeNamedClassDeclaration | MaybeNamedFunctionDeclaration], boolean | void>}
1221
+ * @deprecated TODO webpack 6: remove, with the statement walk it turns on
1222
+ */
996
1223
  blockPreStatement: new SyncBailHook(["declaration"]),
997
1224
  /**
1225
+ * Each statement the parser records for the block pass: a variable, class
1226
+ * or export declaration. Any other type turns the pass back into a walk
1227
+ * of every statement, and goes away with that walk in webpack 6.
998
1228
  * @type {HookMap<SyncBailHook<[Statement | ModuleDeclaration | MaybeNamedClassDeclaration | MaybeNamedFunctionDeclaration], boolean | void>>}
999
1229
  * @since 5.109.0
1000
1230
  */
@@ -1092,6 +1322,12 @@ class JavascriptParser extends Parser {
1092
1322
  rename: new HookMap(() => new SyncBailHook(["initExpression"])),
1093
1323
  /** @type {HookMap<SyncBailHook<[AssignmentExpression], boolean | void>>} */
1094
1324
  assign: new HookMap(() => new SyncBailHook(["expression"])),
1325
+ /** @type {HookMap<SyncBailHook<[WriteStatement], boolean | void>>} */
1326
+ write: new HookMap(() => new SyncBailHook(["node"])),
1327
+ /** @type {HookMap<SyncBailHook<[WriteStatement, Members], boolean | void>>} */
1328
+ writeMemberChain: new HookMap(
1329
+ () => new SyncBailHook(["node", "members"])
1330
+ ),
1095
1331
  /** @type {HookMap<SyncBailHook<[AssignmentExpression, Members], boolean | void>>} */
1096
1332
  assignMemberChain: new HookMap(
1097
1333
  () => new SyncBailHook(["expression", "members"])
@@ -1201,6 +1437,37 @@ class JavascriptParser extends Parser {
1201
1437
  this.statementPath = undefined;
1202
1438
  /** @type {Statement | ModuleDeclaration | Expression | MaybeNamedFunctionDeclaration | MaybeNamedClassDeclaration | undefined} */
1203
1439
  this.prevStatement = undefined;
1440
+ /** @type {boolean} */
1441
+ this._hasParserRecord = false;
1442
+ // BACKWARD-COMPAT START
1443
+
1444
+ // WHY: the hoisting pass reaches declarations alone, so a tap that
1445
+ // expects every statement would quietly stop seeing most of them. Such a
1446
+ // tap turns the pass back into the walk it used to be, which no build
1447
+ // webpack's own plugins drive ever asks for.
1448
+ /** @type {boolean} */
1449
+ this._needsFullPreWalk = false;
1450
+ this.hooks.preStatementByType.intercept({
1451
+ factory: (type, hook) => {
1452
+ if (!RECORDED_STATEMENT_TYPES.has(type)) {
1453
+ this._needsFullPreWalk = true;
1454
+ }
1455
+ return hook;
1456
+ }
1457
+ });
1458
+
1459
+ // The same for the block pass, over the types the parser records for it.
1460
+ /** @type {boolean} */
1461
+ this._needsFullBlockPreWalk = false;
1462
+ this.hooks.blockPreStatementByType.intercept({
1463
+ factory: (type, hook) => {
1464
+ if (!BLOCK_DECLARATION_TYPES.has(type)) {
1465
+ this._needsFullBlockPreWalk = true;
1466
+ }
1467
+ return hook;
1468
+ }
1469
+ });
1470
+ // BACKWARD-COMPAT END
1204
1471
  /** @type {WeakMap<Expression, DestructuringAssignmentProperties> | undefined} */
1205
1472
  this.destructuringAssignmentProperties = undefined;
1206
1473
  /** @type {TagData | undefined} */
@@ -2378,12 +2645,19 @@ class JavascriptParser extends Parser {
2378
2645
  }
2379
2646
 
2380
2647
  /**
2381
- * Module pre walking iterates the scope for import entries
2382
- * @param {(Statement | ModuleDeclaration)[]} statements statements
2648
+ * Report what the program imports and re-exports, before anything reads a
2649
+ * name. The list is what the parser recorded, in source order.
2650
+ * @param {ModuleDeclarations} declarations the program's module declarations
2383
2651
  */
2384
- modulePreWalkStatements(statements) {
2385
- for (let index = 0, len = statements.length; index < len; index++) {
2386
- const statement = statements[index];
2652
+ _modulePreWalkDeclarations(declarations) {
2653
+ for (let index = 0, len = declarations.length; index < len; index += 2) {
2654
+ const statement =
2655
+ /** @type {ModuleDeclaration} */
2656
+ (declarations[index]);
2657
+ // what precedes it decides whether removing it joins two statements
2658
+ this.prevStatement =
2659
+ /** @type {Statement | undefined} */
2660
+ (declarations[index + 1]);
2387
2661
  /** @type {StatementPath} */
2388
2662
  (this.statementPath).push(statement);
2389
2663
  switch (statement.type) {
@@ -2404,19 +2678,212 @@ class JavascriptParser extends Parser {
2404
2678
  }
2405
2679
 
2406
2680
  /**
2407
- * Pre walking iterates the scope for variable declarations
2681
+ * Declare what the scope the node opens hoists, before anything reads a
2682
+ * name in it.
2683
+ * @param {Program | BlockStatement} node the node owning the scope
2684
+ */
2685
+ _preWalkScope(node) {
2686
+ // BACKWARD-COMPAT START
2687
+ if (this._needsFullPreWalk || this.hooks.preStatement.taps.length !== 0) {
2688
+ if (node.type === "Program") this.preWalkStatements(node.body);
2689
+ else this.preWalkStatement(node);
2690
+ return;
2691
+ }
2692
+ // BACKWARD-COMPAT END
2693
+ this._preWalkHoistedDeclarations(
2694
+ hoistedDeclarationsOf(node, this._hasParserRecord)
2695
+ );
2696
+ }
2697
+
2698
+ /**
2699
+ * Declare what hoists to the scope being entered, from what the parser
2700
+ * recorded while reading it. `prevStatement` is not available here.
2701
+ * @param {HoistedDeclarations} hoisted what the scope declares
2702
+ */
2703
+ _preWalkHoistedDeclarations(hoisted) {
2704
+ for (let index = 0, len = hoisted.length; index < len; index++) {
2705
+ // WHY: the statements between two declarations are not read here, so
2706
+ // the one before this declaration is unknown rather than the previous
2707
+ // declaration. A tap needing it taps `preStatement`, which walks.
2708
+ this.prevStatement = undefined;
2709
+ this.preWalkStatement(hoisted[index]);
2710
+ }
2711
+ }
2712
+
2713
+ // BACKWARD-COMPAT START
2714
+ /**
2715
+ * Walk every statement of a scope to declare what hoists to it. Only a tap
2716
+ * on the deprecated `preStatement` hook asks for this.
2408
2717
  * @param {(Statement | ModuleDeclaration)[]} statements statements
2718
+ * @deprecated TODO webpack 6: remove, with the `preStatement` hook
2409
2719
  */
2410
2720
  preWalkStatements(statements) {
2721
+ for (let index = 0, len = statements.length; index < len; index++) {
2722
+ this.preWalkStatement(statements[index]);
2723
+ }
2724
+ }
2725
+
2726
+ /**
2727
+ * Pre walk block statement.
2728
+ * @param {BlockStatement} statement block statement
2729
+ * @deprecated TODO webpack 6: remove, with the `preStatement` hook
2730
+ */
2731
+ preWalkBlockStatement(statement) {
2732
+ this.preWalkStatements(statement.body);
2733
+ }
2734
+
2735
+ /**
2736
+ * Pre walk if statement.
2737
+ * @param {IfStatement} statement if statement
2738
+ * @deprecated TODO webpack 6: remove, with the `preStatement` hook
2739
+ */
2740
+ preWalkIfStatement(statement) {
2741
+ this.preWalkStatement(statement.consequent);
2742
+ if (statement.alternate) {
2743
+ this.preWalkStatement(statement.alternate);
2744
+ }
2745
+ }
2746
+
2747
+ /**
2748
+ * Pre walk labeled statement.
2749
+ * @param {LabeledStatement} statement labeled statement
2750
+ * @deprecated TODO webpack 6: remove, with the `preStatement` hook
2751
+ */
2752
+ preWalkLabeledStatement(statement) {
2753
+ this.preWalkStatement(statement.body);
2754
+ }
2755
+
2756
+ /**
2757
+ * Pre walk with statement.
2758
+ * @param {WithStatement} statement with statement
2759
+ * @deprecated TODO webpack 6: remove, with the `preStatement` hook
2760
+ */
2761
+ preWalkWithStatement(statement) {
2762
+ this.preWalkStatement(statement.body);
2763
+ }
2764
+
2765
+ /**
2766
+ * Pre walk switch statement.
2767
+ * @param {SwitchStatement} statement switch statement
2768
+ * @deprecated TODO webpack 6: remove, with the `preStatement` hook
2769
+ */
2770
+ preWalkSwitchStatement(statement) {
2771
+ this.preWalkSwitchCases(statement.cases);
2772
+ }
2773
+
2774
+ /**
2775
+ * Pre walk switch cases.
2776
+ * @param {SwitchCase[]} switchCases switch cases
2777
+ * @deprecated TODO webpack 6: remove, with the `preStatement` hook
2778
+ */
2779
+ preWalkSwitchCases(switchCases) {
2780
+ for (let index = 0, len = switchCases.length; index < len; index++) {
2781
+ this.preWalkStatements(switchCases[index].consequent);
2782
+ }
2783
+ }
2784
+
2785
+ /**
2786
+ * Pre walk try statement.
2787
+ * @param {TryStatement} statement try statement
2788
+ * @deprecated TODO webpack 6: remove, with the `preStatement` hook
2789
+ */
2790
+ preWalkTryStatement(statement) {
2791
+ this.preWalkStatement(statement.block);
2792
+ if (statement.handler) this.preWalkCatchClause(statement.handler);
2793
+ if (statement.finalizer) this.preWalkStatement(statement.finalizer);
2794
+ }
2795
+
2796
+ /**
2797
+ * Pre walk catch clause.
2798
+ * @param {CatchClause} catchClause catch clause
2799
+ * @deprecated TODO webpack 6: remove, with the `preStatement` hook
2800
+ */
2801
+ preWalkCatchClause(catchClause) {
2802
+ this.preWalkStatement(catchClause.body);
2803
+ }
2804
+
2805
+ /**
2806
+ * Pre walk while statement.
2807
+ * @param {WhileStatement} statement while statement
2808
+ * @deprecated TODO webpack 6: remove, with the `preStatement` hook
2809
+ */
2810
+ preWalkWhileStatement(statement) {
2811
+ this.preWalkStatement(statement.body);
2812
+ }
2813
+
2814
+ /**
2815
+ * Pre walk do while statement.
2816
+ * @param {DoWhileStatement} statement do while statement
2817
+ * @deprecated TODO webpack 6: remove, with the `preStatement` hook
2818
+ */
2819
+ preWalkDoWhileStatement(statement) {
2820
+ this.preWalkStatement(statement.body);
2821
+ }
2822
+
2823
+ /**
2824
+ * Pre walk for statement.
2825
+ * @param {ForStatement} statement for statement
2826
+ * @deprecated TODO webpack 6: remove, with the `preStatement` hook
2827
+ */
2828
+ preWalkForStatement(statement) {
2829
+ if (statement.init && statement.init.type === "VariableDeclaration") {
2830
+ this.preWalkStatement(statement.init);
2831
+ }
2832
+ this.preWalkStatement(statement.body);
2833
+ }
2834
+
2835
+ /**
2836
+ * Pre walk for in statement.
2837
+ * @param {ForInStatement} statement for in statement
2838
+ * @deprecated TODO webpack 6: remove, with the `preStatement` hook
2839
+ */
2840
+ preWalkForInStatement(statement) {
2841
+ if (statement.left.type === "VariableDeclaration") {
2842
+ this.preWalkStatement(statement.left);
2843
+ }
2844
+ this.preWalkStatement(statement.body);
2845
+ }
2846
+
2847
+ /**
2848
+ * Pre walk for of statement. `for await` is reported by the walk, not here.
2849
+ * @param {ForOfStatement} statement for of statement
2850
+ * @deprecated TODO webpack 6: remove, with the `preStatement` hook
2851
+ */
2852
+ preWalkForOfStatement(statement) {
2853
+ if (statement.left.type === "VariableDeclaration") {
2854
+ this.preWalkStatement(statement.left);
2855
+ }
2856
+ this.preWalkStatement(statement.body);
2857
+ }
2858
+
2859
+ /**
2860
+ * Scan every top-level statement for the program's module declarations.
2861
+ * The parser records them now, so nothing inside webpack scans for them.
2862
+ * @deprecated TODO webpack 6: remove
2863
+ * @param {(Statement | ModuleDeclaration)[]} statements statements
2864
+ */
2865
+ modulePreWalkStatements(statements) {
2866
+ /** @type {ModuleDeclarations} */
2867
+ const declarations = [];
2411
2868
  for (let index = 0, len = statements.length; index < len; index++) {
2412
2869
  const statement = statements[index];
2413
- this.preWalkStatement(statement);
2870
+ if (MODULE_STATEMENT_TYPES.has(statement.type)) {
2871
+ declarations.push(
2872
+ statement,
2873
+ index === 0 ? undefined : statements[index - 1]
2874
+ );
2875
+ }
2414
2876
  }
2877
+ this._modulePreWalkDeclarations(declarations);
2415
2878
  }
2879
+ // BACKWARD-COMPAT END
2416
2880
 
2881
+ // BACKWARD-COMPAT START
2417
2882
  /**
2418
- * Block pre walking iterates the scope for block variable declarations
2883
+ * Walk every statement of a list to declare what it binds. Only a tap on the
2884
+ * deprecated `blockPreStatement` hook asks for this.
2419
2885
  * @param {(Statement | ModuleDeclaration)[]} statements statements
2886
+ * @deprecated TODO webpack 6: remove, with the `blockPreStatement` hook
2420
2887
  */
2421
2888
  blockPreWalkStatements(statements) {
2422
2889
  for (let index = 0, len = statements.length; index < len; index++) {
@@ -2424,6 +2891,68 @@ class JavascriptParser extends Parser {
2424
2891
  this.blockPreWalkStatement(statement);
2425
2892
  }
2426
2893
  }
2894
+ // BACKWARD-COMPAT END
2895
+
2896
+ /**
2897
+ * Declare what the statement list binds into its own block scope, before any
2898
+ * of it is walked. The list is what the parser recorded while reading it.
2899
+ * @param {Program | BlockStatement | StaticBlock | SwitchCase} node the node owning the list
2900
+ * @param {(Statement | ModuleDeclaration)[]} statements the list it holds
2901
+ */
2902
+ _blockPreWalkScope(node, statements) {
2903
+ // BACKWARD-COMPAT START
2904
+ if (
2905
+ this._needsFullBlockPreWalk ||
2906
+ this.hooks.blockPreStatement.taps.length !== 0
2907
+ ) {
2908
+ this.blockPreWalkStatements(statements);
2909
+ return;
2910
+ }
2911
+ // BACKWARD-COMPAT END
2912
+ /** @type {BlockDeclarations | undefined} */
2913
+ let recorded;
2914
+ if (this._hasParserRecord) {
2915
+ recorded = /** @type {EXPECTED_ANY} */ (node)[BLOCK_DECLARATIONS];
2916
+ // 72.8% of the lists a build reads declare nothing, and saying so is
2917
+ // the whole pass for them
2918
+ if (recorded === 0) return;
2919
+ }
2920
+ if (recorded === undefined) {
2921
+ recorded = collectBlockDeclarations(statements);
2922
+ if (recorded === 0) return;
2923
+ }
2924
+ this._blockPreWalkDeclarations(statements, recorded);
2925
+ }
2926
+
2927
+ /**
2928
+ * Declare what the recorded statements of the list bind, in source order.
2929
+ * @param {(Statement | ModuleDeclaration)[]} statements the whole list
2930
+ * @param {BlockDeclarations} declarations which of them declare
2931
+ */
2932
+ _blockPreWalkDeclarations(statements, declarations) {
2933
+ if (typeof declarations === "number") {
2934
+ for (let bits = declarations; bits !== 0; bits &= bits - 1) {
2935
+ this._blockPreWalkAt(statements, 31 - Math.clz32(bits & -bits));
2936
+ }
2937
+ return;
2938
+ }
2939
+ for (let index = 0, len = declarations.length; index < len; index++) {
2940
+ this._blockPreWalkAt(statements, declarations[index]);
2941
+ }
2942
+ }
2943
+
2944
+ /**
2945
+ * Declare what one statement of the list binds, with the statement written
2946
+ * before it, so a tap reading `prevStatement` sees what the walk would show.
2947
+ * @param {(Statement | ModuleDeclaration)[]} statements the whole list
2948
+ * @param {number} index which statement declares
2949
+ */
2950
+ _blockPreWalkAt(statements, index) {
2951
+ // the first statement of the list has nothing before it, and leaves
2952
+ // whatever the caller set standing, as walking the whole list did
2953
+ if (index !== 0) this.prevStatement = statements[index - 1];
2954
+ this.blockPreWalkStatement(statements[index]);
2955
+ }
2427
2956
 
2428
2957
  /**
2429
2958
  * Walking iterates the statements and expressions and processes them
@@ -2473,21 +3002,22 @@ class JavascriptParser extends Parser {
2473
3002
  (this.statementPath).pop();
2474
3003
  return;
2475
3004
  }
2476
- // cases ordered by measured statement frequency; the commonest statements
2477
- // (expressions, returns) intentionally match nothing here
3005
+ // a class declaration reaches here for the typed hook alone: defining it
3006
+ // is the block pass's. The rest are only walked into by the deprecated
3007
+ // statement walk, which a tap expecting every statement turns back on.
2478
3008
  switch (statement.type) {
2479
3009
  case "VariableDeclaration":
2480
3010
  this.preWalkVariableDeclaration(statement);
2481
3011
  break;
3012
+ case "FunctionDeclaration":
3013
+ this.preWalkFunctionDeclaration(statement);
3014
+ break;
2482
3015
  case "IfStatement":
2483
3016
  this.preWalkIfStatement(statement);
2484
3017
  break;
2485
3018
  case "BlockStatement":
2486
3019
  this.preWalkBlockStatement(statement);
2487
3020
  break;
2488
- case "FunctionDeclaration":
2489
- this.preWalkFunctionDeclaration(statement);
2490
- break;
2491
3021
  case "ForStatement":
2492
3022
  this.preWalkForStatement(statement);
2493
3023
  break;
@@ -2660,13 +3190,6 @@ class JavascriptParser extends Parser {
2660
3190
  }
2661
3191
 
2662
3192
  // Real Statements
2663
- /**
2664
- * Pre walk block statement.
2665
- * @param {BlockStatement} statement block statement
2666
- */
2667
- preWalkBlockStatement(statement) {
2668
- this.preWalkStatements(statement.body);
2669
- }
2670
3193
 
2671
3194
  /**
2672
3195
  * Walk block statement.
@@ -2676,7 +3199,7 @@ class JavascriptParser extends Parser {
2676
3199
  const oldScope = this._enterBlockScope();
2677
3200
  const body = statement.body;
2678
3201
  const prev = this.prevStatement;
2679
- this.blockPreWalkStatements(body);
3202
+ this._blockPreWalkScope(statement, body);
2680
3203
  this.prevStatement = prev;
2681
3204
  this.walkStatements(body);
2682
3205
 
@@ -2691,17 +3214,6 @@ class JavascriptParser extends Parser {
2691
3214
  this.walkExpression(statement.expression);
2692
3215
  }
2693
3216
 
2694
- /**
2695
- * Pre walk if statement.
2696
- * @param {IfStatement} statement if statement
2697
- */
2698
- preWalkIfStatement(statement) {
2699
- this.preWalkStatement(statement.consequent);
2700
- if (statement.alternate) {
2701
- this.preWalkStatement(statement.alternate);
2702
- }
2703
- }
2704
-
2705
3217
  /**
2706
3218
  * Walks a conditional branch with its guard frame (if any) pushed onto the
2707
3219
  * parser-state guard stack for the duration of the branch body.
@@ -2768,14 +3280,6 @@ class JavascriptParser extends Parser {
2768
3280
  }
2769
3281
  }
2770
3282
 
2771
- /**
2772
- * Pre walk labeled statement.
2773
- * @param {LabeledStatement} statement with statement
2774
- */
2775
- preWalkLabeledStatement(statement) {
2776
- this.preWalkStatement(statement.body);
2777
- }
2778
-
2779
3283
  /**
2780
3284
  * Walk labeled statement.
2781
3285
  * @param {LabeledStatement} statement with statement
@@ -2792,14 +3296,6 @@ class JavascriptParser extends Parser {
2792
3296
  this._exitBlockScope(oldScope, false);
2793
3297
  }
2794
3298
 
2795
- /**
2796
- * Pre walk with statement.
2797
- * @param {WithStatement} statement with statement
2798
- */
2799
- preWalkWithStatement(statement) {
2800
- this.preWalkStatement(statement.body);
2801
- }
2802
-
2803
3299
  /**
2804
3300
  * Walk with statement.
2805
3301
  * @param {WithStatement} statement with statement
@@ -2818,14 +3314,6 @@ class JavascriptParser extends Parser {
2818
3314
  this._exitBlockScope(oldScope, false);
2819
3315
  }
2820
3316
 
2821
- /**
2822
- * Pre walk switch statement.
2823
- * @param {SwitchStatement} statement switch statement
2824
- */
2825
- preWalkSwitchStatement(statement) {
2826
- this.preWalkSwitchCases(statement.cases);
2827
- }
2828
-
2829
3317
  /**
2830
3318
  * Walk switch statement.
2831
3319
  * @param {SwitchStatement} statement switch statement
@@ -2867,16 +3355,6 @@ class JavascriptParser extends Parser {
2867
3355
  this.walkTerminatingStatement(statement);
2868
3356
  }
2869
3357
 
2870
- /**
2871
- * Pre walk try statement.
2872
- * @param {TryStatement} statement try statement
2873
- */
2874
- preWalkTryStatement(statement) {
2875
- this.preWalkStatement(statement.block);
2876
- if (statement.handler) this.preWalkCatchClause(statement.handler);
2877
- if (statement.finalizer) this.preWalkStatement(statement.finalizer);
2878
- }
2879
-
2880
3358
  /**
2881
3359
  * Walk try statement.
2882
3360
  * @param {TryStatement} statement try statement
@@ -2915,14 +3393,6 @@ class JavascriptParser extends Parser {
2915
3393
  }
2916
3394
  }
2917
3395
 
2918
- /**
2919
- * Pre walk while statement.
2920
- * @param {WhileStatement} statement while statement
2921
- */
2922
- preWalkWhileStatement(statement) {
2923
- this.preWalkStatement(statement.body);
2924
- }
2925
-
2926
3396
  /**
2927
3397
  * Walk while statement.
2928
3398
  * @param {WhileStatement} statement while statement
@@ -2935,14 +3405,6 @@ class JavascriptParser extends Parser {
2935
3405
  this._exitBlockScope(oldScope, false);
2936
3406
  }
2937
3407
 
2938
- /**
2939
- * Pre walk do while statement.
2940
- * @param {DoWhileStatement} statement do while statement
2941
- */
2942
- preWalkDoWhileStatement(statement) {
2943
- this.preWalkStatement(statement.body);
2944
- }
2945
-
2946
3408
  /**
2947
3409
  * Walk do while statement.
2948
3410
  * @param {DoWhileStatement} statement do while statement
@@ -2955,17 +3417,6 @@ class JavascriptParser extends Parser {
2955
3417
  this._exitBlockScope(oldScope, false);
2956
3418
  }
2957
3419
 
2958
- /**
2959
- * Pre walk for statement.
2960
- * @param {ForStatement} statement for statement
2961
- */
2962
- preWalkForStatement(statement) {
2963
- if (statement.init && statement.init.type === "VariableDeclaration") {
2964
- this.preWalkStatement(statement.init);
2965
- }
2966
- this.preWalkStatement(statement.body);
2967
- }
2968
-
2969
3420
  /**
2970
3421
  * Walk for statement.
2971
3422
  * @param {ForStatement} statement for statement
@@ -2993,7 +3444,7 @@ class JavascriptParser extends Parser {
2993
3444
  if (body.type === "BlockStatement") {
2994
3445
  // no need to add additional scope
2995
3446
  const prev = this.prevStatement;
2996
- this.blockPreWalkStatements(body.body);
3447
+ this._blockPreWalkScope(body, body.body);
2997
3448
  this.prevStatement = prev;
2998
3449
  this.walkStatements(body.body);
2999
3450
  } else {
@@ -3003,17 +3454,6 @@ class JavascriptParser extends Parser {
3003
3454
  this._exitBlockScope(oldScope, false);
3004
3455
  }
3005
3456
 
3006
- /**
3007
- * Pre walk for in statement.
3008
- * @param {ForInStatement} statement for statement
3009
- */
3010
- preWalkForInStatement(statement) {
3011
- if (statement.left.type === "VariableDeclaration") {
3012
- this.preWalkVariableDeclaration(statement.left);
3013
- }
3014
- this.preWalkStatement(statement.body);
3015
- }
3016
-
3017
3457
  /**
3018
3458
  * Walk for in statement.
3019
3459
  * @param {ForInStatement} statement for statement
@@ -3023,7 +3463,7 @@ class JavascriptParser extends Parser {
3023
3463
  if (statement.left.type === "VariableDeclaration") {
3024
3464
  this.blockPreWalkVariableDeclaration(statement.left);
3025
3465
  this.walkVariableDeclaration(statement.left);
3026
- } else {
3466
+ } else if (!this._walkWriteTarget(statement.left, statement)) {
3027
3467
  this.walkPattern(statement.left);
3028
3468
  }
3029
3469
 
@@ -3034,7 +3474,7 @@ class JavascriptParser extends Parser {
3034
3474
  if (body.type === "BlockStatement") {
3035
3475
  // no need to add additional scope
3036
3476
  const prev = this.prevStatement;
3037
- this.blockPreWalkStatements(body.body);
3477
+ this._blockPreWalkScope(body, body.body);
3038
3478
  this.prevStatement = prev;
3039
3479
  this.walkStatements(body.body);
3040
3480
  } else {
@@ -3044,30 +3484,19 @@ class JavascriptParser extends Parser {
3044
3484
  this._exitBlockScope(oldScope, false);
3045
3485
  }
3046
3486
 
3047
- /**
3048
- * Pre walk for of statement.
3049
- * @param {ForOfStatement} statement statement
3050
- */
3051
- preWalkForOfStatement(statement) {
3052
- if (statement.await && this.scope.topLevelScope === true) {
3053
- this.hooks.topLevelAwait.call(statement);
3054
- }
3055
- if (statement.left.type === "VariableDeclaration") {
3056
- this.preWalkVariableDeclaration(statement.left);
3057
- }
3058
- this.preWalkStatement(statement.body);
3059
- }
3060
-
3061
3487
  /**
3062
3488
  * Walk for of statement.
3063
3489
  * @param {ForOfStatement} statement for statement
3064
3490
  */
3065
3491
  walkForOfStatement(statement) {
3492
+ if (statement.await && this.scope.topLevelScope === true) {
3493
+ this.hooks.topLevelAwait.call(statement);
3494
+ }
3066
3495
  const oldScope = this._enterBlockScope();
3067
3496
  if (statement.left.type === "VariableDeclaration") {
3068
3497
  this.blockPreWalkVariableDeclaration(statement.left);
3069
3498
  this.walkVariableDeclaration(statement.left);
3070
- } else {
3499
+ } else if (!this._walkWriteTarget(statement.left, statement)) {
3071
3500
  this.walkPattern(statement.left);
3072
3501
  }
3073
3502
 
@@ -3078,7 +3507,7 @@ class JavascriptParser extends Parser {
3078
3507
  if (body.type === "BlockStatement") {
3079
3508
  // no need to add additional scope
3080
3509
  const prev = this.prevStatement;
3081
- this.blockPreWalkStatements(body.body);
3510
+ this._blockPreWalkScope(body, body.body);
3082
3511
  this.prevStatement = prev;
3083
3512
  this.walkStatements(body.body);
3084
3513
  } else {
@@ -3120,7 +3549,7 @@ class JavascriptParser extends Parser {
3120
3549
 
3121
3550
  const prev = this.prevStatement;
3122
3551
 
3123
- this.preWalkStatement(statement.body);
3552
+ this._preWalkScope(statement.body);
3124
3553
  this.prevStatement = prev;
3125
3554
  this.walkStatement(statement.body);
3126
3555
 
@@ -3698,17 +4127,6 @@ class JavascriptParser extends Parser {
3698
4127
  this.walkClass(statement);
3699
4128
  }
3700
4129
 
3701
- /**
3702
- * Pre walk switch cases.
3703
- * @param {SwitchCase[]} switchCases switch statement
3704
- */
3705
- preWalkSwitchCases(switchCases) {
3706
- for (let index = 0, len = switchCases.length; index < len; index++) {
3707
- const switchCase = switchCases[index];
3708
- this.preWalkStatements(switchCase.consequent);
3709
- }
3710
- }
3711
-
3712
4130
  /**
3713
4131
  * Processes the provided switch case.
3714
4132
  * @param {SwitchCase[]} switchCases switch statement
@@ -3724,7 +4142,7 @@ class JavascriptParser extends Parser {
3724
4142
 
3725
4143
  if (switchCase.consequent.length > 0) {
3726
4144
  const prev = this.prevStatement;
3727
- this.blockPreWalkStatements(switchCase.consequent);
4145
+ this._blockPreWalkScope(switchCase, switchCase.consequent);
3728
4146
  this.prevStatement = prev;
3729
4147
  }
3730
4148
  }
@@ -3745,14 +4163,6 @@ class JavascriptParser extends Parser {
3745
4163
  this._exitBlockScope(oldScope, false);
3746
4164
  }
3747
4165
 
3748
- /**
3749
- * Pre walk catch clause.
3750
- * @param {CatchClause} catchClause catch clause
3751
- */
3752
- preWalkCatchClause(catchClause) {
3753
- this.preWalkStatement(catchClause.body);
3754
- }
3755
-
3756
4166
  /**
3757
4167
  * Processes the provided catch clause.
3758
4168
  * @param {CatchClause} catchClause catch clause
@@ -4081,7 +4491,7 @@ class JavascriptParser extends Parser {
4081
4491
 
4082
4492
  const prev = this.prevStatement;
4083
4493
 
4084
- this.preWalkStatement(expression.body);
4494
+ this._preWalkScope(expression.body);
4085
4495
  this.prevStatement = prev;
4086
4496
  this.walkStatement(expression.body);
4087
4497
 
@@ -4106,7 +4516,7 @@ class JavascriptParser extends Parser {
4106
4516
  if (expression.body.type === "BlockStatement") {
4107
4517
  this.detectMode(expression.body.body);
4108
4518
  const prev = this.prevStatement;
4109
- this.preWalkStatement(expression.body);
4519
+ this._preWalkScope(expression.body);
4110
4520
  this.prevStatement = prev;
4111
4521
  this.walkStatement(expression.body);
4112
4522
  } else {
@@ -4167,9 +4577,98 @@ class JavascriptParser extends Parser {
4167
4577
  ) {
4168
4578
  this._checkStrictModeAssignTarget(expression.argument.name, expression);
4169
4579
  }
4580
+ if (this._walkWriteTarget(expression.argument, expression)) return;
4170
4581
  this.walkExpression(expression.argument);
4171
4582
  }
4172
4583
 
4584
+ /**
4585
+ * Offers a write that is not an assignment's left side — an update's target
4586
+ * or a loop's binding — to the taps that care, `assign` carrying only an
4587
+ * AssignmentExpression.
4588
+ * @param {Expression | Pattern} target the written reference
4589
+ * @param {WriteStatement} node the construct performing the write
4590
+ * @returns {boolean} true when a tap rendered the write itself
4591
+ */
4592
+ _walkWriteTarget(target, node) {
4593
+ if (target.type === "Identifier") {
4594
+ return Boolean(
4595
+ this._callHooksForName1(this.hooks.write, target.name, node)
4596
+ );
4597
+ }
4598
+ if (target.type.endsWith("Pattern")) {
4599
+ this.enterPattern(/** @type {Pattern} */ (target), (name) => {
4600
+ this._callHooksForName1(this.hooks.write, name, node);
4601
+ });
4602
+ this._walkPatternWriteMembers(target, node);
4603
+ return false;
4604
+ }
4605
+ return Boolean(
4606
+ target.type === "MemberExpression" &&
4607
+ this._callWriteMemberChain(target, node)
4608
+ );
4609
+ }
4610
+
4611
+ /**
4612
+ * Offers the member expressions a destructuring target writes to the
4613
+ * `writeMemberChain` taps, which `enterPattern` passes over because they
4614
+ * bind no name.
4615
+ * @param {Pattern | Expression | null} pattern the destructuring target
4616
+ * @param {WriteStatement} node the construct performing the write
4617
+ * @returns {void}
4618
+ */
4619
+ _walkPatternWriteMembers(pattern, node) {
4620
+ if (!pattern) return;
4621
+ switch (pattern.type) {
4622
+ case "MemberExpression":
4623
+ this._callWriteMemberChain(pattern, node);
4624
+ break;
4625
+ case "ObjectPattern":
4626
+ for (const property of pattern.properties) {
4627
+ this._walkPatternWriteMembers(
4628
+ property.type === "RestElement"
4629
+ ? property.argument
4630
+ : /** @type {Pattern} */ (property.value),
4631
+ node
4632
+ );
4633
+ }
4634
+ break;
4635
+ case "ArrayPattern":
4636
+ for (const element of pattern.elements) {
4637
+ this._walkPatternWriteMembers(element, node);
4638
+ }
4639
+ break;
4640
+ case "AssignmentPattern":
4641
+ this._walkPatternWriteMembers(pattern.left, node);
4642
+ break;
4643
+ case "RestElement":
4644
+ this._walkPatternWriteMembers(pattern.argument, node);
4645
+ break;
4646
+ }
4647
+ }
4648
+
4649
+ /**
4650
+ * Offers a written member expression to the `writeMemberChain` taps.
4651
+ * @param {MemberExpression} target the written member expression
4652
+ * @param {WriteStatement} node the construct performing the write
4653
+ * @returns {boolean} true when a tap rendered the write itself
4654
+ */
4655
+ _callWriteMemberChain(target, node) {
4656
+ return Boolean(
4657
+ this._resolveMemberExpression(target, ALLOWED_MEMBER_TYPES_EXPRESSION) ===
4658
+ 1 &&
4659
+ this._hasHooksForInfo(
4660
+ this.hooks.writeMemberChain,
4661
+ /** @type {string | VariableInfo} */ (this._memberRootInfo)
4662
+ ) &&
4663
+ this.callHooksForInfo(
4664
+ this.hooks.writeMemberChain,
4665
+ /** @type {string | VariableInfo} */ (this._memberRootInfo),
4666
+ node,
4667
+ this._members
4668
+ )
4669
+ );
4670
+ }
4671
+
4173
4672
  /**
4174
4673
  * Reports `arguments.callee` / `arguments.caller` — both throw a TypeError
4175
4674
  * once the module runs in strict-mode ESM output.
@@ -4418,6 +4917,7 @@ class JavascriptParser extends Parser {
4418
4917
  this.defineVariable(name);
4419
4918
  }
4420
4919
  });
4920
+ this._walkPatternWriteMembers(expression.left, expression);
4421
4921
  this.walkPattern(expression.left);
4422
4922
  } else if (expression.left.type === "MemberExpression") {
4423
4923
  if (
@@ -4641,7 +5141,7 @@ class JavascriptParser extends Parser {
4641
5141
  if (functionExpression.body.type === "BlockStatement") {
4642
5142
  this.detectMode(functionExpression.body.body);
4643
5143
  const prev = this.prevStatement;
4644
- this.preWalkStatement(functionExpression.body);
5144
+ this._preWalkScope(functionExpression.body);
4645
5145
  this.prevStatement = prev;
4646
5146
  this.walkStatement(functionExpression.body);
4647
5147
  } else {
@@ -4931,11 +5431,9 @@ class JavascriptParser extends Parser {
4931
5431
  onUnhandled
4932
5432
  ) {
4933
5433
  if (expression.object.type === "MemberExpression") {
4934
- // optimize the case where expression.object is a MemberExpression too.
4935
- // we can keep info here when calling walkMemberExpression directly
4936
- // Read the property from `members` (already extracted by
4937
- // extractMemberExpressionChain) since the AST node may be a
4938
- // TemplateLiteral, which has neither .name nor .value.
5434
+ // A nested MemberExpression keeps its info by calling walkMemberExpression
5435
+ // directly. The property comes from `members` rather than the node, which
5436
+ // may be a TemplateLiteral and carry neither `.name` nor `.value`.
4939
5437
  const property = members[members.length - 1];
4940
5438
  name = name.slice(0, -property.length - 1);
4941
5439
  members.pop();
@@ -5716,6 +6214,13 @@ class JavascriptParser extends Parser {
5716
6214
  const oldStrictInModuleOutput = this._strictInModuleOutput;
5717
6215
  const oldSource = this._source;
5718
6216
  const oldLineStarts = this._lineStarts;
6217
+ const oldHasParserRecord = this._hasParserRecord;
6218
+ // WHY: the parser's record of each scope is only the truth for a tree it
6219
+ // just built. A loader's AST, and whatever `module.parser.javascript.parse`
6220
+ // returns, may have been edited after it was read — or built by something
6221
+ // else entirely — so both are collected from instead of trusted.
6222
+ this._hasParserRecord =
6223
+ typeof source !== "object" && typeof this.options.parse !== "function";
5719
6224
  // a preparsed AST carries no text, but the build passes the module
5720
6225
  // source in the state — offsets still map for it
5721
6226
  this._source =
@@ -5763,11 +6268,13 @@ class JavascriptParser extends Parser {
5763
6268
  if (this._strictInModuleOutput && this.scope.isStrict) {
5764
6269
  this._strictInModuleOutput = false;
5765
6270
  }
5766
- this.modulePreWalkStatements(ast.body);
6271
+ this._modulePreWalkDeclarations(
6272
+ moduleDeclarationsOf(ast, this._hasParserRecord)
6273
+ );
5767
6274
  this.prevStatement = undefined;
5768
- this.preWalkStatements(ast.body);
6275
+ this._preWalkScope(ast);
5769
6276
  this.prevStatement = undefined;
5770
- this.blockPreWalkStatements(ast.body);
6277
+ this._blockPreWalkScope(ast, ast.body);
5771
6278
  this.prevStatement = undefined;
5772
6279
  this.walkStatements(ast.body);
5773
6280
  this.destructuringAssignmentProperties = undefined;
@@ -5784,6 +6291,7 @@ class JavascriptParser extends Parser {
5784
6291
  // release — these retain the whole source string and its line table
5785
6292
  this._source = oldSource;
5786
6293
  this._lineStarts = oldLineStarts;
6294
+ this._hasParserRecord = oldHasParserRecord;
5787
6295
  return state;
5788
6296
  }
5789
6297
 
@@ -5902,10 +6410,9 @@ class JavascriptParser extends Parser {
5902
6410
  });
5903
6411
  }
5904
6412
  case "TemplateLiteral":
5905
- // Thread `commentsStartPos` through the interpolations so a
5906
- // /*#__PURE__*/ comment that sits inside `${ ... }` (or before
5907
- // the first interpolation) is part of the scanned range when
5908
- // the inner expression's purity is evaluated.
6413
+ // Thread `commentsStartPos` through the interpolations, so a
6414
+ // `/*#__PURE__*/` inside `${ … }`, or before the first one, is within
6415
+ // the range scanned when the inner expression's purity is judged.
5909
6416
  return expr.expressions.every((e) => {
5910
6417
  const pureFlag = this.isPure(e, commentsStartPos);
5911
6418
  commentsStartPos = /** @type {Range} */ (e.range)[1];
@@ -5959,17 +6466,13 @@ class JavascriptParser extends Parser {
5959
6466
  return this.isPure(expr.expression, commentsStartPos);
5960
6467
 
5961
6468
  case "UnaryExpression":
5962
- // Safe unary operators — produce their result without invoking
5963
- // user code on the operand:
5964
- // - `typeof` returns a type tag and never throws, even for
5965
- // undeclared identifiers; no coercion.
5966
- // - `void` evaluates the operand and discards it, returning
5967
- // `undefined`; pure iff the operand is pure.
5968
- // - `!` coerces via ToBoolean, which is defined to not call
5969
- // any user code (objects → true, etc.).
5970
- // Other operators (`+`, `-`, `~`, `delete`) fall through to
5971
- // the generic evaluator which can still recognize literal
5972
- // cases (e.g. `-1`, `+5`).
6469
+ // WHY: these three produce their result without invoking user code on
6470
+ // the operand. `typeof` returns a type tag and never throws, even for an
6471
+ // undeclared identifier, and coerces nothing; `void` evaluates the
6472
+ // operand and discards it, so it is pure exactly when the operand is; and
6473
+ // `!` coerces through ToBoolean, which is defined to call no user code.
6474
+ // `+`, `-`, `~` and `delete` fall through to the generic evaluator, which
6475
+ // still recognizes literal cases such as `-1` and `+5`.
5973
6476
  if (
5974
6477
  expr.operator === "typeof" ||
5975
6478
  expr.operator === "void" ||
@@ -5983,12 +6486,12 @@ class JavascriptParser extends Parser {
5983
6486
  return true;
5984
6487
 
5985
6488
  case "BinaryExpression":
5986
- // Strict (in)equality compares without coercion and never invokes
5987
- // user code on its operands, so the result is pure iff both sides
5988
- // are pure. All other binary operators may invoke `valueOf` /
5989
- // `toString` / `[Symbol.hasInstance]` / Proxy traps and fall through
5990
- // to the generic evaluator, which can still recognize the cases
5991
- // where both sides evaluate to known primitive literals.
6489
+ // WHY: strict (in)equality compares without coercion and never invokes user
6490
+ // code on its operands, so the result is pure where both sides are. Every
6491
+ // other binary operator may invoke `valueOf`, `toString`,
6492
+ // `[Symbol.hasInstance]` or a Proxy trap, and falls through to the generic
6493
+ // evaluator, which still recognizes the cases where both sides evaluate
6494
+ // to known primitive literals.
5992
6495
  if (expr.operator === "===" || expr.operator === "!==") {
5993
6496
  return (
5994
6497
  this.isPure(expr.left, commentsStartPos) &&
@@ -6145,6 +6648,46 @@ class JavascriptParser extends Parser {
6145
6648
  );
6146
6649
  }
6147
6650
 
6651
+ /**
6652
+ * Where the comments written for `statement` begin: the run of comments
6653
+ * before it that whitespace alone separates from it and from each other.
6654
+ * @param {StatementPathItem} statement the statement they belong to
6655
+ * @returns {number} the offset the run starts at, or the statement's own
6656
+ */
6657
+ getAttachedCommentsStart(statement) {
6658
+ let position = /** @type {Range} */ (statement.range)[0];
6659
+ // what precedes `export function f() {}` is written for the export, and
6660
+ // the export keyword is the token the run would otherwise stop at
6661
+ const path = this.statementPath;
6662
+ if (path !== undefined && path.length > 1) {
6663
+ const outer = path[path.length - 2];
6664
+ if (
6665
+ (outer.type === "ExportNamedDeclaration" ||
6666
+ outer.type === "ExportDefaultDeclaration") &&
6667
+ outer.declaration === statement
6668
+ ) {
6669
+ position = /** @type {Range} */ (outer.range)[0];
6670
+ }
6671
+ }
6672
+ const comments = this.comments;
6673
+ if (comments === undefined) return position;
6674
+ const source = this._source;
6675
+ for (let index = comments.length - 1; index >= 0; index--) {
6676
+ const range = /** @type {Range} */ (comments[index].range);
6677
+ if (range[1] > position) continue;
6678
+ // with no source to read, nothing can be ruled out, so the run is
6679
+ // taken to reach as far as the comments themselves do
6680
+ if (
6681
+ source !== undefined &&
6682
+ !onlyWhitespaceBetween(source, range[1], position)
6683
+ ) {
6684
+ break;
6685
+ }
6686
+ position = range[0];
6687
+ }
6688
+ return position;
6689
+ }
6690
+
6148
6691
  /**
6149
6692
  * Reports whether `identifier` may occur as a word in the source text of
6150
6693
  * `range`. A conservative pre-filter for expensive AST scans: `false` means