From 3e707b884af97ca9a4851771ae5103d36ba23f19 Mon Sep 17 00:00:00 2001 From: Sergey Melyukov Date: Fri, 26 Oct 2018 01:05:42 +0300 Subject: [PATCH 1/8] ProgressPlugin: support progress by entry points closes #8088 --- declarations/plugins/ProgressPlugin.d.ts | 34 ++++++ lib/Compilation.js | 14 +++ lib/ProgressPlugin.js | 136 ++++++++++++++++------- schemas/plugins/ProgressPlugin.json | 42 +++++++ 4 files changed, 188 insertions(+), 38 deletions(-) create mode 100644 declarations/plugins/ProgressPlugin.d.ts create mode 100644 schemas/plugins/ProgressPlugin.json diff --git a/declarations/plugins/ProgressPlugin.d.ts b/declarations/plugins/ProgressPlugin.d.ts new file mode 100644 index 00000000000..a5b7338f37e --- /dev/null +++ b/declarations/plugins/ProgressPlugin.d.ts @@ -0,0 +1,34 @@ +/** + * This file was automatically generated. + * DO NOT MODIFY BY HAND. + * Run `yarn special-lint-fix` to update + */ + +export type ProgressPluginArgument = ProgressPluginOptions | HandlerFunction; +/** + * Function that executes for every progress step + */ +export type HandlerFunction = (( + percentage: number, + msg: string, + ...args +) => void); + +export interface ProgressPluginOptions { + /** + * Function that executes for every progress step + */ + handler?: HandlerFunction; + /** + * Counting mode. Default: modules + */ + mode?: "modules" | "entries"; + /** + * Minimum modules count to start with. Only for mode=modules. Default: 500 + */ + modulesCount?: number; + /** + * Collect profile data for progress steps. Default: false + */ + profile?: true | false | null; +} diff --git a/lib/Compilation.js b/lib/Compilation.js index 34625418ece..3d04d81b216 100644 --- a/lib/Compilation.js +++ b/lib/Compilation.js @@ -224,6 +224,15 @@ class Compilation extends Tapable { /** @type {SyncHook} */ succeedModule: new SyncHook(["module"]), + /** @type {SyncHook} */ + addEntry: new SyncHook(["entry"]), + /** @type {SyncHook} */ + buildEntry: new SyncHook(["module"]), + /** @type {SyncHook} */ + failedEntry: new SyncHook(["module", "error"]), + /** @type {SyncHook} */ + succeedEntry: new SyncHook(["module"]), + /** @type {SyncWaterfallHook} */ dependencyReference: new SyncWaterfallHook([ "dependencyReference", @@ -1041,6 +1050,8 @@ class Compilation extends Tapable { * @returns {void} returns */ addEntry(context, entry, name, callback) { + this.hooks.addEntry.call(entry); + const slot = { name: name, // TODO webpack 5 remove `request` @@ -1064,10 +1075,12 @@ class Compilation extends Tapable { context, entry, module => { + this.hooks.buildEntry.call(module); this.entries.push(module); }, (err, module) => { if (err) { + this.hooks.failedEntry.call(module, err); return callback(err); } @@ -1079,6 +1092,7 @@ class Compilation extends Tapable { this._preparedEntrypoints.splice(idx, 1); } } + this.hooks.succeedEntry.call(module); return callback(null, module); } ); diff --git a/lib/ProgressPlugin.js b/lib/ProgressPlugin.js index a8612932de1..19fdb2f4e97 100644 --- a/lib/ProgressPlugin.js +++ b/lib/ProgressPlugin.js @@ -4,6 +4,12 @@ */ "use strict"; +const validateOptions = require("schema-utils"); +const schema = require("../schemas/plugins/ProgressPlugin.json"); + +/** @typedef {import("../declarations/plugins/ProgressPlugin").ProgressPluginArgument} ProgressPluginArgument */ +/** @typedef {import("../declarations/plugins/ProgressPlugin").ProgressPluginOptions} ProgressPluginOptions */ + const createDefaultHandler = profile => { let lineCaretPosition = 0; let lastState; @@ -65,16 +71,96 @@ const createDefaultHandler = profile => { return defaultHandler; }; +const createEmitter = (mode, minCount, compiler, handler) => { + let lastCount = 0; + let count = mode === "modules" ? minCount : 0; + let done = 0; + const active = []; + + compiler.hooks.compilation.tap("ProgressPlugin", compilation => { + if (compilation.compiler.isChild()) { + return; + } + + const addHook = + mode === "modules" + ? compilation.hooks.buildModule + : compilation.hooks.addEntry; + const startHook = + mode === "modules" + ? compilation.hooks.buildModule + : compilation.hooks.buildEntry; + const failedHook = + mode === "modules" + ? compilation.hooks.failedModule + : compilation.hooks.failedEntry; + const succeedHook = + mode === "modules" + ? compilation.hooks.succeedModule + : compilation.hooks.succeedEntry; + + const update = () => { + handler( + 0.1 + (done / Math.max(lastCount, count)) * 0.6, + `building ${mode}`, + `${done}/${count} ${mode}`, + `${active.length} active`, + active[active.length - 1] + ); + }; + const doneHandler = module => { + done++; + const ident = module.identifier(); + if (ident) { + const idx = active.indexOf(ident); + if (idx >= 0) { + active.splice(idx, 1); + } + } + update(); + }; + + lastCount = count; + count = 0; + done = 0; + + addHook.tap("ProgressPlugin", () => { + count++; + update(); + }); + startHook.tap("ProgressPlugin", module => { + const ident = module.identifier(); + if (ident) { + active.push(ident); + } + update(); + }); + failedHook.tap("ProgressPlugin", doneHandler); + succeedHook.tap("ProgressPlugin", doneHandler); + }); +}; + class ProgressPlugin { + /** + * @param {ProgressPluginArgument} options options + */ constructor(options) { if (typeof options === "function") { options = { handler: options }; } + options = options || {}; + + validateOptions(schema, options, "Progress Plugin"); + + options = Object.assign(ProgressPlugin.defaultOptions, options); + this.profile = options.profile; this.handler = options.handler; + this.mode = options.mode; + this.modulesCount = options.modulesCount; } apply(compiler) { @@ -94,46 +180,13 @@ class ProgressPlugin { }).apply(compiler); }); } else { - let lastModulesCount = 0; - let moduleCount = 500; - let doneModules = 0; - const activeModules = []; - - const update = module => { - handler( - 0.1 + (doneModules / Math.max(lastModulesCount, moduleCount)) * 0.6, - "building modules", - `${doneModules}/${moduleCount} modules`, - `${activeModules.length} active`, - activeModules[activeModules.length - 1] - ); - }; + createEmitter(this.mode, this.modulesCount, compiler, handler); - const moduleDone = module => { - doneModules++; - const ident = module.identifier(); - if (ident) { - const idx = activeModules.indexOf(ident); - if (idx >= 0) activeModules.splice(idx, 1); - } - update(); - }; compiler.hooks.compilation.tap("ProgressPlugin", compilation => { - if (compilation.compiler.isChild()) return; - lastModulesCount = moduleCount; - moduleCount = 0; - doneModules = 0; - handler(0, "compiling"); - compilation.hooks.buildModule.tap("ProgressPlugin", module => { - moduleCount++; - const ident = module.identifier(); - if (ident) { - activeModules.push(ident); - } - update(); - }); - compilation.hooks.failedModule.tap("ProgressPlugin", moduleDone); - compilation.hooks.succeedModule.tap("ProgressPlugin", moduleDone); + if (compilation.compiler.isChild()) { + return; + } + const hooks = { finishModules: "finish module graph", seal: "sealing", @@ -243,4 +296,11 @@ class ProgressPlugin { } } } + +ProgressPlugin.defaultOptions = { + profile: false, + mode: "modules", + modulesCount: 500 +}; + module.exports = ProgressPlugin; diff --git a/schemas/plugins/ProgressPlugin.json b/schemas/plugins/ProgressPlugin.json new file mode 100644 index 00000000000..5d86b685034 --- /dev/null +++ b/schemas/plugins/ProgressPlugin.json @@ -0,0 +1,42 @@ +{ + "definitions": { + "HandlerFunction": { + "description": "Function that executes for every progress step", + "instanceof": "Function", + "tsType": "((percentage: number, msg: string, ...args) => void)" + } + }, + "title": "ProgressPluginArgument", + "oneOf": [ + { + "title": "ProgressPluginOptions", + "type": "object", + "additionalProperties": false, + "properties": { + "handler": { + "description": "Function that executes for every progress step", + "anyOf": [ + { + "$ref": "#/definitions/HandlerFunction" + } + ] + }, + "mode": { + "description": "Counting mode. Default: modules", + "enum": ["modules", "entries"] + }, + "modulesCount": { + "description": "Minimum modules count to start with. Only for mode=modules. Default: 500", + "type": "number" + }, + "profile": { + "description": "Collect profile data for progress steps. Default: false", + "enum": [true, false, null] + } + } + }, + { + "$ref": "#/definitions/HandlerFunction" + } + ] +} From 905929b391830d74731f36487a064385d09fd2df Mon Sep 17 00:00:00 2001 From: Sergey Melyukov Date: Fri, 26 Oct 2018 15:07:41 +0300 Subject: [PATCH 2/8] some refactoring --- lib/ProgressPlugin.js | 137 +++++++++++++++++++----------------------- 1 file changed, 62 insertions(+), 75 deletions(-) diff --git a/lib/ProgressPlugin.js b/lib/ProgressPlugin.js index 19fdb2f4e97..71f4760c942 100644 --- a/lib/ProgressPlugin.js +++ b/lib/ProgressPlugin.js @@ -71,75 +71,6 @@ const createDefaultHandler = profile => { return defaultHandler; }; -const createEmitter = (mode, minCount, compiler, handler) => { - let lastCount = 0; - let count = mode === "modules" ? minCount : 0; - let done = 0; - const active = []; - - compiler.hooks.compilation.tap("ProgressPlugin", compilation => { - if (compilation.compiler.isChild()) { - return; - } - - const addHook = - mode === "modules" - ? compilation.hooks.buildModule - : compilation.hooks.addEntry; - const startHook = - mode === "modules" - ? compilation.hooks.buildModule - : compilation.hooks.buildEntry; - const failedHook = - mode === "modules" - ? compilation.hooks.failedModule - : compilation.hooks.failedEntry; - const succeedHook = - mode === "modules" - ? compilation.hooks.succeedModule - : compilation.hooks.succeedEntry; - - const update = () => { - handler( - 0.1 + (done / Math.max(lastCount, count)) * 0.6, - `building ${mode}`, - `${done}/${count} ${mode}`, - `${active.length} active`, - active[active.length - 1] - ); - }; - const doneHandler = module => { - done++; - const ident = module.identifier(); - if (ident) { - const idx = active.indexOf(ident); - if (idx >= 0) { - active.splice(idx, 1); - } - } - update(); - }; - - lastCount = count; - count = 0; - done = 0; - - addHook.tap("ProgressPlugin", () => { - count++; - update(); - }); - startHook.tap("ProgressPlugin", module => { - const ident = module.identifier(); - if (ident) { - active.push(ident); - } - update(); - }); - failedHook.tap("ProgressPlugin", doneHandler); - succeedHook.tap("ProgressPlugin", doneHandler); - }); -}; - class ProgressPlugin { /** * @param {ProgressPluginArgument} options options @@ -152,9 +83,7 @@ class ProgressPlugin { } options = options || {}; - validateOptions(schema, options, "Progress Plugin"); - options = Object.assign(ProgressPlugin.defaultOptions, options); this.profile = options.profile; @@ -164,6 +93,7 @@ class ProgressPlugin { } apply(compiler) { + const { mode, modulesCount } = this; const handler = this.handler || createDefaultHandler(this.profile); if (compiler.compilers) { const states = new Array(compiler.compilers.length); @@ -180,13 +110,70 @@ class ProgressPlugin { }).apply(compiler); }); } else { - createEmitter(this.mode, this.modulesCount, compiler, handler); + let lastCount = 0; + let count = mode === "modules" ? modulesCount : 0; + let doneModules = 0; + const activeModules = []; - compiler.hooks.compilation.tap("ProgressPlugin", compilation => { - if (compilation.compiler.isChild()) { - return; + const update = module => { + handler( + 0.1 + (doneModules / Math.max(lastCount, count)) * 0.6, + `building ${mode}`, + `${doneModules}/${count} ${mode}`, + `${activeModules.length} active`, + activeModules[activeModules.length - 1] + ); + }; + + const moduleDone = module => { + doneModules++; + const ident = module.identifier(); + if (ident) { + const idx = activeModules.indexOf(ident); + if (idx >= 0) activeModules.splice(idx, 1); } + update(); + }; + compiler.hooks.compilation.tap("ProgressPlugin", compilation => { + if (compilation.compiler.isChild()) return; + lastCount = count; + count = 0; + doneModules = 0; + handler(0, "compiling"); + const addHook = + mode === "modules" + ? compilation.hooks.buildModule + : compilation.hooks.addEntry; + const startHook = + mode === "modules" + ? compilation.hooks.buildModule + : compilation.hooks.buildEntry; + const failedHook = + mode === "modules" + ? compilation.hooks.failedModule + : compilation.hooks.failedEntry; + const succeedHook = + mode === "modules" + ? compilation.hooks.succeedModule + : compilation.hooks.succeedEntry; + + lastCount = count; + count = 0; + doneModules = 0; + addHook.tap("ProgressPlugin", () => { + count++; + update(); + }); + startHook.tap("ProgressPlugin", module => { + const ident = module.identifier(); + if (ident) { + activeModules.push(ident); + } + update(); + }); + failedHook.tap("ProgressPlugin", moduleDone); + succeedHook.tap("ProgressPlugin", moduleDone); const hooks = { finishModules: "finish module graph", seal: "sealing", From aa8cb0fab74160caca59e1cfdd0758c0346313b0 Mon Sep 17 00:00:00 2001 From: Sergey Melyukov Date: Mon, 29 Oct 2018 15:49:31 +0300 Subject: [PATCH 3/8] ProgressPlugin: refactoring --- declarations/plugins/ProgressPlugin.d.ts | 30 ++++++++ lib/Compilation.js | 14 ++++ lib/ProgressPlugin.js | 90 ++++++++++++++++++++---- schemas/plugins/ProgressPlugin.json | 38 ++++++++++ 4 files changed, 158 insertions(+), 14 deletions(-) create mode 100644 declarations/plugins/ProgressPlugin.d.ts create mode 100644 schemas/plugins/ProgressPlugin.json diff --git a/declarations/plugins/ProgressPlugin.d.ts b/declarations/plugins/ProgressPlugin.d.ts new file mode 100644 index 00000000000..cb278f34a51 --- /dev/null +++ b/declarations/plugins/ProgressPlugin.d.ts @@ -0,0 +1,30 @@ +/** + * This file was automatically generated. + * DO NOT MODIFY BY HAND. + * Run `yarn special-lint-fix` to update + */ + +export type ProgressPluginArgument = ProgressPluginOptions | HandlerFunction; +/** + * Function that executes for every progress step + */ +export type HandlerFunction = (( + percentage: number, + msg: string, + ...args: string[] +) => void); + +export interface ProgressPluginOptions { + /** + * Function that executes for every progress step + */ + handler?: HandlerFunction; + /** + * Minimum modules count to start with. Only for mode=modules. Default: 500 + */ + modulesCount?: number; + /** + * Collect profile data for progress steps. Default: false + */ + profile?: true | false | null; +} diff --git a/lib/Compilation.js b/lib/Compilation.js index 34625418ece..3c81e5667de 100644 --- a/lib/Compilation.js +++ b/lib/Compilation.js @@ -224,6 +224,15 @@ class Compilation extends Tapable { /** @type {SyncHook} */ succeedModule: new SyncHook(["module"]), + /** @type {SyncHook} */ + addEntry: new SyncHook(["entry", "name"]), + /** @type {SyncHook} */ + buildEntry: new SyncHook(["entry", "name"]), + /** @type {SyncHook} */ + failedEntry: new SyncHook(["entry", "name", "error"]), + /** @type {SyncHook} */ + succeedEntry: new SyncHook(["entry", "name", "module"]), + /** @type {SyncWaterfallHook} */ dependencyReference: new SyncWaterfallHook([ "dependencyReference", @@ -1041,6 +1050,8 @@ class Compilation extends Tapable { * @returns {void} returns */ addEntry(context, entry, name, callback) { + this.hooks.addEntry.call(entry, name); + const slot = { name: name, // TODO webpack 5 remove `request` @@ -1064,10 +1075,12 @@ class Compilation extends Tapable { context, entry, module => { + this.hooks.buildEntry.call(entry, name); this.entries.push(module); }, (err, module) => { if (err) { + this.hooks.failedEntry.call(entry, name, err); return callback(err); } @@ -1079,6 +1092,7 @@ class Compilation extends Tapable { this._preparedEntrypoints.splice(idx, 1); } } + this.hooks.succeedEntry.call(entry, name, module); return callback(null, module); } ); diff --git a/lib/ProgressPlugin.js b/lib/ProgressPlugin.js index a8612932de1..7b5293f43a9 100644 --- a/lib/ProgressPlugin.js +++ b/lib/ProgressPlugin.js @@ -4,6 +4,12 @@ */ "use strict"; +const validateOptions = require("schema-utils"); +const schema = require("../schemas/plugins/ProgressPlugin.json"); + +/** @typedef {import("../declarations/plugins/ProgressPlugin").ProgressPluginArgument} ProgressPluginArgument */ +/** @typedef {import("../declarations/plugins/ProgressPlugin").ProgressPluginOptions} ProgressPluginOptions */ + const createDefaultHandler = profile => { let lineCaretPosition = 0; let lastState; @@ -66,18 +72,27 @@ const createDefaultHandler = profile => { }; class ProgressPlugin { + /** + * @param {ProgressPluginArgument} options options + */ constructor(options) { if (typeof options === "function") { options = { handler: options }; } + options = options || {}; + validateOptions(schema, options, "Progress Plugin"); + options = Object.assign({}, ProgressPlugin.defaultOptions, options); + this.profile = options.profile; this.handler = options.handler; + this.modulesCount = options.modulesCount; } apply(compiler) { + const { modulesCount } = this; const handler = this.handler || createDefaultHandler(this.profile); if (compiler.compilers) { const states = new Array(compiler.compilers.length); @@ -95,20 +110,51 @@ class ProgressPlugin { }); } else { let lastModulesCount = 0; - let moduleCount = 500; + let lastEntriesCount = 0; + let moduleCount = modulesCount; + let entriesCount = 0; let doneModules = 0; + let doneEntries = 0; const activeModules = []; + const activeEntries = []; + + const update = () => { + const percentByModules = + doneModules / Math.max(lastModulesCount, moduleCount); + const percentByEntries = + doneEntries / Math.max(lastEntriesCount, entriesCount); - const update = module => { handler( - 0.1 + (doneModules / Math.max(lastModulesCount, moduleCount)) * 0.6, - "building modules", + 0.1 + Math.min(percentByModules, percentByEntries) * 0.6, + "building", + `${doneEntries}/${entriesCount} entries`, `${doneModules}/${moduleCount} modules`, `${activeModules.length} active`, activeModules[activeModules.length - 1] ); }; + const moduleAdd = () => { + moduleCount++; + update(); + }; + const entryAdd = () => { + entriesCount++; + update(); + }; + + const moduleBuild = module => { + const ident = module.identifier(); + if (ident) { + activeModules.push(ident); + } + update(); + }; + const entryBuild = (entry, name) => { + activeEntries.push(name); + update(); + }; + const moduleDone = module => { doneModules++; const ident = module.identifier(); @@ -118,22 +164,32 @@ class ProgressPlugin { } update(); }; + const entryDone = (entry, name) => { + doneEntries++; + const idx = activeEntries.indexOf(name); + if (idx >= 0) activeEntries.splice(idx, 1); + update(); + }; + compiler.hooks.compilation.tap("ProgressPlugin", compilation => { if (compilation.compiler.isChild()) return; lastModulesCount = moduleCount; - moduleCount = 0; - doneModules = 0; + lastEntriesCount = entriesCount; + moduleCount = entriesCount = 0; + doneModules = doneEntries = 0; handler(0, "compiling"); - compilation.hooks.buildModule.tap("ProgressPlugin", module => { - moduleCount++; - const ident = module.identifier(); - if (ident) { - activeModules.push(ident); - } - update(); - }); + + compilation.hooks.buildModule.tap("ProgressPlugin", moduleAdd); + compilation.hooks.addEntry.tap("ProgressPlugin", entryAdd); + + compilation.hooks.buildModule.tap("ProgressPlugin", moduleBuild); + compilation.hooks.buildEntry.tap("ProgressPlugin", entryBuild); + compilation.hooks.failedModule.tap("ProgressPlugin", moduleDone); + compilation.hooks.failedEntry.tap("ProgressPlugin", entryDone); compilation.hooks.succeedModule.tap("ProgressPlugin", moduleDone); + compilation.hooks.succeedEntry.tap("ProgressPlugin", entryDone); + const hooks = { finishModules: "finish module graph", seal: "sealing", @@ -243,4 +299,10 @@ class ProgressPlugin { } } } + +ProgressPlugin.defaultOptions = { + profile: false, + modulesCount: 500 +}; + module.exports = ProgressPlugin; diff --git a/schemas/plugins/ProgressPlugin.json b/schemas/plugins/ProgressPlugin.json new file mode 100644 index 00000000000..b66b163dd2f --- /dev/null +++ b/schemas/plugins/ProgressPlugin.json @@ -0,0 +1,38 @@ +{ + "definitions": { + "HandlerFunction": { + "description": "Function that executes for every progress step", + "instanceof": "Function", + "tsType": "((percentage: number, msg: string, ...args: string[]) => void)" + } + }, + "title": "ProgressPluginArgument", + "oneOf": [ + { + "title": "ProgressPluginOptions", + "type": "object", + "additionalProperties": false, + "properties": { + "handler": { + "description": "Function that executes for every progress step", + "anyOf": [ + { + "$ref": "#/definitions/HandlerFunction" + } + ] + }, + "modulesCount": { + "description": "Minimum modules count to start with. Only for mode=modules. Default: 500", + "type": "number" + }, + "profile": { + "description": "Collect profile data for progress steps. Default: false", + "enum": [true, false, null] + } + } + }, + { + "$ref": "#/definitions/HandlerFunction" + } + ] +} From 64e382606791d47d833818f3c6b2c06f8d50266f Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Sun, 4 Nov 2018 10:27:13 +0100 Subject: [PATCH 4/8] use flags to show different parts of the progress message refactor activeModules to Set for performance reasons --- declarations/plugins/ProgressPlugin.d.ts | 12 ++++ lib/Compilation.js | 3 - lib/ProgressPlugin.js | 92 ++++++++++++++---------- schemas/plugins/ProgressPlugin.json | 12 ++++ 4 files changed, 78 insertions(+), 41 deletions(-) diff --git a/declarations/plugins/ProgressPlugin.d.ts b/declarations/plugins/ProgressPlugin.d.ts index cb278f34a51..78d4f74a205 100644 --- a/declarations/plugins/ProgressPlugin.d.ts +++ b/declarations/plugins/ProgressPlugin.d.ts @@ -15,10 +15,22 @@ export type HandlerFunction = (( ) => void); export interface ProgressPluginOptions { + /** + * Show active modules count and one active module in progress message + */ + activeModules?: boolean; + /** + * Show entries count in progress message + */ + entries?: boolean; /** * Function that executes for every progress step */ handler?: HandlerFunction; + /** + * Show modules count in progress message + */ + modules?: boolean; /** * Minimum modules count to start with. Only for mode=modules. Default: 500 */ diff --git a/lib/Compilation.js b/lib/Compilation.js index 3c81e5667de..0d02acfc375 100644 --- a/lib/Compilation.js +++ b/lib/Compilation.js @@ -226,8 +226,6 @@ class Compilation extends Tapable { /** @type {SyncHook} */ addEntry: new SyncHook(["entry", "name"]), - /** @type {SyncHook} */ - buildEntry: new SyncHook(["entry", "name"]), /** @type {SyncHook} */ failedEntry: new SyncHook(["entry", "name", "error"]), /** @type {SyncHook} */ @@ -1075,7 +1073,6 @@ class Compilation extends Tapable { context, entry, module => { - this.hooks.buildEntry.call(entry, name); this.entries.push(module); }, (err, module) => { diff --git a/lib/ProgressPlugin.js b/lib/ProgressPlugin.js index 7b5293f43a9..b8a6521d63e 100644 --- a/lib/ProgressPlugin.js +++ b/lib/ProgressPlugin.js @@ -89,11 +89,17 @@ class ProgressPlugin { this.profile = options.profile; this.handler = options.handler; this.modulesCount = options.modulesCount; + this.showEntries = options.entries; + this.showModules = options.modules; + this.showActiveModules = options.activeModules; } apply(compiler) { const { modulesCount } = this; const handler = this.handler || createDefaultHandler(this.profile); + const showEntries = this.showEntries; + const showModules = this.showModules; + const showActiveModules = this.showActiveModules; if (compiler.compilers) { const states = new Array(compiler.compilers.length); compiler.compilers.forEach((compiler, idx) => { @@ -112,11 +118,11 @@ class ProgressPlugin { let lastModulesCount = 0; let lastEntriesCount = 0; let moduleCount = modulesCount; - let entriesCount = 0; + let entriesCount = 1; let doneModules = 0; let doneEntries = 0; - const activeModules = []; - const activeEntries = []; + const activeModules = new Set(); + let lastActiveModule = ""; const update = () => { const percentByModules = @@ -124,50 +130,59 @@ class ProgressPlugin { const percentByEntries = doneEntries / Math.max(lastEntriesCount, entriesCount); - handler( - 0.1 + Math.min(percentByModules, percentByEntries) * 0.6, - "building", - `${doneEntries}/${entriesCount} entries`, - `${doneModules}/${moduleCount} modules`, - `${activeModules.length} active`, - activeModules[activeModules.length - 1] - ); + const items = [ + 0.1 + Math.max(percentByModules, percentByEntries) * 0.6, + "building" + ]; + if (showEntries) { + items.push(`${doneEntries}/${entriesCount} entries`); + } + if (showModules) { + items.push(`${doneModules}/${moduleCount} modules`); + } + if (showActiveModules) { + items.push(`${activeModules.size} active`); + items.push(lastActiveModule); + } + handler(...items); }; - const moduleAdd = () => { + const moduleAdd = module => { moduleCount++; - update(); - }; - const entryAdd = () => { - entriesCount++; - update(); - }; - - const moduleBuild = module => { - const ident = module.identifier(); - if (ident) { - activeModules.push(ident); + if (showActiveModules) { + const ident = module.identifier(); + if (ident) { + activeModules.add(ident); + lastActiveModule = ident; + } } update(); }; - const entryBuild = (entry, name) => { - activeEntries.push(name); + + const entryAdd = (entry, name) => { + entriesCount++; update(); }; const moduleDone = module => { doneModules++; - const ident = module.identifier(); - if (ident) { - const idx = activeModules.indexOf(ident); - if (idx >= 0) activeModules.splice(idx, 1); + if (showActiveModules) { + const ident = module.identifier(); + if (ident) { + activeModules.delete(ident); + if (lastActiveModule === ident) { + lastActiveModule = ""; + for (const m of activeModules) { + lastActiveModule = m; + } + } + } } update(); }; + const entryDone = (entry, name) => { doneEntries++; - const idx = activeEntries.indexOf(name); - if (idx >= 0) activeEntries.splice(idx, 1); update(); }; @@ -180,14 +195,11 @@ class ProgressPlugin { handler(0, "compiling"); compilation.hooks.buildModule.tap("ProgressPlugin", moduleAdd); - compilation.hooks.addEntry.tap("ProgressPlugin", entryAdd); - - compilation.hooks.buildModule.tap("ProgressPlugin", moduleBuild); - compilation.hooks.buildEntry.tap("ProgressPlugin", entryBuild); - compilation.hooks.failedModule.tap("ProgressPlugin", moduleDone); - compilation.hooks.failedEntry.tap("ProgressPlugin", entryDone); compilation.hooks.succeedModule.tap("ProgressPlugin", moduleDone); + + compilation.hooks.addEntry.tap("ProgressPlugin", entryAdd); + compilation.hooks.failedEntry.tap("ProgressPlugin", entryDone); compilation.hooks.succeedEntry.tap("ProgressPlugin", entryDone); const hooks = { @@ -302,7 +314,11 @@ class ProgressPlugin { ProgressPlugin.defaultOptions = { profile: false, - modulesCount: 500 + modulesCount: 500, + modules: true, + activeModules: true, + // TODO webpack 5 default this to true + entries: false }; module.exports = ProgressPlugin; diff --git a/schemas/plugins/ProgressPlugin.json b/schemas/plugins/ProgressPlugin.json index b66b163dd2f..fa38fc0d08f 100644 --- a/schemas/plugins/ProgressPlugin.json +++ b/schemas/plugins/ProgressPlugin.json @@ -13,6 +13,14 @@ "type": "object", "additionalProperties": false, "properties": { + "activeModules": { + "description": "Show active modules count and one active module in progress message", + "type": "boolean" + }, + "entries": { + "description": "Show entries count in progress message", + "type": "boolean" + }, "handler": { "description": "Function that executes for every progress step", "anyOf": [ @@ -21,6 +29,10 @@ } ] }, + "modules": { + "description": "Show modules count in progress message", + "type": "boolean" + }, "modulesCount": { "description": "Minimum modules count to start with. Only for mode=modules. Default: 500", "type": "number" From 56d8a8f01023175838d394d669cc22bbfdbec654 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Sun, 4 Nov 2018 10:33:37 +0100 Subject: [PATCH 5/8] prevent writing the same message multiple times to stderr --- lib/ProgressPlugin.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/ProgressPlugin.js b/lib/ProgressPlugin.js index b8a6521d63e..68b71434b93 100644 --- a/lib/ProgressPlugin.js +++ b/lib/ProgressPlugin.js @@ -12,6 +12,7 @@ const schema = require("../schemas/plugins/ProgressPlugin.json"); const createDefaultHandler = profile => { let lineCaretPosition = 0; + let lastMessage = ""; let lastState; let lastStateTime; @@ -52,8 +53,11 @@ const createDefaultHandler = profile => { lastStateTime = now; } } - goToLineStart(msg); - process.stderr.write(msg); + if (lastMessage !== msg) { + goToLineStart(msg); + process.stderr.write(msg); + lastMessage = msg; + } }; const goToLineStart = nextMessage => { From 3d745045a1d53d1fb3c78bdbb2c85ebe8099a19f Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Sun, 4 Nov 2018 10:36:47 +0100 Subject: [PATCH 6/8] add missing hooks to progress --- lib/ProgressPlugin.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/ProgressPlugin.js b/lib/ProgressPlugin.js index 68b71434b93..4f479f3395a 100644 --- a/lib/ProgressPlugin.js +++ b/lib/ProgressPlugin.js @@ -209,6 +209,8 @@ class ProgressPlugin { const hooks = { finishModules: "finish module graph", seal: "sealing", + beforeChunks: "chunk graph", + afterChunks: "after chunk graph", optimizeDependenciesBasic: "basic dependencies optimization", optimizeDependencies: "dependencies optimization", optimizeDependenciesAdvanced: "advanced dependencies optimization", @@ -243,6 +245,7 @@ class ProgressPlugin { recordModules: "record modules", recordChunks: "record chunks", beforeHash: "hashing", + contentHash: "content hashing", afterHash: "after hashing", recordHash: "record hash", beforeModuleAssets: "module assets processing", From 6be1411838a7914119f064b72288c18e21c189b6 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Sun, 4 Nov 2018 10:41:12 +0100 Subject: [PATCH 7/8] move schema into definitions --- schemas/plugins/ProgressPlugin.json | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/schemas/plugins/ProgressPlugin.json b/schemas/plugins/ProgressPlugin.json index fa38fc0d08f..e3847c7e134 100644 --- a/schemas/plugins/ProgressPlugin.json +++ b/schemas/plugins/ProgressPlugin.json @@ -4,12 +4,8 @@ "description": "Function that executes for every progress step", "instanceof": "Function", "tsType": "((percentage: number, msg: string, ...args: string[]) => void)" - } - }, - "title": "ProgressPluginArgument", - "oneOf": [ - { - "title": "ProgressPluginOptions", + }, + "ProgressPluginOptions": { "type": "object", "additionalProperties": false, "properties": { @@ -42,6 +38,12 @@ "enum": [true, false, null] } } + } + }, + "title": "ProgressPluginArgument", + "oneOf": [ + { + "$ref": "#/definitions/ProgressPluginOptions" }, { "$ref": "#/definitions/HandlerFunction" From e35d08471fbf4545bcd7c4259930e9a7fcecaef5 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Mon, 5 Nov 2018 06:46:46 +0100 Subject: [PATCH 8/8] increase test timeout --- test/NodeTemplatePlugin.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/NodeTemplatePlugin.test.js b/test/NodeTemplatePlugin.test.js index 6080e6d0b42..0a0c0223e17 100644 --- a/test/NodeTemplatePlugin.test.js +++ b/test/NodeTemplatePlugin.test.js @@ -5,6 +5,7 @@ const path = require("path"); const webpack = require("../lib/webpack"); describe("NodeTemplatePlugin", () => { + jest.setTimeout(20000); it("should compile and run a simple module", done => { webpack( {