1 |
|
2 |
|
3 |
|
4 |
|
5 | "use strict";
|
6 |
|
7 | const validateOptions = require("schema-utils");
|
8 | const schema = require("../schemas/plugins/ProgressPlugin.json");
|
9 |
|
10 |
|
11 |
|
12 |
|
13 | const 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 |
|
50 | class ProgressPlugin {
|
51 | |
52 |
|
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 |
|
251 |
|
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 |
|
298 | ProgressPlugin.defaultOptions = {
|
299 | profile: false,
|
300 | modulesCount: 500,
|
301 | modules: true,
|
302 | activeModules: true,
|
303 |
|
304 | entries: false
|
305 | };
|
306 |
|
307 | module.exports = ProgressPlugin;
|