webpack 4.35.2 → 4.37.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.
@@ -75,6 +75,16 @@ export type ExternalItem =
75
75
  * via the `definition` "ArrayOfStringValues".
76
76
  */
77
77
  export type ArrayOfStringValues = string[];
78
+ /**
79
+ * This interface was referenced by `WebpackOptions`'s JSON-Schema
80
+ * via the `definition` "FilterTypes".
81
+ */
82
+ export type FilterTypes = FilterItemTypes | FilterItemTypes[];
83
+ /**
84
+ * This interface was referenced by `WebpackOptions`'s JSON-Schema
85
+ * via the `definition` "FilterItemTypes".
86
+ */
87
+ export type FilterItemTypes = RegExp | string | ((value: string) => boolean);
78
88
  /**
79
89
  * One or multiple rule conditions
80
90
  *
@@ -235,16 +245,6 @@ export type WebpackPluginFunction = (
235
245
  * via the `definition` "RuleSetRules".
236
246
  */
237
247
  export type RuleSetRules = RuleSetRule[];
238
- /**
239
- * This interface was referenced by `WebpackOptions`'s JSON-Schema
240
- * via the `definition` "FilterTypes".
241
- */
242
- export type FilterTypes = FilterItemTypes | FilterItemTypes[];
243
- /**
244
- * This interface was referenced by `WebpackOptions`'s JSON-Schema
245
- * via the `definition` "FilterItemTypes".
246
- */
247
- export type FilterItemTypes = RegExp | string | Function;
248
248
 
249
249
  export interface WebpackOptions {
250
250
  /**
@@ -293,6 +293,19 @@ export interface WebpackOptions {
293
293
  * Specify dependencies that shouldn't be resolved by webpack, but should become dependencies of the resulting bundle. The kind of the dependency depends on `output.libraryTarget`.
294
294
  */
295
295
  externals?: Externals;
296
+ /**
297
+ * Options for infrastructure level logging
298
+ */
299
+ infrastructureLogging?: {
300
+ /**
301
+ * Enable debug logging for specific loggers
302
+ */
303
+ debug?: FilterTypes | boolean;
304
+ /**
305
+ * Log level
306
+ */
307
+ level?: "none" | "error" | "warn" | "info" | "log" | "verbose";
308
+ };
296
309
  /**
297
310
  * Custom values available in the loader context.
298
311
  */
@@ -1094,7 +1107,7 @@ export interface OutputOptions {
1094
1107
  /**
1095
1108
  * Algorithm used for generation the hash (see node.js crypto package)
1096
1109
  */
1097
- hashFunction?: string | (new () => import("../lib/util/createHash").Hash);
1110
+ hashFunction?: string | import("../lib/util/createHash").HashConstructor;
1098
1111
  /**
1099
1112
  * Any string which is added to the hash to salt it
1100
1113
  */
@@ -1343,6 +1356,18 @@ export interface StatsOptions {
1343
1356
  * add the hash of the compilation
1344
1357
  */
1345
1358
  hash?: boolean;
1359
+ /**
1360
+ * add logging output
1361
+ */
1362
+ logging?: boolean | ("none" | "error" | "warn" | "info" | "log" | "verbose");
1363
+ /**
1364
+ * Include debug logging of specified loggers (i. e. for plugins or loaders). Filters can be Strings, RegExps or Functions
1365
+ */
1366
+ loggingDebug?: FilterTypes | boolean;
1367
+ /**
1368
+ * add stack traces to logging output
1369
+ */
1370
+ loggingTrace?: boolean;
1346
1371
  /**
1347
1372
  * Set the maximum number of modules to be shown
1348
1373
  */
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+
3
+ const WebpackError = require("./WebpackError");
4
+ const CURRENT_METHOD_REGEXP = /at ([a-zA-Z0-9_.]*)/;
5
+
6
+ /**
7
+ * @param {string=} method method name
8
+ * @returns {string} message
9
+ */
10
+ function createMessage(method) {
11
+ return `Abstract method${method ? " " + method : ""}. Must be overridden.`;
12
+ }
13
+
14
+ /**
15
+ * @constructor
16
+ */
17
+ function Message() {
18
+ this.stack = undefined;
19
+ Error.captureStackTrace(this);
20
+ /** @type {RegExpMatchArray} */
21
+ const match = this.stack.split("\n")[3].match(CURRENT_METHOD_REGEXP);
22
+
23
+ this.message = match && match[1] ? createMessage(match[1]) : createMessage();
24
+ }
25
+
26
+ /**
27
+ * Error for abstract method
28
+ * @example
29
+ * class FooClass {
30
+ * abstractMethod() {
31
+ * throw new AbstractMethodError(); // error message: Abstract method FooClass.abstractMethod. Must be overriden.
32
+ * }
33
+ * }
34
+ *
35
+ */
36
+ class AbstractMethodError extends WebpackError {
37
+ constructor() {
38
+ super(new Message().message);
39
+ this.name = "AbstractMethodError";
40
+ }
41
+ }
42
+
43
+ module.exports = AbstractMethodError;
@@ -36,6 +36,8 @@ const SortableSet = require("./util/SortableSet");
36
36
  const GraphHelpers = require("./GraphHelpers");
37
37
  const ModuleDependency = require("./dependencies/ModuleDependency");
38
38
  const compareLocations = require("./compareLocations");
39
+ const { Logger, LogType } = require("./logging/Logger");
40
+ const ErrorHelpers = require("./ErrorHelpers");
39
41
 
40
42
  /** @typedef {import("./Module")} Module */
41
43
  /** @typedef {import("./Compiler")} Compiler */
@@ -95,6 +97,14 @@ const compareLocations = require("./compareLocations");
95
97
  * @property {DependenciesBlockVariable[]} variables
96
98
  */
97
99
 
100
+ /**
101
+ * @typedef {Object} LogEntry
102
+ * @property {string} type
103
+ * @property {any[]} args
104
+ * @property {number} time
105
+ * @property {string[]=} trace
106
+ */
107
+
98
108
  /**
99
109
  * @param {Chunk} a first chunk to sort by id
100
110
  * @param {Chunk} b second chunk to sort by id
@@ -384,6 +394,9 @@ class Compilation extends Tapable {
384
394
  "compilerIndex"
385
395
  ]),
386
396
 
397
+ /** @type {SyncBailHook<string, LogEntry>} */
398
+ log: new SyncBailHook(["origin", "logEntry"]),
399
+
387
400
  // TODO the following hooks are weirdly located here
388
401
  // TODO move them for webpack 5
389
402
  /** @type {SyncHook<object, Module>} */
@@ -469,6 +482,8 @@ class Compilation extends Tapable {
469
482
  this.warnings = [];
470
483
  /** @type {Compilation[]} */
471
484
  this.children = [];
485
+ /** @type {Map<string, LogEntry[]>} */
486
+ this.logging = new Map();
472
487
  /** @type {Map<DepConstructor, ModuleFactory>} */
473
488
  this.dependencyFactories = new Map();
474
489
  /** @type {Map<DepConstructor, DependencyTemplate>} */
@@ -499,6 +514,69 @@ class Compilation extends Tapable {
499
514
  return new Stats(this);
500
515
  }
501
516
 
517
+ /**
518
+ * @param {string | (function(): string)} name name of the logger, or function called once to get the logger name
519
+ * @returns {Logger} a logger with that name
520
+ */
521
+ getLogger(name) {
522
+ if (!name) {
523
+ throw new TypeError("Compilation.getLogger(name) called without a name");
524
+ }
525
+ /** @type {LogEntry[] | undefined} */
526
+ let logEntries;
527
+ return new Logger((type, args) => {
528
+ if (typeof name === "function") {
529
+ name = name();
530
+ if (!name) {
531
+ throw new TypeError(
532
+ "Compilation.getLogger(name) called with a function not returning a name"
533
+ );
534
+ }
535
+ }
536
+ let trace;
537
+ switch (type) {
538
+ case LogType.warn:
539
+ case LogType.error:
540
+ case LogType.trace:
541
+ trace = ErrorHelpers.cutOffLoaderExecution(new Error("Trace").stack)
542
+ .split("\n")
543
+ .slice(3);
544
+ break;
545
+ }
546
+ /** @type {LogEntry} */
547
+ const logEntry = {
548
+ time: Date.now(),
549
+ type,
550
+ args,
551
+ trace
552
+ };
553
+ if (this.hooks.log.call(name, logEntry) === undefined) {
554
+ if (logEntry.type === LogType.profileEnd) {
555
+ // eslint-disable-next-line node/no-unsupported-features/node-builtins
556
+ if (typeof console.profileEnd === "function") {
557
+ // eslint-disable-next-line node/no-unsupported-features/node-builtins
558
+ console.profileEnd(`[${name}] ${logEntry.args[0]}`);
559
+ }
560
+ }
561
+ if (logEntries === undefined) {
562
+ logEntries = this.logging.get(name);
563
+ if (logEntries === undefined) {
564
+ logEntries = [];
565
+ this.logging.set(name, logEntries);
566
+ }
567
+ }
568
+ logEntries.push(logEntry);
569
+ if (logEntry.type === LogType.profile) {
570
+ // eslint-disable-next-line node/no-unsupported-features/node-builtins
571
+ if (typeof console.profile === "function") {
572
+ // eslint-disable-next-line node/no-unsupported-features/node-builtins
573
+ console.profile(`[${name}] ${logEntry.args[0]}`);
574
+ }
575
+ }
576
+ }
577
+ });
578
+ }
579
+
502
580
  /**
503
581
  * @typedef {Object} AddModuleResult
504
582
  * @property {Module} module the added or existing module
@@ -2294,7 +2372,7 @@ class Compilation extends Tapable {
2294
2372
  const module = modules[i];
2295
2373
  const moduleHash = createHash(hashFunction);
2296
2374
  module.updateHash(moduleHash);
2297
- module.hash = moduleHash.digest(hashDigest);
2375
+ module.hash = /** @type {string} */ (moduleHash.digest(hashDigest));
2298
2376
  module.renderedHash = module.hash.substr(0, hashDigestLength);
2299
2377
  }
2300
2378
  // clone needed as sort below is inplace mutation
@@ -2329,7 +2407,7 @@ class Compilation extends Tapable {
2329
2407
  this.dependencyTemplates
2330
2408
  );
2331
2409
  this.hooks.chunkHash.call(chunk, chunkHash);
2332
- chunk.hash = chunkHash.digest(hashDigest);
2410
+ chunk.hash = /** @type {string} */ (chunkHash.digest(hashDigest));
2333
2411
  hash.update(chunk.hash);
2334
2412
  chunk.renderedHash = chunk.hash.substr(0, hashDigestLength);
2335
2413
  this.hooks.contentHash.call(chunk);
@@ -2337,7 +2415,7 @@ class Compilation extends Tapable {
2337
2415
  this.errors.push(new ChunkRenderError(chunk, "", err));
2338
2416
  }
2339
2417
  }
2340
- this.fullHash = hash.digest(hashDigest);
2418
+ this.fullHash = /** @type {string} */ (hash.digest(hashDigest));
2341
2419
  this.hash = this.fullHash.substr(0, hashDigestLength);
2342
2420
  }
2343
2421
 
@@ -2353,7 +2431,7 @@ class Compilation extends Tapable {
2353
2431
  const hash = createHash(hashFunction);
2354
2432
  hash.update(this.fullHash);
2355
2433
  hash.update(update);
2356
- this.fullHash = hash.digest(hashDigest);
2434
+ this.fullHash = /** @type {string} */ (hash.digest(hashDigest));
2357
2435
  this.hash = this.fullHash.substr(0, hashDigestLength);
2358
2436
  }
2359
2437
 
package/lib/Compiler.js CHANGED
@@ -27,6 +27,7 @@ const ResolverFactory = require("./ResolverFactory");
27
27
  const RequestShortener = require("./RequestShortener");
28
28
  const { makePathsRelative } = require("./util/identifier");
29
29
  const ConcurrentCompilationError = require("./ConcurrentCompilationError");
30
+ const { Logger } = require("./logging/Logger");
30
31
 
31
32
  /** @typedef {import("../declarations/WebpackOptions").Entry} Entry */
32
33
  /** @typedef {import("../declarations/WebpackOptions").WebpackOptions} WebpackOptions */
@@ -84,6 +85,9 @@ class Compiler extends Tapable {
84
85
  /** @type {SyncHook} */
85
86
  watchClose: new SyncHook([]),
86
87
 
88
+ /** @type {SyncBailHook<string, string, any[]>} */
89
+ infrastructurelog: new SyncBailHook(["origin", "type", "args"]),
90
+
87
91
  // TODO the following hooks are weirdly located here
88
92
  // TODO move them for webpack 5
89
93
  /** @type {SyncHook} */
@@ -137,6 +141,8 @@ class Compiler extends Tapable {
137
141
  /** @type {ResolverFactory} */
138
142
  this.resolverFactory = new ResolverFactory();
139
143
 
144
+ this.infrastructureLogger = undefined;
145
+
140
146
  // TODO remove in webpack 5
141
147
  this.resolvers = {
142
148
  normal: {
@@ -196,6 +202,33 @@ class Compiler extends Tapable {
196
202
  this._assetEmittingWrittenFiles = new Map();
197
203
  }
198
204
 
205
+ /**
206
+ * @param {string | (function(): string)} name name of the logger, or function called once to get the logger name
207
+ * @returns {Logger} a logger with that name
208
+ */
209
+ getInfrastructureLogger(name) {
210
+ if (!name) {
211
+ throw new TypeError(
212
+ "Compiler.getInfrastructureLogger(name) called without a name"
213
+ );
214
+ }
215
+ return new Logger((type, args) => {
216
+ if (typeof name === "function") {
217
+ name = name();
218
+ if (!name) {
219
+ throw new TypeError(
220
+ "Compiler.getInfrastructureLogger(name) called with a function not returning a name"
221
+ );
222
+ }
223
+ }
224
+ if (this.hooks.infrastructurelog.call(name, type, args) === undefined) {
225
+ if (this.infrastructureLogger !== undefined) {
226
+ this.infrastructureLogger(name, type, args);
227
+ }
228
+ }
229
+ });
230
+ }
231
+
199
232
  watch(watchOptions, handler) {
200
233
  if (this.running) return handler(new ConcurrentCompilationError());
201
234
 
@@ -45,7 +45,9 @@ class HashedModuleIdsPlugin {
45
45
  });
46
46
  const hash = createHash(options.hashFunction);
47
47
  hash.update(id);
48
- const hashId = hash.digest(options.hashDigest);
48
+ const hashId = /** @type {string} */ (hash.digest(
49
+ options.hashDigest
50
+ ));
49
51
  let len = options.hashDigestLength;
50
52
  while (usedIds.has(hashId.substr(0, len))) len++;
51
53
  module.id = hashId.substr(0, len);
@@ -148,9 +148,8 @@ class JavascriptModulesPlugin {
148
148
  hash.update(m.hash);
149
149
  }
150
150
  }
151
- chunk.contentHash.javascript = hash
152
- .digest(hashDigest)
153
- .substr(0, hashDigestLength);
151
+ const digest = /** @type {string} */ (hash.digest(hashDigest));
152
+ chunk.contentHash.javascript = digest.substr(0, hashDigestLength);
154
153
  });
155
154
  }
156
155
  );
@@ -28,6 +28,7 @@ class JsonGenerator {
28
28
  })
29
29
  );
30
30
  }
31
+ let finalJson;
31
32
  if (
32
33
  Array.isArray(module.buildMeta.providedExports) &&
33
34
  !module.isUsed("default")
@@ -41,12 +42,14 @@ class JsonGenerator {
41
42
  reducedJson[used] = data[exportName];
42
43
  }
43
44
  }
44
- source.add(
45
- `${module.moduleArgument}.exports = ${stringifySafe(reducedJson)};`
46
- );
45
+ finalJson = reducedJson;
47
46
  } else {
48
- source.add(`${module.moduleArgument}.exports = ${stringifySafe(data)};`);
47
+ finalJson = data;
49
48
  }
49
+ // Use JSON because JSON.parse() is much faster than JavaScript evaluation
50
+ const jsonSource = JSON.stringify(stringifySafe(finalJson));
51
+ const jsonExpr = `JSON.parse(${jsonSource})`;
52
+ source.add(`${module.moduleArgument}.exports = ${jsonExpr};`);
50
53
  return source;
51
54
  }
52
55
  }
@@ -44,7 +44,8 @@ const getBefore = (str, token) => {
44
44
  const getHash = str => {
45
45
  const hash = createHash("md4");
46
46
  hash.update(str);
47
- return hash.digest("hex").substr(0, 4);
47
+ const digest = /** @type {string} */ (hash.digest("hex"));
48
+ return digest.substr(0, 4);
48
49
  };
49
50
 
50
51
  const asRegExp = test => {
@@ -10,7 +10,8 @@ const RequestShortener = require("./RequestShortener");
10
10
  const getHash = str => {
11
11
  const hash = createHash("md4");
12
12
  hash.update(str);
13
- return hash.digest("hex").substr(0, 4);
13
+ const digest = /** @type {string} */ (hash.digest("hex"));
14
+ return digest.substr(0, 4);
14
15
  };
15
16
 
16
17
  class NamedModulesPlugin {
@@ -147,16 +147,20 @@ class NormalModule extends Module {
147
147
 
148
148
  createLoaderContext(resolver, options, compilation, fs) {
149
149
  const requestShortener = compilation.runtimeTemplate.requestShortener;
150
+ const getCurrentLoaderName = () => {
151
+ const currentLoader = this.getCurrentLoader(loaderContext);
152
+ if (!currentLoader) return "(not in loader scope)";
153
+ return requestShortener.shorten(currentLoader.loader);
154
+ };
150
155
  const loaderContext = {
151
156
  version: 2,
152
157
  emitWarning: warning => {
153
158
  if (!(warning instanceof Error)) {
154
159
  warning = new NonErrorEmittedError(warning);
155
160
  }
156
- const currentLoader = this.getCurrentLoader(loaderContext);
157
161
  this.warnings.push(
158
162
  new ModuleWarning(this, warning, {
159
- from: requestShortener.shorten(currentLoader.loader)
163
+ from: getCurrentLoaderName()
160
164
  })
161
165
  );
162
166
  },
@@ -164,13 +168,20 @@ class NormalModule extends Module {
164
168
  if (!(error instanceof Error)) {
165
169
  error = new NonErrorEmittedError(error);
166
170
  }
167
- const currentLoader = this.getCurrentLoader(loaderContext);
168
171
  this.errors.push(
169
172
  new ModuleError(this, error, {
170
- from: requestShortener.shorten(currentLoader.loader)
173
+ from: getCurrentLoaderName()
171
174
  })
172
175
  );
173
176
  },
177
+ getLogger: name => {
178
+ const currentLoader = this.getCurrentLoader(loaderContext);
179
+ return compilation.getLogger(() =>
180
+ [currentLoader && currentLoader.loader, name, this.identifier()]
181
+ .filter(Boolean)
182
+ .join("|")
183
+ );
184
+ },
174
185
  // TODO remove in webpack 5
175
186
  exec: (code, filename) => {
176
187
  // @ts-ignore Argument of type 'this' is not assignable to parameter of type 'Module'.
@@ -405,7 +416,7 @@ class NormalModule extends Module {
405
416
  }
406
417
  hash.update("meta");
407
418
  hash.update(JSON.stringify(this.buildMeta));
408
- this._buildHash = hash.digest("hex");
419
+ this._buildHash = /** @type {string} */ (hash.digest("hex"));
409
420
  }
410
421
 
411
422
  build(options, compilation, resolver, fs, callback) {
@@ -17,7 +17,7 @@ const {
17
17
  const NormalModule = require("./NormalModule");
18
18
  const RawModule = require("./RawModule");
19
19
  const RuleSet = require("./RuleSet");
20
- const cachedMerge = require("./util/cachedMerge");
20
+ const { cachedCleverMerge } = require("./util/cleverMerge");
21
21
 
22
22
  const EMPTY_RESOLVE_OPTIONS = {};
23
23
 
@@ -304,7 +304,7 @@ class NormalModuleFactory extends Tapable {
304
304
  typeof settings[r.type] === "object" &&
305
305
  settings[r.type] !== null
306
306
  ) {
307
- settings[r.type] = cachedMerge(settings[r.type], r.value);
307
+ settings[r.type] = cachedCleverMerge(settings[r.type], r.value);
308
308
  } else {
309
309
  settings[r.type] = r.value;
310
310
  }
@@ -6,8 +6,8 @@
6
6
 
7
7
  /**
8
8
  * Gets the value at path of object
9
- * @param {object} obj - object to query
10
- * @param {string} path - query path
9
+ * @param {object} obj object to query
10
+ * @param {string} path query path
11
11
  * @returns {any} - if {@param path} requests element from array, then `undefined` will be returned
12
12
  */
13
13
  const getProperty = (obj, path) => {
@@ -21,9 +21,9 @@ const getProperty = (obj, path) => {
21
21
 
22
22
  /**
23
23
  * Sets the value at path of object. Stops execution, if {@param path} requests element from array to be set
24
- * @param {object} obj - object to query
25
- * @param {string} path - query path
26
- * @param {any} value - value to be set
24
+ * @param {object} obj object to query
25
+ * @param {string} path query path
26
+ * @param {any} value value to be set
27
27
  * @returns {void}
28
28
  */
29
29
  const setProperty = (obj, path, value) => {
@@ -65,7 +65,7 @@ class OptionsDefaulter {
65
65
 
66
66
  /**
67
67
  * Enhancing {@param options} with default values
68
- * @param {object} options - provided options
68
+ * @param {object} options provided options
69
69
  * @returns {object} - enhanced options
70
70
  * @throws {Error} - will throw error, if configuration value is other then `undefined` or {@link ConfigType}
71
71
  */
@@ -122,9 +122,9 @@ class OptionsDefaulter {
122
122
 
123
123
  /**
124
124
  * Builds up default values
125
- * @param {string} name - option path
126
- * @param {ConfigType | any} config - if {@param def} is provided, then only {@link ConfigType} is allowed
127
- * @param {MakeConfigHandler | CallConfigHandler | AppendConfigValues} [def] - defaults
125
+ * @param {string} name option path
126
+ * @param {ConfigType | any} config if {@param def} is provided, then only {@link ConfigType} is allowed
127
+ * @param {MakeConfigHandler | CallConfigHandler | AppendConfigValues} [def] defaults
128
128
  * @returns {void}
129
129
  */
130
130
  set(name, config, def) {
package/lib/Parser.js CHANGED
@@ -7,14 +7,13 @@
7
7
  // Syntax: https://developer.mozilla.org/en/SpiderMonkey/Parser_API
8
8
 
9
9
  const acorn = require("acorn");
10
- const acornDynamicImport = require("acorn-dynamic-import").default;
11
10
  const { Tapable, SyncBailHook, HookMap } = require("tapable");
12
11
  const util = require("util");
13
12
  const vm = require("vm");
14
13
  const BasicEvaluatedExpression = require("./BasicEvaluatedExpression");
15
14
  const StackedSetMap = require("./util/StackedSetMap");
16
15
 
17
- const acornParser = acorn.Parser.extend(acornDynamicImport);
16
+ const acornParser = acorn.Parser;
18
17
 
19
18
  const joinRanges = (startRange, endRange) => {
20
19
  if (!endRange) return startRange;
@@ -25,7 +24,7 @@ const joinRanges = (startRange, endRange) => {
25
24
  const defaultParserOptions = {
26
25
  ranges: true,
27
26
  locations: true,
28
- ecmaVersion: 2019,
27
+ ecmaVersion: 11,
29
28
  sourceType: "module",
30
29
  onComment: null
31
30
  };
@@ -6,9 +6,12 @@
6
6
 
7
7
  const { Tapable, HookMap, SyncHook, SyncWaterfallHook } = require("tapable");
8
8
  const Factory = require("enhanced-resolve").ResolverFactory;
9
+ const { cachedCleverMerge } = require("./util/cleverMerge");
9
10
 
10
11
  /** @typedef {import("enhanced-resolve").Resolver} Resolver */
11
12
 
13
+ const EMTPY_RESOLVE_OPTIONS = {};
14
+
12
15
  module.exports = class ResolverFactory extends Tapable {
13
16
  constructor() {
14
17
  super();
@@ -22,30 +25,24 @@ module.exports = class ResolverFactory extends Tapable {
22
25
  let match;
23
26
  match = /^resolve-options (.+)$/.exec(options.name);
24
27
  if (match) {
25
- this.hooks.resolveOptions.tap(
26
- match[1],
27
- options.fn.name || "unnamed compat plugin",
28
- options.fn
29
- );
28
+ this.hooks.resolveOptions
29
+ .for(match[1])
30
+ .tap(options.fn.name || "unnamed compat plugin", options.fn);
30
31
  return true;
31
32
  }
32
33
  match = /^resolver (.+)$/.exec(options.name);
33
34
  if (match) {
34
- this.hooks.resolver.tap(
35
- match[1],
36
- options.fn.name || "unnamed compat plugin",
37
- options.fn
38
- );
35
+ this.hooks.resolver
36
+ .for(match[1])
37
+ .tap(options.fn.name || "unnamed compat plugin", options.fn);
39
38
  return true;
40
39
  }
41
40
  });
42
- this.cache1 = new WeakMap();
43
41
  this.cache2 = new Map();
44
42
  }
45
43
 
46
44
  get(type, resolveOptions) {
47
- const cachedResolver = this.cache1.get(resolveOptions);
48
- if (cachedResolver) return cachedResolver();
45
+ resolveOptions = resolveOptions || EMTPY_RESOLVE_OPTIONS;
49
46
  const ident = `${type}|${JSON.stringify(resolveOptions)}`;
50
47
  const resolver = this.cache2.get(ident);
51
48
  if (resolver) return resolver;
@@ -66,7 +63,7 @@ module.exports = class ResolverFactory extends Tapable {
66
63
  resolver.withOptions = options => {
67
64
  const cacheEntry = childCache.get(options);
68
65
  if (cacheEntry !== undefined) return cacheEntry;
69
- const mergedOptions = Object.assign({}, originalResolveOptions, options);
66
+ const mergedOptions = cachedCleverMerge(originalResolveOptions, options);
70
67
  const resolver = this.get(type, mergedOptions);
71
68
  childCache.set(options, resolver);
72
69
  return resolver;