webpack 4.36.1 → 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
  */
@@ -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
  */
@@ -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
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
 
@@ -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'.
@@ -25,20 +25,16 @@ module.exports = class ResolverFactory extends Tapable {
25
25
  let match;
26
26
  match = /^resolve-options (.+)$/.exec(options.name);
27
27
  if (match) {
28
- this.hooks.resolveOptions.tap(
29
- match[1],
30
- options.fn.name || "unnamed compat plugin",
31
- options.fn
32
- );
28
+ this.hooks.resolveOptions
29
+ .for(match[1])
30
+ .tap(options.fn.name || "unnamed compat plugin", options.fn);
33
31
  return true;
34
32
  }
35
33
  match = /^resolver (.+)$/.exec(options.name);
36
34
  if (match) {
37
- this.hooks.resolver.tap(
38
- match[1],
39
- options.fn.name || "unnamed compat plugin",
40
- options.fn
41
- );
35
+ this.hooks.resolver
36
+ .for(match[1])
37
+ .tap(options.fn.name || "unnamed compat plugin", options.fn);
42
38
  return true;
43
39
  }
44
40
  });
package/lib/Stats.js CHANGED
@@ -9,6 +9,7 @@ const SizeFormatHelpers = require("./SizeFormatHelpers");
9
9
  const formatLocation = require("./formatLocation");
10
10
  const identifierUtils = require("./util/identifier");
11
11
  const compareLocations = require("./compareLocations");
12
+ const { LogType } = require("./logging/Logger");
12
13
 
13
14
  const optionsOrFallback = (...args) => {
14
15
  let optionValues = [];
@@ -198,6 +199,18 @@ class Stats {
198
199
  options.publicPath,
199
200
  !forToString
200
201
  );
202
+ const showLogging = optionOrLocalFallback(
203
+ options.logging,
204
+ forToString ? "info" : true
205
+ );
206
+ const showLoggingTrace = optionOrLocalFallback(
207
+ options.loggingTrace,
208
+ !forToString
209
+ );
210
+ const loggingDebug = []
211
+ .concat(optionsOrFallback(options.loggingDebug, []))
212
+ .map(testAgainstGivenOption);
213
+
201
214
  const excludeModules = []
202
215
  .concat(optionsOrFallback(options.excludeModules, options.exclude, []))
203
216
  .map(testAgainstGivenOption);
@@ -699,6 +712,117 @@ class Stats {
699
712
  obj.filteredModules = compilation.modules.length - obj.modules.length;
700
713
  obj.modules.sort(sortByField(sortModules, obj.modules));
701
714
  }
715
+ if (showLogging) {
716
+ const util = require("util");
717
+ obj.logging = {};
718
+ let acceptedTypes;
719
+ let collapsedGroups = false;
720
+ switch (showLogging) {
721
+ case "none":
722
+ acceptedTypes = new Set([]);
723
+ break;
724
+ case "error":
725
+ acceptedTypes = new Set([LogType.error]);
726
+ break;
727
+ case "warn":
728
+ acceptedTypes = new Set([LogType.error, LogType.warn]);
729
+ break;
730
+ case "info":
731
+ acceptedTypes = new Set([LogType.error, LogType.warn, LogType.info]);
732
+ break;
733
+ case true:
734
+ case "log":
735
+ acceptedTypes = new Set([
736
+ LogType.error,
737
+ LogType.warn,
738
+ LogType.info,
739
+ LogType.log,
740
+ LogType.group,
741
+ LogType.groupEnd,
742
+ LogType.groupCollapsed,
743
+ LogType.clear
744
+ ]);
745
+ break;
746
+ case "verbose":
747
+ acceptedTypes = new Set([
748
+ LogType.error,
749
+ LogType.warn,
750
+ LogType.info,
751
+ LogType.log,
752
+ LogType.group,
753
+ LogType.groupEnd,
754
+ LogType.groupCollapsed,
755
+ LogType.profile,
756
+ LogType.profileEnd,
757
+ LogType.time,
758
+ LogType.clear
759
+ ]);
760
+ collapsedGroups = true;
761
+ break;
762
+ }
763
+ for (const [origin, logEntries] of compilation.logging) {
764
+ const debugMode = loggingDebug.some(fn => fn(origin));
765
+ let collapseCounter = 0;
766
+ let processedLogEntries = logEntries;
767
+ if (!debugMode) {
768
+ processedLogEntries = processedLogEntries.filter(entry => {
769
+ if (!acceptedTypes.has(entry.type)) return false;
770
+ if (!collapsedGroups) {
771
+ switch (entry.type) {
772
+ case LogType.groupCollapsed:
773
+ collapseCounter++;
774
+ return collapseCounter === 1;
775
+ case LogType.group:
776
+ if (collapseCounter > 0) collapseCounter++;
777
+ return collapseCounter === 0;
778
+ case LogType.groupEnd:
779
+ if (collapseCounter > 0) {
780
+ collapseCounter--;
781
+ return false;
782
+ }
783
+ return true;
784
+ default:
785
+ return collapseCounter === 0;
786
+ }
787
+ }
788
+ return true;
789
+ });
790
+ }
791
+ processedLogEntries = processedLogEntries.map(entry => {
792
+ let message = undefined;
793
+ if (entry.type === LogType.time) {
794
+ message = `${entry.args[0]}: ${entry.args[1] * 1000 +
795
+ entry.args[2] / 1000000}ms`;
796
+ } else if (entry.args && entry.args.length > 0) {
797
+ message = util.format(entry.args[0], ...entry.args.slice(1));
798
+ }
799
+ return {
800
+ type:
801
+ (debugMode || collapsedGroups) &&
802
+ entry.type === LogType.groupCollapsed
803
+ ? LogType.group
804
+ : entry.type,
805
+ message,
806
+ trace: showLoggingTrace && entry.trace ? entry.trace : undefined
807
+ };
808
+ });
809
+ let name = identifierUtils
810
+ .makePathsRelative(context, origin, compilation.cache)
811
+ .replace(/\|/g, " ");
812
+ if (name in obj.logging) {
813
+ let i = 1;
814
+ while (`${name}#${i}` in obj.logging) {
815
+ i++;
816
+ }
817
+ name = `${name}#${i}`;
818
+ }
819
+ obj.logging[name] = {
820
+ entries: processedLogEntries,
821
+ filteredEntries: logEntries.length - processedLogEntries.length,
822
+ debug: debugMode
823
+ };
824
+ }
825
+ }
702
826
  if (showChildren) {
703
827
  obj.children = compilation.children.map((child, idx) => {
704
828
  const childOptions = Stats.getChildOptions(options, idx);
@@ -1305,6 +1429,95 @@ class Stats {
1305
1429
 
1306
1430
  processModulesList(obj, "");
1307
1431
 
1432
+ if (obj.logging) {
1433
+ for (const origin of Object.keys(obj.logging)) {
1434
+ const logData = obj.logging[origin];
1435
+ if (logData.entries.length > 0) {
1436
+ newline();
1437
+ if (logData.debug) {
1438
+ colors.red("DEBUG ");
1439
+ }
1440
+ colors.bold("LOG from " + origin);
1441
+ newline();
1442
+ let indent = "";
1443
+ for (const entry of logData.entries) {
1444
+ let color = colors.normal;
1445
+ let prefix = "";
1446
+ switch (entry.type) {
1447
+ case LogType.clear:
1448
+ colors.normal(`${indent}-------`);
1449
+ newline();
1450
+ continue;
1451
+ case LogType.error:
1452
+ color = colors.red;
1453
+ prefix = "<e> ";
1454
+ break;
1455
+ case LogType.warn:
1456
+ color = colors.yellow;
1457
+ prefix = "<w> ";
1458
+ break;
1459
+ case LogType.info:
1460
+ color = colors.green;
1461
+ prefix = "<i> ";
1462
+ break;
1463
+ case LogType.log:
1464
+ color = colors.bold;
1465
+ break;
1466
+ case LogType.trace:
1467
+ case LogType.debug:
1468
+ color = colors.normal;
1469
+ break;
1470
+ case LogType.profile:
1471
+ color = colors.magenta;
1472
+ prefix = "<p> ";
1473
+ break;
1474
+ case LogType.profileEnd:
1475
+ color = colors.magenta;
1476
+ prefix = "</p> ";
1477
+ break;
1478
+ case LogType.time:
1479
+ color = colors.magenta;
1480
+ prefix = "<t> ";
1481
+ break;
1482
+ case LogType.group:
1483
+ color = colors.cyan;
1484
+ break;
1485
+ case LogType.groupCollapsed:
1486
+ color = colors.cyan;
1487
+ prefix = "<+> ";
1488
+ break;
1489
+ case LogType.groupEnd:
1490
+ if (indent.length > 2)
1491
+ indent = indent.slice(0, indent.length - 2);
1492
+ continue;
1493
+ }
1494
+ if (entry.message) {
1495
+ for (const line of entry.message.split("\n")) {
1496
+ colors.normal(`${indent}${prefix}`);
1497
+ color(line);
1498
+ newline();
1499
+ }
1500
+ }
1501
+ if (entry.trace) {
1502
+ for (const line of entry.trace) {
1503
+ colors.normal(`${indent}| ${line}`);
1504
+ newline();
1505
+ }
1506
+ }
1507
+ switch (entry.type) {
1508
+ case LogType.group:
1509
+ indent += " ";
1510
+ break;
1511
+ }
1512
+ }
1513
+ if (logData.filteredEntries) {
1514
+ colors.normal(`+ ${logData.filteredEntries} hidden lines`);
1515
+ newline();
1516
+ }
1517
+ }
1518
+ }
1519
+ }
1520
+
1308
1521
  if (obj._showWarnings && obj.warnings) {
1309
1522
  for (const warning of obj.warnings) {
1310
1523
  newline();
@@ -1375,6 +1588,7 @@ class Stats {
1375
1588
  optimizationBailout: true,
1376
1589
  errorDetails: true,
1377
1590
  publicPath: true,
1591
+ logging: "verbose",
1378
1592
  exclude: false,
1379
1593
  maxModules: Infinity
1380
1594
  };
@@ -1391,6 +1605,7 @@ class Stats {
1391
1605
  optimizationBailout: true,
1392
1606
  errorDetails: true,
1393
1607
  publicPath: true,
1608
+ logging: true,
1394
1609
  exclude: false,
1395
1610
  maxModules: Infinity
1396
1611
  };
@@ -1400,19 +1615,22 @@ class Stats {
1400
1615
  modules: true,
1401
1616
  maxModules: 0,
1402
1617
  errors: true,
1403
- warnings: true
1618
+ warnings: true,
1619
+ logging: "warn"
1404
1620
  };
1405
1621
  case "errors-only":
1406
1622
  return {
1407
1623
  all: false,
1408
1624
  errors: true,
1409
- moduleTrace: true
1625
+ moduleTrace: true,
1626
+ logging: "error"
1410
1627
  };
1411
1628
  case "errors-warnings":
1412
1629
  return {
1413
1630
  all: false,
1414
1631
  errors: true,
1415
- warnings: true
1632
+ warnings: true,
1633
+ logging: "warn"
1416
1634
  };
1417
1635
  default:
1418
1636
  return {};
@@ -363,6 +363,12 @@ class WebpackOptionsDefaulter extends OptionsDefaulter {
363
363
  options.resolveLoader.plugins.length > 0
364
364
  );
365
365
  });
366
+
367
+ this.set("infrastructureLogging", "call", value =>
368
+ Object.assign({}, value)
369
+ );
370
+ this.set("infrastructureLogging.level", "info");
371
+ this.set("infrastructureLogging.debug", false);
366
372
  }
367
373
  }
368
374
 
@@ -0,0 +1,126 @@
1
+ /*
2
+ MIT License http://www.opensource.org/licenses/mit-license.php
3
+ Author Tobias Koppers @sokra
4
+ */
5
+
6
+ "use strict";
7
+
8
+ /**
9
+ * @enum {string}
10
+ */
11
+ const LogType = Object.freeze({
12
+ error: "error", // message, c style arguments
13
+ warn: "warn", // message, c style arguments
14
+ info: "info", // message, c style arguments
15
+ log: "log", // message, c style arguments
16
+ debug: "debug", // message, c style arguments
17
+
18
+ trace: "trace", // no arguments
19
+
20
+ group: "group", // [label]
21
+ groupCollapsed: "groupCollapsed", // [label]
22
+ groupEnd: "groupEnd", // [label]
23
+
24
+ profile: "profile", // [profileName]
25
+ profileEnd: "profileEnd", // [profileName]
26
+
27
+ time: "time", // name, time as [seconds, nanoseconds]
28
+
29
+ clear: "clear" // no arguments
30
+ });
31
+
32
+ exports.LogType = LogType;
33
+
34
+ /** @typedef {LogType} LogTypeEnum */
35
+
36
+ const LOG_SYMBOL = Symbol("webpack logger raw log method");
37
+ const TIMERS_SYMBOL = Symbol("webpack logger times");
38
+
39
+ class WebpackLogger {
40
+ /**
41
+ * @param {function(LogType, any[]=): void} log log function
42
+ */
43
+ constructor(log) {
44
+ this[LOG_SYMBOL] = log;
45
+ }
46
+
47
+ error(...args) {
48
+ this[LOG_SYMBOL](LogType.error, args);
49
+ }
50
+
51
+ warn(...args) {
52
+ this[LOG_SYMBOL](LogType.warn, args);
53
+ }
54
+
55
+ info(...args) {
56
+ this[LOG_SYMBOL](LogType.info, args);
57
+ }
58
+
59
+ log(...args) {
60
+ this[LOG_SYMBOL](LogType.log, args);
61
+ }
62
+
63
+ debug(...args) {
64
+ this[LOG_SYMBOL](LogType.debug, args);
65
+ }
66
+
67
+ assert(assertion, ...args) {
68
+ if (!assertion) {
69
+ this[LOG_SYMBOL](LogType.error, args);
70
+ }
71
+ }
72
+
73
+ trace() {
74
+ this[LOG_SYMBOL](LogType.trace, ["Trace"]);
75
+ }
76
+
77
+ clear() {
78
+ this[LOG_SYMBOL](LogType.clear);
79
+ }
80
+
81
+ group(...args) {
82
+ this[LOG_SYMBOL](LogType.group, args);
83
+ }
84
+
85
+ groupCollapsed(...args) {
86
+ this[LOG_SYMBOL](LogType.groupCollapsed, args);
87
+ }
88
+
89
+ groupEnd(...args) {
90
+ this[LOG_SYMBOL](LogType.groupEnd, args);
91
+ }
92
+
93
+ profile(label) {
94
+ this[LOG_SYMBOL](LogType.profile, [label]);
95
+ }
96
+
97
+ profileEnd(label) {
98
+ this[LOG_SYMBOL](LogType.profileEnd, [label]);
99
+ }
100
+
101
+ time(label) {
102
+ this[TIMERS_SYMBOL] = this[TIMERS_SYMBOL] || new Map();
103
+ this[TIMERS_SYMBOL].set(label, process.hrtime());
104
+ }
105
+
106
+ timeLog(label) {
107
+ const prev = this[TIMERS_SYMBOL] && this[TIMERS_SYMBOL].get(label);
108
+ if (!prev) {
109
+ throw new Error(`No such label '${label}' for WebpackLogger.timeLog()`);
110
+ }
111
+ const time = process.hrtime(prev);
112
+ this[LOG_SYMBOL](LogType.time, [label, ...time]);
113
+ }
114
+
115
+ timeEnd(label) {
116
+ const prev = this[TIMERS_SYMBOL] && this[TIMERS_SYMBOL].get(label);
117
+ if (!prev) {
118
+ throw new Error(`No such label '${label}' for WebpackLogger.timeEnd()`);
119
+ }
120
+ const time = process.hrtime(prev);
121
+ this[TIMERS_SYMBOL].delete(label);
122
+ this[LOG_SYMBOL](LogType.time, [label, ...time]);
123
+ }
124
+ }
125
+
126
+ exports.Logger = WebpackLogger;
@@ -0,0 +1,188 @@
1
+ /*
2
+ MIT License http://www.opensource.org/licenses/mit-license.php
3
+ Author Tobias Koppers @sokra
4
+ */
5
+
6
+ "use strict";
7
+
8
+ const { LogType } = require("./Logger");
9
+
10
+ /** @typedef {import("./Logger").LogTypeEnum} LogTypeEnum */
11
+ /** @typedef {import("../../declarations/WebpackOptions").FilterTypes} FilterTypes */
12
+ /** @typedef {import("../../declarations/WebpackOptions").FilterItemTypes} FilterItemTypes */
13
+
14
+ /** @typedef {function(string): boolean} FilterFunction */
15
+
16
+ /**
17
+ * @typedef {Object} LoggerOptions
18
+ * @property {false|true|"none"|"error"|"warn"|"info"|"log"|"verbose"} options.level loglevel
19
+ * @property {FilterTypes|boolean} options.debug filter for debug logging
20
+ */
21
+
22
+ /**
23
+ * @param {FilterItemTypes} item an input item
24
+ * @returns {FilterFunction} filter funtion
25
+ */
26
+ const filterToFunction = item => {
27
+ if (typeof item === "string") {
28
+ const regExp = new RegExp(
29
+ `[\\\\/]${item.replace(
30
+ // eslint-disable-next-line no-useless-escape
31
+ /[-[\]{}()*+?.\\^$|]/g,
32
+ "\\$&"
33
+ )}([\\\\/]|$|!|\\?)`
34
+ );
35
+ return ident => regExp.test(ident);
36
+ }
37
+ if (item && typeof item === "object" && typeof item.test === "function") {
38
+ return ident => item.test(ident);
39
+ }
40
+ if (typeof item === "function") {
41
+ return item;
42
+ }
43
+ if (typeof item === "boolean") {
44
+ return () => item;
45
+ }
46
+ };
47
+
48
+ /**
49
+ * @enum {number} */
50
+ const LogLevel = {
51
+ none: 6,
52
+ false: 6,
53
+ error: 5,
54
+ warn: 4,
55
+ info: 3,
56
+ log: 2,
57
+ true: 2,
58
+ verbose: 1
59
+ };
60
+
61
+ /**
62
+ * @param {LoggerOptions} options options object
63
+ * @returns {function(string, LogTypeEnum, any[]): void} logging function
64
+ */
65
+ module.exports = ({ level = "info", debug = false }) => {
66
+ const debugFilters =
67
+ typeof debug === "boolean"
68
+ ? [() => debug]
69
+ : /** @type {FilterItemTypes[]} */ ([])
70
+ .concat(debug)
71
+ .map(filterToFunction);
72
+ /** @type {number} */
73
+ const loglevel = LogLevel[`${level}`] || 0;
74
+
75
+ /**
76
+ * @param {string} name name of the logger
77
+ * @param {LogTypeEnum} type type of the log entry
78
+ * @param {any[]} args arguments of the log entry
79
+ * @returns {void}
80
+ */
81
+ const logger = (name, type, args) => {
82
+ const labeledArgs = (prefix = "") => {
83
+ if (Array.isArray(args)) {
84
+ if (args.length > 0 && typeof args[0] === "string") {
85
+ return [`${prefix}[${name}] ${args[0]}`, ...args.slice(1)];
86
+ } else {
87
+ return [`${prefix}[${name}]`, ...args];
88
+ }
89
+ } else {
90
+ return [];
91
+ }
92
+ };
93
+ const debug = debugFilters.some(f => f(name));
94
+ switch (type) {
95
+ case LogType.debug:
96
+ if (!debug) return;
97
+ // eslint-disable-next-line node/no-unsupported-features/node-builtins
98
+ if (typeof console.debug === "function") {
99
+ // eslint-disable-next-line node/no-unsupported-features/node-builtins
100
+ console.debug(...labeledArgs());
101
+ } else {
102
+ console.log(...labeledArgs());
103
+ }
104
+ break;
105
+ case LogType.log:
106
+ if (!debug && loglevel > LogLevel.log) return;
107
+ console.log(...labeledArgs());
108
+ break;
109
+ case LogType.info:
110
+ if (!debug && loglevel > LogLevel.info) return;
111
+ console.info(...labeledArgs("<i> "));
112
+ break;
113
+ case LogType.warn:
114
+ if (!debug && loglevel > LogLevel.warn) return;
115
+ console.warn(...labeledArgs("<w> "));
116
+ break;
117
+ case LogType.error:
118
+ if (!debug && loglevel > LogLevel.error) return;
119
+ console.error(...labeledArgs("<e> "));
120
+ break;
121
+ case LogType.trace:
122
+ if (!debug) return;
123
+ console.trace();
124
+ break;
125
+ case LogType.group:
126
+ if (!debug && loglevel > LogLevel.log) return;
127
+ // eslint-disable-next-line node/no-unsupported-features/node-builtins
128
+ if (typeof console.group === "function") {
129
+ // eslint-disable-next-line node/no-unsupported-features/node-builtins
130
+ console.group(...labeledArgs());
131
+ } else {
132
+ console.log(...labeledArgs());
133
+ }
134
+ break;
135
+ case LogType.groupCollapsed:
136
+ if (!debug && loglevel > LogLevel.log) return;
137
+ // eslint-disable-next-line node/no-unsupported-features/node-builtins
138
+ if (typeof console.groupCollapsed === "function") {
139
+ // eslint-disable-next-line node/no-unsupported-features/node-builtins
140
+ console.groupCollapsed(...labeledArgs());
141
+ } else {
142
+ console.log(...labeledArgs("<g> "));
143
+ }
144
+ break;
145
+ case LogType.groupEnd:
146
+ if (!debug && loglevel > LogLevel.log) return;
147
+ // eslint-disable-next-line node/no-unsupported-features/node-builtins
148
+ if (typeof console.groupEnd === "function") {
149
+ // eslint-disable-next-line node/no-unsupported-features/node-builtins
150
+ console.groupEnd();
151
+ } else {
152
+ console.log(...labeledArgs("</g> "));
153
+ }
154
+ break;
155
+ case LogType.time:
156
+ if (!debug && loglevel > LogLevel.log) return;
157
+ console.log(
158
+ `[${name}] ${args[0]}: ${args[1] * 1000 + args[2] / 1000000}ms`
159
+ );
160
+ break;
161
+ case LogType.profile:
162
+ // eslint-disable-next-line node/no-unsupported-features/node-builtins
163
+ if (typeof console.profile === "function") {
164
+ // eslint-disable-next-line node/no-unsupported-features/node-builtins
165
+ console.profile(...labeledArgs());
166
+ }
167
+ break;
168
+ case LogType.profileEnd:
169
+ // eslint-disable-next-line node/no-unsupported-features/node-builtins
170
+ if (typeof console.profileEnd === "function") {
171
+ // eslint-disable-next-line node/no-unsupported-features/node-builtins
172
+ console.profileEnd(...labeledArgs());
173
+ }
174
+ break;
175
+ case LogType.clear:
176
+ if (!debug && loglevel > LogLevel.log) return;
177
+ // eslint-disable-next-line node/no-unsupported-features/node-builtins
178
+ if (typeof console.clear === "function") {
179
+ // eslint-disable-next-line node/no-unsupported-features/node-builtins
180
+ console.clear();
181
+ }
182
+ break;
183
+ default:
184
+ throw new Error(`Unexpected LogType ${type}`);
185
+ }
186
+ };
187
+ return logger;
188
+ };
@@ -0,0 +1,35 @@
1
+ const SyncBailHook = require("tapable/lib/SyncBailHook");
2
+ const { Logger } = require("./Logger");
3
+ const createConsoleLogger = require("./createConsoleLogger");
4
+
5
+ /** @type {createConsoleLogger.LoggerOptions} */
6
+ let currentDefaultLoggerOptions = {
7
+ level: "info",
8
+ debug: false
9
+ };
10
+ let currentDefaultLogger = createConsoleLogger(currentDefaultLoggerOptions);
11
+
12
+ /**
13
+ * @param {string} name name of the logger
14
+ * @returns {Logger} a logger
15
+ */
16
+ exports.getLogger = name => {
17
+ return new Logger((type, args) => {
18
+ if (exports.hooks.log.call(name, type, args) === undefined) {
19
+ currentDefaultLogger(name, type, args);
20
+ }
21
+ });
22
+ };
23
+
24
+ /**
25
+ * @param {createConsoleLogger.LoggerOptions} options new options, merge with old options
26
+ * @returns {void}
27
+ */
28
+ exports.configureDefaultLogger = options => {
29
+ Object.assign(currentDefaultLoggerOptions, options);
30
+ currentDefaultLogger = createConsoleLogger(currentDefaultLoggerOptions);
31
+ };
32
+
33
+ exports.hooks = {
34
+ log: new SyncBailHook(["origin", "type", "args"])
35
+ };
@@ -8,9 +8,23 @@ const NodeWatchFileSystem = require("./NodeWatchFileSystem");
8
8
  const NodeOutputFileSystem = require("./NodeOutputFileSystem");
9
9
  const NodeJsInputFileSystem = require("enhanced-resolve/lib/NodeJsInputFileSystem");
10
10
  const CachedInputFileSystem = require("enhanced-resolve/lib/CachedInputFileSystem");
11
+ const createConsoleLogger = require("../logging/createConsoleLogger");
11
12
 
12
13
  class NodeEnvironmentPlugin {
14
+ constructor(options) {
15
+ this.options = options || {};
16
+ }
17
+
13
18
  apply(compiler) {
19
+ compiler.infrastructureLogger = createConsoleLogger(
20
+ Object.assign(
21
+ {
22
+ level: "info",
23
+ debug: false
24
+ },
25
+ this.options.infrastructureLogging
26
+ )
27
+ );
14
28
  compiler.inputFileSystem = new CachedInputFileSystem(
15
29
  new NodeJsInputFileSystem(),
16
30
  60000
package/lib/webpack.js CHANGED
@@ -40,7 +40,9 @@ const webpack = (options, callback) => {
40
40
 
41
41
  compiler = new Compiler(options.context);
42
42
  compiler.options = options;
43
- new NodeEnvironmentPlugin().apply(compiler);
43
+ new NodeEnvironmentPlugin({
44
+ infrastructureLogging: options.infrastructureLogging
45
+ }).apply(compiler);
44
46
  if (options.plugins && Array.isArray(options.plugins)) {
45
47
  for (const plugin of options.plugins) {
46
48
  if (typeof plugin === "function") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webpack",
3
- "version": "4.36.1",
3
+ "version": "4.37.0",
4
4
  "author": "Tobias Koppers @sokra",
5
5
  "description": "Packs CommonJs/AMD modules for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand. Support loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.",
6
6
  "license": "MIT",
@@ -166,7 +166,7 @@
166
166
  },
167
167
  {
168
168
  "instanceof": "Function",
169
- "tsType": "Function"
169
+ "tsType": "((value: string) => boolean)"
170
170
  }
171
171
  ]
172
172
  },
@@ -1822,6 +1822,35 @@
1822
1822
  "description": "add the hash of the compilation",
1823
1823
  "type": "boolean"
1824
1824
  },
1825
+ "logging": {
1826
+ "description": "add logging output",
1827
+ "anyOf": [
1828
+ {
1829
+ "description": "enable/disable logging output (true: shows normal logging output, loglevel: log)",
1830
+ "type": "boolean"
1831
+ },
1832
+ {
1833
+ "description": "specify log level of logging output",
1834
+ "enum": ["none", "error", "warn", "info", "log", "verbose"]
1835
+ }
1836
+ ]
1837
+ },
1838
+ "loggingDebug": {
1839
+ "description": "Include debug logging of specified loggers (i. e. for plugins or loaders). Filters can be Strings, RegExps or Functions",
1840
+ "anyOf": [
1841
+ {
1842
+ "$ref": "#/definitions/FilterTypes"
1843
+ },
1844
+ {
1845
+ "description": "Enable/Disable debug logging for all loggers",
1846
+ "type": "boolean"
1847
+ }
1848
+ ]
1849
+ },
1850
+ "loggingTrace": {
1851
+ "description": "add stack traces to logging output",
1852
+ "type": "boolean"
1853
+ },
1825
1854
  "maxModules": {
1826
1855
  "description": "Set the maximum number of modules to be shown",
1827
1856
  "type": "number"
@@ -1996,6 +2025,29 @@
1996
2025
  }
1997
2026
  ]
1998
2027
  },
2028
+ "infrastructureLogging": {
2029
+ "description": "Options for infrastructure level logging",
2030
+ "type": "object",
2031
+ "additionalProperties": false,
2032
+ "properties": {
2033
+ "debug": {
2034
+ "description": "Enable debug logging for specific loggers",
2035
+ "anyOf": [
2036
+ {
2037
+ "$ref": "#/definitions/FilterTypes"
2038
+ },
2039
+ {
2040
+ "description": "Enable/Disable debug logging for all loggers",
2041
+ "type": "boolean"
2042
+ }
2043
+ ]
2044
+ },
2045
+ "level": {
2046
+ "description": "Log level",
2047
+ "enum": ["none", "error", "warn", "info", "log", "verbose"]
2048
+ }
2049
+ }
2050
+ },
1999
2051
  "loader": {
2000
2052
  "description": "Custom values available in the loader context.",
2001
2053
  "type": "object"