UNPKG

9.34 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5"use strict";
6
7const validateOptions = require("schema-utils");
8const schema = require("../schemas/plugins/ProgressPlugin.json");
9
10/** @typedef {import("../declarations/plugins/ProgressPlugin").ProgressPluginArgument} ProgressPluginArgument */
11/** @typedef {import("../declarations/plugins/ProgressPlugin").ProgressPluginOptions} ProgressPluginOptions */
12
13const createDefaultHandler = (profile, logger) => {
14 let lastState;
15 let lastStateTime;
16
17 const defaultHandler = (percentage, msg, ...args) => {
18 logger.status(`${Math.floor(percentage * 100)}%`, msg, ...args);
19 if (profile) {
20 let state = msg;
21 state = state.replace(/^\d+\/\d+\s+/, "");
22 if (percentage === 0) {
23 lastState = null;
24 lastStateTime = Date.now();
25 } else if (state !== lastState || percentage === 1) {
26 const now = Date.now();
27 if (lastState) {
28 const diff = now - lastStateTime;
29 const stateMsg = `${diff}ms ${lastState}`;
30 if (diff > 1000) {
31 logger.warn(stateMsg);
32 } else if (diff > 10) {
33 logger.info(stateMsg);
34 } else if (diff > 0) {
35 logger.log(stateMsg);
36 } else {
37 logger.debug(stateMsg);
38 }
39 }
40 lastState = state;
41 lastStateTime = now;
42 }
43 }
44 if (percentage === 1) logger.status();
45 };
46
47 return defaultHandler;
48};
49
50class ProgressPlugin {
51 /**
52 * @param {ProgressPluginArgument} options options
53 */
54 constructor(options) {
55 if (typeof options === "function") {
56 options = {
57 handler: options
58 };
59 }
60
61 options = options || {};
62 validateOptions(schema, options, "Progress Plugin");
63 options = Object.assign({}, ProgressPlugin.defaultOptions, options);
64
65 this.profile = options.profile;
66 this.handler = options.handler;
67 this.modulesCount = options.modulesCount;
68 this.showEntries = options.entries;
69 this.showModules = options.modules;
70 this.showActiveModules = options.activeModules;
71 }
72
73 apply(compiler) {
74 const { modulesCount } = this;
75 const handler =
76 this.handler ||
77 createDefaultHandler(
78 this.profile,
79 compiler.getInfrastructureLogger("webpack.Progress")
80 );
81 const showEntries = this.showEntries;
82 const showModules = this.showModules;
83 const showActiveModules = this.showActiveModules;
84 if (compiler.compilers) {
85 const states = new Array(compiler.compilers.length);
86 compiler.compilers.forEach((compiler, idx) => {
87 new ProgressPlugin((p, msg, ...args) => {
88 states[idx] = [p, msg, ...args];
89 handler(
90 states
91 .map(state => (state && state[0]) || 0)
92 .reduce((a, b) => a + b) / states.length,
93 `[${idx}] ${msg}`,
94 ...args
95 );
96 }).apply(compiler);
97 });
98 } else {
99 let lastModulesCount = 0;
100 let lastEntriesCount = 0;
101 let moduleCount = modulesCount;
102 let entriesCount = 1;
103 let doneModules = 0;
104 let doneEntries = 0;
105 const activeModules = new Set();
106 let lastActiveModule = "";
107
108 const update = () => {
109 const percentByModules =
110 doneModules / Math.max(lastModulesCount, moduleCount);
111 const percentByEntries =
112 doneEntries / Math.max(lastEntriesCount, entriesCount);
113
114 const items = [
115 0.1 + Math.max(percentByModules, percentByEntries) * 0.6,
116 "building"
117 ];
118 if (showEntries) {
119 items.push(`${doneEntries}/${entriesCount} entries`);
120 }
121 if (showModules) {
122 items.push(`${doneModules}/${moduleCount} modules`);
123 }
124 if (showActiveModules) {
125 items.push(`${activeModules.size} active`);
126 items.push(lastActiveModule);
127 }
128 handler(...items);
129 };
130
131 const moduleAdd = module => {
132 moduleCount++;
133 if (showActiveModules) {
134 const ident = module.identifier();
135 if (ident) {
136 activeModules.add(ident);
137 lastActiveModule = ident;
138 }
139 }
140 update();
141 };
142
143 const entryAdd = (entry, name) => {
144 entriesCount++;
145 update();
146 };
147
148 const moduleDone = module => {
149 doneModules++;
150 if (showActiveModules) {
151 const ident = module.identifier();
152 if (ident) {
153 activeModules.delete(ident);
154 if (lastActiveModule === ident) {
155 lastActiveModule = "";
156 for (const m of activeModules) {
157 lastActiveModule = m;
158 }
159 }
160 }
161 }
162 update();
163 };
164
165 const entryDone = (entry, name) => {
166 doneEntries++;
167 update();
168 };
169
170 compiler.hooks.compilation.tap("ProgressPlugin", compilation => {
171 if (compilation.compiler.isChild()) return;
172 lastModulesCount = moduleCount;
173 lastEntriesCount = entriesCount;
174 moduleCount = entriesCount = 0;
175 doneModules = doneEntries = 0;
176 handler(0, "compiling");
177
178 compilation.hooks.buildModule.tap("ProgressPlugin", moduleAdd);
179 compilation.hooks.failedModule.tap("ProgressPlugin", moduleDone);
180 compilation.hooks.succeedModule.tap("ProgressPlugin", moduleDone);
181
182 compilation.hooks.addEntry.tap("ProgressPlugin", entryAdd);
183 compilation.hooks.failedEntry.tap("ProgressPlugin", entryDone);
184 compilation.hooks.succeedEntry.tap("ProgressPlugin", entryDone);
185
186 const hooks = {
187 finishModules: "finish module graph",
188 seal: "sealing",
189 beforeChunks: "chunk graph",
190 afterChunks: "after chunk graph",
191 optimizeDependenciesBasic: "basic dependencies optimization",
192 optimizeDependencies: "dependencies optimization",
193 optimizeDependenciesAdvanced: "advanced dependencies optimization",
194 afterOptimizeDependencies: "after dependencies optimization",
195 optimize: "optimizing",
196 optimizeModulesBasic: "basic module optimization",
197 optimizeModules: "module optimization",
198 optimizeModulesAdvanced: "advanced module optimization",
199 afterOptimizeModules: "after module optimization",
200 optimizeChunksBasic: "basic chunk optimization",
201 optimizeChunks: "chunk optimization",
202 optimizeChunksAdvanced: "advanced chunk optimization",
203 afterOptimizeChunks: "after chunk optimization",
204 optimizeTree: "module and chunk tree optimization",
205 afterOptimizeTree: "after module and chunk tree optimization",
206 optimizeChunkModulesBasic: "basic chunk modules optimization",
207 optimizeChunkModules: "chunk modules optimization",
208 optimizeChunkModulesAdvanced: "advanced chunk modules optimization",
209 afterOptimizeChunkModules: "after chunk modules optimization",
210 reviveModules: "module reviving",
211 optimizeModuleOrder: "module order optimization",
212 advancedOptimizeModuleOrder: "advanced module order optimization",
213 beforeModuleIds: "before module ids",
214 moduleIds: "module ids",
215 optimizeModuleIds: "module id optimization",
216 afterOptimizeModuleIds: "module id optimization",
217 reviveChunks: "chunk reviving",
218 optimizeChunkOrder: "chunk order optimization",
219 beforeChunkIds: "before chunk ids",
220 optimizeChunkIds: "chunk id optimization",
221 afterOptimizeChunkIds: "after chunk id optimization",
222 recordModules: "record modules",
223 recordChunks: "record chunks",
224 beforeHash: "hashing",
225 afterHash: "after hashing",
226 recordHash: "record hash",
227 beforeModuleAssets: "module assets processing",
228 beforeChunkAssets: "chunk assets processing",
229 additionalChunkAssets: "additional chunk assets processing",
230 record: "recording",
231 additionalAssets: "additional asset processing",
232 optimizeChunkAssets: "chunk asset optimization",
233 afterOptimizeChunkAssets: "after chunk asset optimization",
234 optimizeAssets: "asset optimization",
235 afterOptimizeAssets: "after asset optimization",
236 afterSeal: "after seal"
237 };
238 const numberOfHooks = Object.keys(hooks).length;
239 Object.keys(hooks).forEach((name, idx) => {
240 const title = hooks[name];
241 const percentage = (idx / numberOfHooks) * 0.25 + 0.7;
242 compilation.hooks[name].intercept({
243 name: "ProgressPlugin",
244 context: true,
245 call: () => {
246 handler(percentage, title);
247 },
248 tap: (context, tap) => {
249 if (context) {
250 // p is percentage from 0 to 1
251 // args is any number of messages in a hierarchical matter
252 context.reportProgress = (p, ...args) => {
253 handler(percentage, title, tap.name, ...args);
254 };
255 }
256 handler(percentage, title, tap.name);
257 }
258 });
259 });
260 });
261 compiler.hooks.emit.intercept({
262 name: "ProgressPlugin",
263 context: true,
264 call: () => {
265 handler(0.95, "emitting");
266 },
267 tap: (context, tap) => {
268 if (context) {
269 context.reportProgress = (p, ...args) => {
270 handler(0.95, "emitting", tap.name, ...args);
271 };
272 }
273 handler(0.95, "emitting", tap.name);
274 }
275 });
276 compiler.hooks.afterEmit.intercept({
277 name: "ProgressPlugin",
278 context: true,
279 call: () => {
280 handler(0.98, "after emitting");
281 },
282 tap: (context, tap) => {
283 if (context) {
284 context.reportProgress = (p, ...args) => {
285 handler(0.98, "after emitting", tap.name, ...args);
286 };
287 }
288 handler(0.98, "after emitting", tap.name);
289 }
290 });
291 compiler.hooks.done.tap("ProgressPlugin", () => {
292 handler(1, "");
293 });
294 }
295 }
296}
297
298ProgressPlugin.defaultOptions = {
299 profile: false,
300 modulesCount: 500,
301 modules: true,
302 activeModules: true,
303 // TODO webpack 5 default this to true
304 entries: false
305};
306
307module.exports = ProgressPlugin;