Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ProgressPlugin: support progress by entry points #8279

Merged
merged 10 commits into from Nov 5, 2018
42 changes: 42 additions & 0 deletions declarations/plugins/ProgressPlugin.d.ts
@@ -0,0 +1,42 @@
/**
* 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 {
/**
* 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
*/
modulesCount?: number;
/**
* Collect profile data for progress steps. Default: false
*/
profile?: true | false | null;
}
11 changes: 11 additions & 0 deletions lib/Compilation.js
Expand Up @@ -224,6 +224,13 @@ class Compilation extends Tapable {
/** @type {SyncHook<Module>} */
succeedModule: new SyncHook(["module"]),

/** @type {SyncHook<Dependency, string>} */
addEntry: new SyncHook(["entry", "name"]),
/** @type {SyncHook<Dependency, string, Error>} */
failedEntry: new SyncHook(["entry", "name", "error"]),
/** @type {SyncHook<Dependency, string, Module>} */
succeedEntry: new SyncHook(["entry", "name", "module"]),

/** @type {SyncWaterfallHook<DependencyReference, Dependency, Module>} */
dependencyReference: new SyncWaterfallHook([
"dependencyReference",
Expand Down Expand Up @@ -1041,6 +1048,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`
Expand Down Expand Up @@ -1068,6 +1077,7 @@ class Compilation extends Tapable {
},
(err, module) => {
if (err) {
this.hooks.failedEntry.call(entry, name, err);
return callback(err);
}

Expand All @@ -1079,6 +1089,7 @@ class Compilation extends Tapable {
this._preparedEntrypoints.splice(idx, 1);
}
}
this.hooks.succeedEntry.call(entry, name, module);
return callback(null, module);
}
);
Expand Down
139 changes: 112 additions & 27 deletions lib/ProgressPlugin.js
Expand Up @@ -4,8 +4,15 @@
*/
"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 lastMessage = "";
let lastState;
let lastStateTime;

Expand Down Expand Up @@ -46,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 => {
Expand All @@ -66,19 +76,34 @@ 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;
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) => {
Expand All @@ -95,48 +120,97 @@ class ProgressPlugin {
});
} else {
let lastModulesCount = 0;
let moduleCount = 500;
let lastEntriesCount = 0;
let moduleCount = modulesCount;
let entriesCount = 1;
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]
);
let doneEntries = 0;
const activeModules = new Set();
let lastActiveModule = "";

const update = () => {
const percentByModules =
doneModules / Math.max(lastModulesCount, moduleCount);
const percentByEntries =
doneEntries / Math.max(lastEntriesCount, entriesCount);

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 = module => {
moduleCount++;
if (showActiveModules) {
const ident = module.identifier();
if (ident) {
activeModules.add(ident);
lastActiveModule = ident;
}
}
update();
};

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++;
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.failedModule.tap("ProgressPlugin", moduleDone);
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 = {
finishModules: "finish module graph",
seal: "sealing",
beforeChunks: "chunk graph",
afterChunks: "after chunk graph",
optimizeDependenciesBasic: "basic dependencies optimization",
optimizeDependencies: "dependencies optimization",
optimizeDependenciesAdvanced: "advanced dependencies optimization",
Expand Down Expand Up @@ -171,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",
Expand Down Expand Up @@ -243,4 +318,14 @@ class ProgressPlugin {
}
}
}

ProgressPlugin.defaultOptions = {
profile: false,
modulesCount: 500,
modules: true,
activeModules: true,
// TODO webpack 5 default this to true
entries: false
};

module.exports = ProgressPlugin;
52 changes: 52 additions & 0 deletions schemas/plugins/ProgressPlugin.json
@@ -0,0 +1,52 @@
{
"definitions": {
"HandlerFunction": {
"description": "Function that executes for every progress step",
"instanceof": "Function",
"tsType": "((percentage: number, msg: string, ...args: string[]) => void)"
},
"ProgressPluginOptions": {
"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": [
{
"$ref": "#/definitions/HandlerFunction"
}
]
},
"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"
},
"profile": {
"description": "Collect profile data for progress steps. Default: false",
"enum": [true, false, null]
smelukov marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
},
"title": "ProgressPluginArgument",
"oneOf": [
{
"$ref": "#/definitions/ProgressPluginOptions"
},
{
"$ref": "#/definitions/HandlerFunction"
}
]
}
1 change: 1 addition & 0 deletions test/NodeTemplatePlugin.test.js
Expand Up @@ -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(
{
Expand Down