webpack 5.45.1 → 5.48.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.

Potentially problematic release.


This version of webpack might be problematic. Click here for more details.

Files changed (31) hide show
  1. package/bin/webpack.js +0 -0
  2. package/hot/only-dev-server.js +1 -1
  3. package/hot/poll.js +1 -1
  4. package/hot/signal.js +1 -1
  5. package/lib/NormalModule.js +14 -8
  6. package/lib/NormalModuleFactory.js +8 -2
  7. package/lib/Template.js +1 -4
  8. package/lib/asset/AssetGenerator.js +1 -1
  9. package/lib/asset/AssetModulesPlugin.js +23 -18
  10. package/lib/config/defaults.js +18 -12
  11. package/lib/dependencies/AMDRequireDependency.js +2 -8
  12. package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +6 -3
  13. package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +4 -2
  14. package/lib/dependencies/HarmonyImportDependency.js +5 -1
  15. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +40 -5
  16. package/lib/dependencies/HarmonyImportSideEffectDependency.js +2 -2
  17. package/lib/dependencies/HarmonyImportSpecifierDependency.js +10 -2
  18. package/lib/dependencies/ModuleDependency.js +8 -1
  19. package/lib/hmr/HotModuleReplacement.runtime.js +66 -60
  20. package/lib/javascript/ArrayPushCallbackChunkFormatPlugin.js +2 -8
  21. package/lib/javascript/JavascriptModulesPlugin.js +48 -29
  22. package/lib/javascript/JavascriptParser.js +14 -9
  23. package/lib/rules/{DescriptionDataMatcherRulePlugin.js → ObjectMatcherRulePlugin.js} +14 -10
  24. package/lib/stats/DefaultStatsFactoryPlugin.js +32 -2
  25. package/lib/stats/DefaultStatsPresetPlugin.js +6 -2
  26. package/lib/stats/DefaultStatsPrinterPlugin.js +51 -9
  27. package/lib/wasm-async/AsyncWebAssemblyModulesPlugin.js +2 -2
  28. package/package.json +3 -2
  29. package/schemas/WebpackOptions.check.js +1 -1
  30. package/schemas/WebpackOptions.json +15 -0
  31. package/types.d.ts +98 -11
package/bin/webpack.js CHANGED
File without changes
@@ -13,7 +13,7 @@ if (module.hot) {
13
13
  module.hot
14
14
  .check()
15
15
  .then(function (updatedModules) {
16
- if (!updatedModules) {
16
+ if (!updatedModules || updatedModules.length === 0) {
17
17
  log("warning", "[HMR] Cannot find update. Need to do a full reload!");
18
18
  log(
19
19
  "warning",
package/hot/poll.js CHANGED
@@ -12,7 +12,7 @@ if (module.hot) {
12
12
  module.hot
13
13
  .check(true)
14
14
  .then(function (updatedModules) {
15
- if (!updatedModules) {
15
+ if (!updatedModules || updatedModules.length === 0) {
16
16
  if (fromUpdate) log("info", "[HMR] Update applied.");
17
17
  return;
18
18
  }
package/hot/signal.js CHANGED
@@ -9,7 +9,7 @@ if (module.hot) {
9
9
  module.hot
10
10
  .check()
11
11
  .then(function (updatedModules) {
12
- if (!updatedModules) {
12
+ if (!updatedModules || updatedModules.length === 0) {
13
13
  if (fromUpdate) log("info", "[HMR] Update applied.");
14
14
  else log("warning", "[HMR] Cannot find update.");
15
15
  return;
@@ -293,9 +293,13 @@ class NormalModule extends Module {
293
293
  */
294
294
  identifier() {
295
295
  if (this.layer === null) {
296
- return this.request;
296
+ if (this.type === "javascript/auto") {
297
+ return this.request;
298
+ } else {
299
+ return `${this.type}|${this.request}`;
300
+ }
297
301
  } else {
298
- return `${this.request}|${this.layer}`;
302
+ return `${this.type}|${this.request}|${this.layer}`;
299
303
  }
300
304
  }
301
305
 
@@ -350,18 +354,20 @@ class NormalModule extends Module {
350
354
  this.resource = m.resource;
351
355
  this.matchResource = m.matchResource;
352
356
  this.loaders = m.loaders;
353
- this._sourceTypes = m._sourceTypes;
354
- this._sourceSizes = m._sourceSizes;
355
357
  }
356
358
 
357
359
  /**
358
360
  * Assuming this module is in the cache. Remove internal references to allow freeing some memory.
359
361
  */
360
362
  cleanupForCache() {
361
- // Make sure to cache types and sizes before cleanup
362
- if (this._sourceTypes === undefined) this.getSourceTypes();
363
- for (const type of this._sourceTypes) {
364
- this.size(type);
363
+ // Make sure to cache types and sizes before cleanup when this module has been built
364
+ // They are accessed by the stats and we don't want them to crash after cleanup
365
+ // TODO reconsider this for webpack 6
366
+ if (this.buildInfo) {
367
+ if (this._sourceTypes === undefined) this.getSourceTypes();
368
+ for (const type of this._sourceTypes) {
369
+ this.size(type);
370
+ }
365
371
  }
366
372
  super.cleanupForCache();
367
373
  this.parser = undefined;
@@ -20,7 +20,7 @@ const ModuleGraph = require("./ModuleGraph");
20
20
  const NormalModule = require("./NormalModule");
21
21
  const BasicEffectRulePlugin = require("./rules/BasicEffectRulePlugin");
22
22
  const BasicMatcherRulePlugin = require("./rules/BasicMatcherRulePlugin");
23
- const DescriptionDataMatcherRulePlugin = require("./rules/DescriptionDataMatcherRulePlugin");
23
+ const ObjectMatcherRulePlugin = require("./rules/ObjectMatcherRulePlugin");
24
24
  const RuleSetCompiler = require("./rules/RuleSetCompiler");
25
25
  const UseEffectRulePlugin = require("./rules/UseEffectRulePlugin");
26
26
  const LazySet = require("./util/LazySet");
@@ -44,6 +44,7 @@ const { parseResource } = require("./util/identifier");
44
44
  * @property {ModuleFactoryCreateData["resolveOptions"]} resolveOptions
45
45
  * @property {string} context
46
46
  * @property {string} request
47
+ * @property {Record<string, any> | undefined} assertions
47
48
  * @property {ModuleDependency[]} dependencies
48
49
  * @property {Object} createData
49
50
  * @property {LazySet<string>} fileDependencies
@@ -182,7 +183,8 @@ const ruleSetCompiler = new RuleSetCompiler([
182
183
  new BasicMatcherRulePlugin("issuer"),
183
184
  new BasicMatcherRulePlugin("compiler"),
184
185
  new BasicMatcherRulePlugin("issuerLayer"),
185
- new DescriptionDataMatcherRulePlugin(),
186
+ new ObjectMatcherRulePlugin("assert", "assertions"),
187
+ new ObjectMatcherRulePlugin("descriptionData"),
186
188
  new BasicEffectRulePlugin("type"),
187
189
  new BasicEffectRulePlugin("sideEffects"),
188
190
  new BasicEffectRulePlugin("parser"),
@@ -339,6 +341,7 @@ class NormalModuleFactory extends ModuleFactory {
339
341
  context,
340
342
  dependencies,
341
343
  request,
344
+ assertions,
342
345
  resolveOptions,
343
346
  fileDependencies,
344
347
  missingDependencies,
@@ -447,6 +450,7 @@ class NormalModuleFactory extends ModuleFactory {
447
450
  resourceQuery: resourceDataForRules.query,
448
451
  resourceFragment: resourceDataForRules.fragment,
449
452
  scheme,
453
+ assertions,
450
454
  mimetype: matchResourceData ? "" : resourceData.data.mimetype || "",
451
455
  dependency: dependencyType,
452
456
  descriptionData: matchResourceData
@@ -694,6 +698,7 @@ class NormalModuleFactory extends ModuleFactory {
694
698
  const resolveOptions = data.resolveOptions || EMPTY_RESOLVE_OPTIONS;
695
699
  const dependency = dependencies[0];
696
700
  const request = dependency.request;
701
+ const assertions = dependency.assertions;
697
702
  const contextInfo = data.contextInfo;
698
703
  const fileDependencies = new LazySet();
699
704
  const missingDependencies = new LazySet();
@@ -704,6 +709,7 @@ class NormalModuleFactory extends ModuleFactory {
704
709
  resolveOptions,
705
710
  context,
706
711
  request,
712
+ assertions,
707
713
  dependencies,
708
714
  fileDependencies,
709
715
  missingDependencies,
package/lib/Template.js CHANGED
@@ -350,7 +350,7 @@ class Template {
350
350
 
351
351
  /**
352
352
  * @param {RuntimeModule[]} runtimeModules array of runtime modules in order
353
- * @param {RenderContext & { codeGenerationResults?: CodeGenerationResults, useStrict?: boolean }} renderContext render context
353
+ * @param {RenderContext & { codeGenerationResults?: CodeGenerationResults }} renderContext render context
354
354
  * @returns {Source} rendered runtime modules in a Source object
355
355
  */
356
356
  static renderRuntimeModules(runtimeModules, renderContext) {
@@ -382,12 +382,10 @@ class Template {
382
382
  source.add("\n\n");
383
383
  } else if (renderContext.runtimeTemplate.supportsArrowFunction()) {
384
384
  source.add("(() => {\n");
385
- if (renderContext.useStrict) source.add('\t"use strict";\n');
386
385
  source.add(new PrefixSource("\t", runtimeSource));
387
386
  source.add("\n})();\n\n");
388
387
  } else {
389
388
  source.add("!function() {\n");
390
- if (renderContext.useStrict) source.add('\t"use strict";\n');
391
389
  source.add(new PrefixSource("\t", runtimeSource));
392
390
  source.add("\n}();\n\n");
393
391
  }
@@ -406,7 +404,6 @@ class Template {
406
404
  "/******/ ",
407
405
  new ConcatSource(
408
406
  "function(__webpack_require__) { // webpackRuntimeModules\n",
409
- '"use strict";\n\n',
410
407
  this.renderRuntimeModules(runtimeModules, renderContext),
411
408
  "}\n"
412
409
  )
@@ -229,7 +229,7 @@ class AssetGenerator extends Generator {
229
229
  }
230
230
  );
231
231
  let publicPath;
232
- if (this.publicPath) {
232
+ if (this.publicPath !== undefined) {
233
233
  const { path, info } =
234
234
  runtimeTemplate.compilation.getAssetPathWithInfo(
235
235
  this.publicPath,
@@ -172,24 +172,29 @@ class AssetModulesPlugin {
172
172
  );
173
173
  if (modules) {
174
174
  for (const module of modules) {
175
- const codeGenResult = codeGenerationResults.get(
176
- module,
177
- chunk.runtime
178
- );
179
- result.push({
180
- render: () => codeGenResult.sources.get(type),
181
- filename:
182
- module.buildInfo.filename ||
183
- codeGenResult.data.get("filename"),
184
- info:
185
- module.buildInfo.assetInfo ||
186
- codeGenResult.data.get("assetInfo"),
187
- auxiliary: true,
188
- identifier: `assetModule${chunkGraph.getModuleId(module)}`,
189
- hash:
190
- module.buildInfo.fullContentHash ||
191
- codeGenResult.data.get("fullContentHash")
192
- });
175
+ try {
176
+ const codeGenResult = codeGenerationResults.get(
177
+ module,
178
+ chunk.runtime
179
+ );
180
+ result.push({
181
+ render: () => codeGenResult.sources.get(type),
182
+ filename:
183
+ module.buildInfo.filename ||
184
+ codeGenResult.data.get("filename"),
185
+ info:
186
+ module.buildInfo.assetInfo ||
187
+ codeGenResult.data.get("assetInfo"),
188
+ auxiliary: true,
189
+ identifier: `assetModule${chunkGraph.getModuleId(module)}`,
190
+ hash:
191
+ module.buildInfo.fullContentHash ||
192
+ codeGenResult.data.get("fullContentHash")
193
+ });
194
+ } catch (e) {
195
+ e.message += `\nduring rendering of asset ${module.identifier()}`;
196
+ throw e;
197
+ }
193
198
  }
194
199
  }
195
200
 
@@ -482,18 +482,6 @@ const applyModuleDefaults = (
482
482
  or: ["text/javascript", "application/javascript"]
483
483
  },
484
484
  ...esm
485
- },
486
- {
487
- dependency: "url",
488
- oneOf: [
489
- {
490
- scheme: /^data$/,
491
- type: "asset/inline"
492
- },
493
- {
494
- type: "asset/resource"
495
- }
496
- ]
497
485
  }
498
486
  ];
499
487
  if (asyncWebAssembly) {
@@ -541,6 +529,24 @@ const applyModuleDefaults = (
541
529
  ...wasm
542
530
  });
543
531
  }
532
+ rules.push(
533
+ {
534
+ dependency: "url",
535
+ oneOf: [
536
+ {
537
+ scheme: /^data$/,
538
+ type: "asset/inline"
539
+ },
540
+ {
541
+ type: "asset/resource"
542
+ }
543
+ ]
544
+ },
545
+ {
546
+ assert: { type: "json" },
547
+ type: "json"
548
+ }
549
+ );
544
550
  return rules;
545
551
  });
546
552
  };
@@ -123,10 +123,7 @@ AMDRequireDependency.Template = class AMDRequireDependencyTemplate extends (
123
123
 
124
124
  source.replace(dep.outerRange[0], dep.arrayRange[0] - 1, startBlock);
125
125
 
126
- source.insert(
127
- dep.arrayRange[0] + 0.9,
128
- "var __WEBPACK_AMD_REQUIRE_ARRAY__ = "
129
- );
126
+ source.insert(dep.arrayRange[0], "var __WEBPACK_AMD_REQUIRE_ARRAY__ = ");
130
127
 
131
128
  source.replace(dep.arrayRange[1], dep.functionRange[0] - 1, "; (");
132
129
 
@@ -160,10 +157,7 @@ AMDRequireDependency.Template = class AMDRequireDependencyTemplate extends (
160
157
 
161
158
  source.replace(dep.outerRange[0], dep.arrayRange[0] - 1, startBlock);
162
159
 
163
- source.insert(
164
- dep.arrayRange[0] + 0.9,
165
- "var __WEBPACK_AMD_REQUIRE_ARRAY__ = "
166
- );
160
+ source.insert(dep.arrayRange[0], "var __WEBPACK_AMD_REQUIRE_ARRAY__ = ");
167
161
 
168
162
  source.replace(dep.arrayRange[1], dep.functionRange[0] - 1, "; (");
169
163
 
@@ -12,7 +12,8 @@ const HarmonyExportHeaderDependency = require("./HarmonyExportHeaderDependency")
12
12
  const HarmonyExportImportedSpecifierDependency = require("./HarmonyExportImportedSpecifierDependency");
13
13
  const HarmonyExportSpecifierDependency = require("./HarmonyExportSpecifierDependency");
14
14
  const {
15
- harmonySpecifierTag
15
+ harmonySpecifierTag,
16
+ getAssertions
16
17
  } = require("./HarmonyImportDependencyParserPlugin");
17
18
  const HarmonyImportSideEffectDependency = require("./HarmonyImportSideEffectDependency");
18
19
 
@@ -48,7 +49,8 @@ module.exports = class HarmonyExportDependencyParserPlugin {
48
49
  parser.state.module.addPresentationalDependency(clearDep);
49
50
  const sideEffectDep = new HarmonyImportSideEffectDependency(
50
51
  source,
51
- parser.state.lastHarmonyImportOrder
52
+ parser.state.lastHarmonyImportOrder,
53
+ getAssertions(statement)
52
54
  );
53
55
  sideEffectDep.loc = Object.create(statement.loc);
54
56
  sideEffectDep.loc.index = -1;
@@ -127,7 +129,8 @@ module.exports = class HarmonyExportDependencyParserPlugin {
127
129
  harmonyNamedExports,
128
130
  null,
129
131
  this.strictExportPresence,
130
- null
132
+ null,
133
+ settings.assertions
131
134
  );
132
135
  } else {
133
136
  dep = new HarmonyExportSpecifierDependency(id, name);
@@ -159,6 +159,7 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
159
159
  * @param {ReadonlyArray<HarmonyExportImportedSpecifierDependency> | Iterable<HarmonyExportImportedSpecifierDependency>} otherStarExports other star exports in the module before this import
160
160
  * @param {boolean} strictExportPresence when true, missing exports in the imported module lead to errors instead of warnings
161
161
  * @param {HarmonyStarExportsList} allStarExports all star exports in the module
162
+ * @param {Record<string, any>=} assertions import assertions
162
163
  */
163
164
  constructor(
164
165
  request,
@@ -168,9 +169,10 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
168
169
  activeExports,
169
170
  otherStarExports,
170
171
  strictExportPresence,
171
- allStarExports
172
+ allStarExports,
173
+ assertions
172
174
  ) {
173
- super(request, sourceOrder);
175
+ super(request, sourceOrder, assertions);
174
176
 
175
177
  this.ids = ids;
176
178
  this.name = name;
@@ -32,10 +32,12 @@ class HarmonyImportDependency extends ModuleDependency {
32
32
  *
33
33
  * @param {string} request request string
34
34
  * @param {number} sourceOrder source order
35
+ * @param {Record<string, any>=} assertions import assertions
35
36
  */
36
- constructor(request, sourceOrder) {
37
+ constructor(request, sourceOrder, assertions) {
37
38
  super(request);
38
39
  this.sourceOrder = sourceOrder;
40
+ this.assertions = assertions;
39
41
  }
40
42
 
41
43
  get category() {
@@ -201,12 +203,14 @@ class HarmonyImportDependency extends ModuleDependency {
201
203
  serialize(context) {
202
204
  const { write } = context;
203
205
  write(this.sourceOrder);
206
+ write(this.assertions);
204
207
  super.serialize(context);
205
208
  }
206
209
 
207
210
  deserialize(context) {
208
211
  const { read } = context;
209
212
  this.sourceOrder = read();
213
+ this.assertions = read();
210
214
  super.deserialize(context);
211
215
  }
212
216
  }
@@ -14,7 +14,11 @@ const HarmonyExports = require("./HarmonyExports");
14
14
  const HarmonyImportSideEffectDependency = require("./HarmonyImportSideEffectDependency");
15
15
  const HarmonyImportSpecifierDependency = require("./HarmonyImportSpecifierDependency");
16
16
 
17
+ /** @typedef {import("estree").ExportAllDeclaration} ExportAllDeclaration */
18
+ /** @typedef {import("estree").ExportNamedDeclaration} ExportNamedDeclaration */
17
19
  /** @typedef {import("estree").Identifier} Identifier */
20
+ /** @typedef {import("estree").ImportDeclaration} ImportDeclaration */
21
+ /** @typedef {import("estree").ImportExpression} ImportExpression */
18
22
  /** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
19
23
  /** @typedef {import("../optimize/InnerGraph").InnerGraph} InnerGraph */
20
24
  /** @typedef {import("../optimize/InnerGraph").TopLevelSymbol} TopLevelSymbol */
@@ -29,8 +33,32 @@ const harmonySpecifierTag = Symbol("harmony import");
29
33
  * @property {number} sourceOrder
30
34
  * @property {string} name
31
35
  * @property {boolean} await
36
+ * @property {Record<string, any> | undefined} assertions
32
37
  */
33
38
 
39
+ /**
40
+ * @param {ImportDeclaration | ExportNamedDeclaration | ExportAllDeclaration | ImportExpression} node node with assertions
41
+ * @returns {Record<string, any> | undefined} assertions
42
+ */
43
+ function getAssertions(node) {
44
+ // TODO remove cast when @types/estree has been updated to import assertions
45
+ const assertions = /** @type {{ assertions?: ImportAttributeNode[] }} */ (
46
+ node
47
+ ).assertions;
48
+ if (assertions === undefined) {
49
+ return undefined;
50
+ }
51
+ const result = {};
52
+ for (const assertion of assertions) {
53
+ const key =
54
+ assertion.key.type === "Identifier"
55
+ ? assertion.key.name
56
+ : assertion.key.value;
57
+ result[key] = assertion.value.value;
58
+ }
59
+ return result;
60
+ }
61
+
34
62
  module.exports = class HarmonyImportDependencyParserPlugin {
35
63
  constructor(options) {
36
64
  this.strictExportPresence = options.strictExportPresence;
@@ -65,9 +93,11 @@ module.exports = class HarmonyImportDependencyParserPlugin {
65
93
  clearDep.loc = statement.loc;
66
94
  parser.state.module.addPresentationalDependency(clearDep);
67
95
  parser.unsetAsiPosition(statement.range[1]);
96
+ const assertions = getAssertions(statement);
68
97
  const sideEffectDep = new HarmonyImportSideEffectDependency(
69
98
  source,
70
- parser.state.lastHarmonyImportOrder
99
+ parser.state.lastHarmonyImportOrder,
100
+ assertions
71
101
  );
72
102
  sideEffectDep.loc = statement.loc;
73
103
  parser.state.module.addDependency(sideEffectDep);
@@ -82,7 +112,8 @@ module.exports = class HarmonyImportDependencyParserPlugin {
82
112
  name,
83
113
  source,
84
114
  ids,
85
- sourceOrder: parser.state.lastHarmonyImportOrder
115
+ sourceOrder: parser.state.lastHarmonyImportOrder,
116
+ assertions: getAssertions(statement)
86
117
  });
87
118
  return true;
88
119
  }
@@ -97,7 +128,8 @@ module.exports = class HarmonyImportDependencyParserPlugin {
97
128
  settings.ids,
98
129
  settings.name,
99
130
  expr.range,
100
- this.strictExportPresence
131
+ this.strictExportPresence,
132
+ settings.assertions
101
133
  );
102
134
  dep.shorthand = parser.scope.inShorthand;
103
135
  dep.directImport = true;
@@ -118,7 +150,8 @@ module.exports = class HarmonyImportDependencyParserPlugin {
118
150
  ids,
119
151
  settings.name,
120
152
  expr.range,
121
- this.strictExportPresence
153
+ this.strictExportPresence,
154
+ settings.assertions
122
155
  );
123
156
  dep.asiSafe = !parser.isAsiPosition(expr.range[0]);
124
157
  dep.loc = expr.loc;
@@ -138,7 +171,8 @@ module.exports = class HarmonyImportDependencyParserPlugin {
138
171
  ids,
139
172
  settings.name,
140
173
  callee.range,
141
- this.strictExportPresence
174
+ this.strictExportPresence,
175
+ settings.assertions
142
176
  );
143
177
  dep.directImport = members.length === 0;
144
178
  dep.call = true;
@@ -206,3 +240,4 @@ module.exports = class HarmonyImportDependencyParserPlugin {
206
240
  };
207
241
 
208
242
  module.exports.harmonySpecifierTag = harmonySpecifierTag;
243
+ module.exports.getAssertions = getAssertions;
@@ -20,8 +20,8 @@ const HarmonyImportDependency = require("./HarmonyImportDependency");
20
20
  /** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
21
21
 
22
22
  class HarmonyImportSideEffectDependency extends HarmonyImportDependency {
23
- constructor(request, sourceOrder) {
24
- super(request, sourceOrder);
23
+ constructor(request, sourceOrder, assertions) {
24
+ super(request, sourceOrder, assertions);
25
25
  }
26
26
 
27
27
  get type() {
@@ -29,8 +29,16 @@ const HarmonyImportDependency = require("./HarmonyImportDependency");
29
29
  const idsSymbol = Symbol("HarmonyImportSpecifierDependency.ids");
30
30
 
31
31
  class HarmonyImportSpecifierDependency extends HarmonyImportDependency {
32
- constructor(request, sourceOrder, ids, name, range, strictExportPresence) {
33
- super(request, sourceOrder);
32
+ constructor(
33
+ request,
34
+ sourceOrder,
35
+ ids,
36
+ name,
37
+ range,
38
+ strictExportPresence,
39
+ assertions
40
+ ) {
41
+ super(request, sourceOrder, assertions);
34
42
  this.ids = ids;
35
43
  this.name = name;
36
44
  this.range = range;
@@ -22,13 +22,20 @@ class ModuleDependency extends Dependency {
22
22
  this.request = request;
23
23
  this.userRequest = request;
24
24
  this.range = undefined;
25
+ // assertions must be serialized by subclasses that use it
26
+ /** @type {Record<string, any> | undefined} */
27
+ this.assertions = undefined;
25
28
  }
26
29
 
27
30
  /**
28
31
  * @returns {string | null} an identifier to merge equal requests
29
32
  */
30
33
  getResourceIdentifier() {
31
- return `module${this.request}`;
34
+ let str = `module${this.request}`;
35
+ if (this.assertions !== undefined) {
36
+ str += JSON.stringify(this.assertions);
37
+ }
38
+ return str;
32
39
  }
33
40
 
34
41
  /**