1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | "use strict";
|
7 |
|
8 | const Stats = require("./Stats");
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 | class Watching {
|
23 | |
24 |
|
25 |
|
26 |
|
27 |
|
28 | constructor(compiler, watchOptions, handler) {
|
29 | this.startTime = null;
|
30 | this.invalid = false;
|
31 | this.handler = handler;
|
32 |
|
33 | this.callbacks = [];
|
34 |
|
35 | this._closeCallbacks = undefined;
|
36 | this.closed = false;
|
37 | this.suspended = false;
|
38 | this.blocked = false;
|
39 | this._isBlocked = () => false;
|
40 | this._onChange = () => {};
|
41 | this._onInvalid = () => {};
|
42 | if (typeof watchOptions === "number") {
|
43 | this.watchOptions = {
|
44 | aggregateTimeout: watchOptions
|
45 | };
|
46 | } else if (watchOptions && typeof watchOptions === "object") {
|
47 | this.watchOptions = { ...watchOptions };
|
48 | } else {
|
49 | this.watchOptions = {};
|
50 | }
|
51 | if (typeof this.watchOptions.aggregateTimeout !== "number") {
|
52 | this.watchOptions.aggregateTimeout = 200;
|
53 | }
|
54 | this.compiler = compiler;
|
55 | this.running = false;
|
56 | this._initial = true;
|
57 | this._invalidReported = true;
|
58 | this._needRecords = true;
|
59 | this.watcher = undefined;
|
60 | this.pausedWatcher = undefined;
|
61 |
|
62 | this._collectedChangedFiles = undefined;
|
63 |
|
64 | this._collectedRemovedFiles = undefined;
|
65 | this._done = this._done.bind(this);
|
66 | process.nextTick(() => {
|
67 | if (this._initial) this._invalidate();
|
68 | });
|
69 | }
|
70 |
|
71 | |
72 |
|
73 |
|
74 |
|
75 | _mergeWithCollected(changedFiles, removedFiles) {
|
76 | if (!changedFiles) return;
|
77 | if (!this._collectedChangedFiles) {
|
78 | this._collectedChangedFiles = new Set(changedFiles);
|
79 | this._collectedRemovedFiles = new Set(removedFiles);
|
80 | } else {
|
81 | for (const file of changedFiles) {
|
82 | this._collectedChangedFiles.add(file);
|
83 | this._collectedRemovedFiles.delete(file);
|
84 | }
|
85 | for (const file of removedFiles) {
|
86 | this._collectedChangedFiles.delete(file);
|
87 | this._collectedRemovedFiles.add(file);
|
88 | }
|
89 | }
|
90 | }
|
91 |
|
92 | |
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 |
|
99 | _go(fileTimeInfoEntries, contextTimeInfoEntries, changedFiles, removedFiles) {
|
100 | this._initial = false;
|
101 | if (this.startTime === null) this.startTime = Date.now();
|
102 | this.running = true;
|
103 | if (this.watcher) {
|
104 | this.pausedWatcher = this.watcher;
|
105 | this.lastWatcherStartTime = Date.now();
|
106 | this.watcher.pause();
|
107 | this.watcher = null;
|
108 | } else if (!this.lastWatcherStartTime) {
|
109 | this.lastWatcherStartTime = Date.now();
|
110 | }
|
111 | this.compiler.fsStartTime = Date.now();
|
112 | this._mergeWithCollected(
|
113 | changedFiles ||
|
114 | (this.pausedWatcher &&
|
115 | this.pausedWatcher.getAggregatedChanges &&
|
116 | this.pausedWatcher.getAggregatedChanges()),
|
117 | (this.compiler.removedFiles =
|
118 | removedFiles ||
|
119 | (this.pausedWatcher &&
|
120 | this.pausedWatcher.getAggregatedRemovals &&
|
121 | this.pausedWatcher.getAggregatedRemovals()))
|
122 | );
|
123 |
|
124 | this.compiler.modifiedFiles = this._collectedChangedFiles;
|
125 | this._collectedChangedFiles = undefined;
|
126 | this.compiler.removedFiles = this._collectedRemovedFiles;
|
127 | this._collectedRemovedFiles = undefined;
|
128 |
|
129 | this.compiler.fileTimestamps =
|
130 | fileTimeInfoEntries ||
|
131 | (this.pausedWatcher && this.pausedWatcher.getFileTimeInfoEntries());
|
132 | this.compiler.contextTimestamps =
|
133 | contextTimeInfoEntries ||
|
134 | (this.pausedWatcher && this.pausedWatcher.getContextTimeInfoEntries());
|
135 |
|
136 | const run = () => {
|
137 | if (this.compiler.idle) {
|
138 | return this.compiler.cache.endIdle(err => {
|
139 | if (err) return this._done(err);
|
140 | this.compiler.idle = false;
|
141 | run();
|
142 | });
|
143 | }
|
144 | if (this._needRecords) {
|
145 | return this.compiler.readRecords(err => {
|
146 | if (err) return this._done(err);
|
147 |
|
148 | this._needRecords = false;
|
149 | run();
|
150 | });
|
151 | }
|
152 | this.invalid = false;
|
153 | this._invalidReported = false;
|
154 | this.compiler.hooks.watchRun.callAsync(this.compiler, err => {
|
155 | if (err) return this._done(err);
|
156 | const onCompiled = (err, compilation) => {
|
157 | if (err) return this._done(err, compilation);
|
158 | if (this.invalid) return this._done(null, compilation);
|
159 |
|
160 | if (this.compiler.hooks.shouldEmit.call(compilation) === false) {
|
161 | return this._done(null, compilation);
|
162 | }
|
163 |
|
164 | process.nextTick(() => {
|
165 | const logger = compilation.getLogger("webpack.Compiler");
|
166 | logger.time("emitAssets");
|
167 | this.compiler.emitAssets(compilation, err => {
|
168 | logger.timeEnd("emitAssets");
|
169 | if (err) return this._done(err, compilation);
|
170 | if (this.invalid) return this._done(null, compilation);
|
171 |
|
172 | logger.time("emitRecords");
|
173 | this.compiler.emitRecords(err => {
|
174 | logger.timeEnd("emitRecords");
|
175 | if (err) return this._done(err, compilation);
|
176 |
|
177 | if (compilation.hooks.needAdditionalPass.call()) {
|
178 | compilation.needAdditionalPass = true;
|
179 |
|
180 | compilation.startTime = this.startTime;
|
181 | compilation.endTime = Date.now();
|
182 | logger.time("done hook");
|
183 | const stats = new Stats(compilation);
|
184 | this.compiler.hooks.done.callAsync(stats, err => {
|
185 | logger.timeEnd("done hook");
|
186 | if (err) return this._done(err, compilation);
|
187 |
|
188 | this.compiler.hooks.additionalPass.callAsync(err => {
|
189 | if (err) return this._done(err, compilation);
|
190 | this.compiler.compile(onCompiled);
|
191 | });
|
192 | });
|
193 | return;
|
194 | }
|
195 | return this._done(null, compilation);
|
196 | });
|
197 | });
|
198 | });
|
199 | };
|
200 | this.compiler.compile(onCompiled);
|
201 | });
|
202 | };
|
203 |
|
204 | run();
|
205 | }
|
206 |
|
207 | |
208 |
|
209 |
|
210 |
|
211 | _getStats(compilation) {
|
212 | const stats = new Stats(compilation);
|
213 | return stats;
|
214 | }
|
215 |
|
216 | |
217 |
|
218 |
|
219 |
|
220 |
|
221 | _done(err, compilation) {
|
222 | this.running = false;
|
223 |
|
224 | const logger = compilation && compilation.getLogger("webpack.Watching");
|
225 |
|
226 | let stats = null;
|
227 |
|
228 | const handleError = (err, cbs) => {
|
229 | this.compiler.hooks.failed.call(err);
|
230 | this.compiler.cache.beginIdle();
|
231 | this.compiler.idle = true;
|
232 | this.handler(err, stats);
|
233 | if (!cbs) {
|
234 | cbs = this.callbacks;
|
235 | this.callbacks = [];
|
236 | }
|
237 | for (const cb of cbs) cb(err);
|
238 | };
|
239 |
|
240 | if (
|
241 | this.invalid &&
|
242 | !this.suspended &&
|
243 | !this.blocked &&
|
244 | !(this._isBlocked() && (this.blocked = true))
|
245 | ) {
|
246 | if (compilation) {
|
247 | logger.time("storeBuildDependencies");
|
248 | this.compiler.cache.storeBuildDependencies(
|
249 | compilation.buildDependencies,
|
250 | err => {
|
251 | logger.timeEnd("storeBuildDependencies");
|
252 | if (err) return handleError(err);
|
253 | this._go();
|
254 | }
|
255 | );
|
256 | } else {
|
257 | this._go();
|
258 | }
|
259 | return;
|
260 | }
|
261 |
|
262 | if (compilation) {
|
263 | compilation.startTime = this.startTime;
|
264 | compilation.endTime = Date.now();
|
265 | stats = new Stats(compilation);
|
266 | }
|
267 | this.startTime = null;
|
268 | if (err) return handleError(err);
|
269 |
|
270 | const cbs = this.callbacks;
|
271 | this.callbacks = [];
|
272 | logger.time("done hook");
|
273 | this.compiler.hooks.done.callAsync(stats, err => {
|
274 | logger.timeEnd("done hook");
|
275 | if (err) return handleError(err, cbs);
|
276 | this.handler(null, stats);
|
277 | logger.time("storeBuildDependencies");
|
278 | this.compiler.cache.storeBuildDependencies(
|
279 | compilation.buildDependencies,
|
280 | err => {
|
281 | logger.timeEnd("storeBuildDependencies");
|
282 | if (err) return handleError(err, cbs);
|
283 | logger.time("beginIdle");
|
284 | this.compiler.cache.beginIdle();
|
285 | this.compiler.idle = true;
|
286 | logger.timeEnd("beginIdle");
|
287 | process.nextTick(() => {
|
288 | if (!this.closed) {
|
289 | this.watch(
|
290 | compilation.fileDependencies,
|
291 | compilation.contextDependencies,
|
292 | compilation.missingDependencies
|
293 | );
|
294 | }
|
295 | });
|
296 | for (const cb of cbs) cb(null);
|
297 | this.compiler.hooks.afterDone.call(stats);
|
298 | }
|
299 | );
|
300 | });
|
301 | }
|
302 |
|
303 | |
304 |
|
305 |
|
306 |
|
307 |
|
308 |
|
309 | watch(files, dirs, missing) {
|
310 | this.pausedWatcher = null;
|
311 | this.watcher = this.compiler.watchFileSystem.watch(
|
312 | files,
|
313 | dirs,
|
314 | missing,
|
315 | this.lastWatcherStartTime,
|
316 | this.watchOptions,
|
317 | (
|
318 | err,
|
319 | fileTimeInfoEntries,
|
320 | contextTimeInfoEntries,
|
321 | changedFiles,
|
322 | removedFiles
|
323 | ) => {
|
324 | if (err) {
|
325 | this.compiler.modifiedFiles = undefined;
|
326 | this.compiler.removedFiles = undefined;
|
327 | this.compiler.fileTimestamps = undefined;
|
328 | this.compiler.contextTimestamps = undefined;
|
329 | this.compiler.fsStartTime = undefined;
|
330 | return this.handler(err);
|
331 | }
|
332 | this._invalidate(
|
333 | fileTimeInfoEntries,
|
334 | contextTimeInfoEntries,
|
335 | changedFiles,
|
336 | removedFiles
|
337 | );
|
338 | this._onChange();
|
339 | },
|
340 | (fileName, changeTime) => {
|
341 | if (!this._invalidReported) {
|
342 | this._invalidReported = true;
|
343 | this.compiler.hooks.invalid.call(fileName, changeTime);
|
344 | }
|
345 | this._onInvalid();
|
346 | }
|
347 | );
|
348 | }
|
349 |
|
350 | |
351 |
|
352 |
|
353 |
|
354 | invalidate(callback) {
|
355 | if (callback) {
|
356 | this.callbacks.push(callback);
|
357 | }
|
358 | if (!this._invalidReported) {
|
359 | this._invalidReported = true;
|
360 | this.compiler.hooks.invalid.call(null, Date.now());
|
361 | }
|
362 | this._onChange();
|
363 | this._invalidate();
|
364 | }
|
365 |
|
366 | _invalidate(
|
367 | fileTimeInfoEntries,
|
368 | contextTimeInfoEntries,
|
369 | changedFiles,
|
370 | removedFiles
|
371 | ) {
|
372 | if (this.suspended || (this._isBlocked() && (this.blocked = true))) {
|
373 | this._mergeWithCollected(changedFiles, removedFiles);
|
374 | return;
|
375 | }
|
376 |
|
377 | if (this.running) {
|
378 | this._mergeWithCollected(changedFiles, removedFiles);
|
379 | this.invalid = true;
|
380 | } else {
|
381 | this._go(
|
382 | fileTimeInfoEntries,
|
383 | contextTimeInfoEntries,
|
384 | changedFiles,
|
385 | removedFiles
|
386 | );
|
387 | }
|
388 | }
|
389 |
|
390 | suspend() {
|
391 | this.suspended = true;
|
392 | }
|
393 |
|
394 | resume() {
|
395 | if (this.suspended) {
|
396 | this.suspended = false;
|
397 | this._invalidate();
|
398 | }
|
399 | }
|
400 |
|
401 | |
402 |
|
403 |
|
404 |
|
405 | close(callback) {
|
406 | if (this._closeCallbacks) {
|
407 | if (callback) {
|
408 | this._closeCallbacks.push(callback);
|
409 | }
|
410 | return;
|
411 | }
|
412 | const finalCallback = (err, compilation) => {
|
413 | this.running = false;
|
414 | this.compiler.running = false;
|
415 | this.compiler.watching = undefined;
|
416 | this.compiler.watchMode = false;
|
417 | this.compiler.modifiedFiles = undefined;
|
418 | this.compiler.removedFiles = undefined;
|
419 | this.compiler.fileTimestamps = undefined;
|
420 | this.compiler.contextTimestamps = undefined;
|
421 | this.compiler.fsStartTime = undefined;
|
422 | const shutdown = err => {
|
423 | this.compiler.hooks.watchClose.call();
|
424 | const closeCallbacks = this._closeCallbacks;
|
425 | this._closeCallbacks = undefined;
|
426 | for (const cb of closeCallbacks) cb(err);
|
427 | };
|
428 | if (compilation) {
|
429 | const logger = compilation.getLogger("webpack.Watching");
|
430 | logger.time("storeBuildDependencies");
|
431 | this.compiler.cache.storeBuildDependencies(
|
432 | compilation.buildDependencies,
|
433 | err2 => {
|
434 | logger.timeEnd("storeBuildDependencies");
|
435 | shutdown(err || err2);
|
436 | }
|
437 | );
|
438 | } else {
|
439 | shutdown(err);
|
440 | }
|
441 | };
|
442 |
|
443 | this.closed = true;
|
444 | if (this.watcher) {
|
445 | this.watcher.close();
|
446 | this.watcher = null;
|
447 | }
|
448 | if (this.pausedWatcher) {
|
449 | this.pausedWatcher.close();
|
450 | this.pausedWatcher = null;
|
451 | }
|
452 | this._closeCallbacks = [];
|
453 | if (callback) {
|
454 | this._closeCallbacks.push(callback);
|
455 | }
|
456 | if (this.running) {
|
457 | this.invalid = true;
|
458 | this._done = finalCallback;
|
459 | } else {
|
460 | finalCallback();
|
461 | }
|
462 | }
|
463 | }
|
464 |
|
465 | module.exports = Watching;
|