source: frontend/node_modules/webpack/lib/Watching.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 11 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 14.9 KB
Line 
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const Stats = require("./Stats");
9
10/** @typedef {import("../declarations/WebpackOptions").WatchOptions} WatchOptions */
11/** @typedef {import("./Compilation")} Compilation */
12/** @typedef {import("./Compiler")} Compiler */
13/** @typedef {import("./Compiler").ErrorCallback} ErrorCallback */
14/** @typedef {import("./logging/Logger").Logger} Logger */
15/** @typedef {import("./util/fs").TimeInfoEntries} TimeInfoEntries */
16/** @typedef {import("./util/fs").WatchFileSystem} WatchFileSystem */
17/** @typedef {import("./util/fs").Watcher} Watcher */
18
19/**
20 * Defines the callback type used by this module.
21 * @template T
22 * @template [R=void]
23 * @typedef {import("./webpack").Callback<T, R>} Callback
24 */
25
26/** @typedef {Set<string>} CollectedFiles */
27
28class Watching {
29 /**
30 * Creates an instance of Watching.
31 * @param {Compiler} compiler the compiler
32 * @param {WatchOptions} watchOptions options
33 * @param {Callback<Stats>} handler completion handler
34 */
35 constructor(compiler, watchOptions, handler) {
36 /** @type {null | number} */
37 this.startTime = null;
38 this.invalid = false;
39 /** @type {Callback<Stats>} */
40 this.handler = handler;
41 /** @type {ErrorCallback[]} */
42 this.callbacks = [];
43 /** @type {ErrorCallback[] | undefined} */
44 this._closeCallbacks = undefined;
45 this.closed = false;
46 this.suspended = false;
47 this.blocked = false;
48 this._isBlocked = () => false;
49 this._onChange = () => {};
50 this._onInvalid = () => {};
51 if (typeof watchOptions === "number") {
52 /** @type {WatchOptions} */
53 this.watchOptions = {
54 aggregateTimeout: watchOptions
55 };
56 } else if (watchOptions && typeof watchOptions === "object") {
57 /** @type {WatchOptions} */
58 this.watchOptions = { ...watchOptions };
59 } else {
60 /** @type {WatchOptions} */
61 this.watchOptions = {};
62 }
63 if (typeof this.watchOptions.aggregateTimeout !== "number") {
64 this.watchOptions.aggregateTimeout = 20;
65 }
66 this.compiler = compiler;
67 this.running = false;
68 this._initial = true;
69 this._invalidReported = true;
70 this._needRecords = true;
71 /** @type {undefined | null | Watcher} */
72 this.watcher = undefined;
73 /** @type {undefined | null | Watcher} */
74 this.pausedWatcher = undefined;
75 /** @type {CollectedFiles | undefined} */
76 this._collectedChangedFiles = undefined;
77 /** @type {CollectedFiles | undefined} */
78 this._collectedRemovedFiles = undefined;
79 this._done = this._done.bind(this);
80 process.nextTick(() => {
81 if (this._initial) this._invalidate();
82 });
83 }
84
85 /**
86 * Merge with collected.
87 * @param {ReadonlySet<string> | undefined | null} changedFiles changed files
88 * @param {ReadonlySet<string> | undefined | null} removedFiles removed files
89 */
90 _mergeWithCollected(changedFiles, removedFiles) {
91 if (!changedFiles) return;
92 if (!this._collectedChangedFiles) {
93 this._collectedChangedFiles = new Set(changedFiles);
94 this._collectedRemovedFiles = new Set(removedFiles);
95 } else {
96 for (const file of changedFiles) {
97 this._collectedChangedFiles.add(file);
98 /** @type {CollectedFiles} */
99 (this._collectedRemovedFiles).delete(file);
100 }
101 for (const file of /** @type {ReadonlySet<string>} */ (removedFiles)) {
102 this._collectedChangedFiles.delete(file);
103 /** @type {CollectedFiles} */
104 (this._collectedRemovedFiles).add(file);
105 }
106 }
107 }
108
109 /**
110 * Processes the provided file time info entries.
111 * @param {TimeInfoEntries=} fileTimeInfoEntries info for files
112 * @param {TimeInfoEntries=} contextTimeInfoEntries info for directories
113 * @param {ReadonlySet<string>=} changedFiles changed files
114 * @param {ReadonlySet<string>=} removedFiles removed files
115 * @returns {void}
116 */
117 _go(fileTimeInfoEntries, contextTimeInfoEntries, changedFiles, removedFiles) {
118 this._initial = false;
119 if (this.startTime === null) this.startTime = Date.now();
120 this.running = true;
121 if (this.watcher) {
122 this.pausedWatcher = this.watcher;
123 this.lastWatcherStartTime = Date.now();
124 this.watcher.pause();
125 this.watcher = null;
126 } else if (!this.lastWatcherStartTime) {
127 this.lastWatcherStartTime = Date.now();
128 }
129 this.compiler.fsStartTime = Date.now();
130 if (
131 changedFiles &&
132 removedFiles &&
133 fileTimeInfoEntries &&
134 contextTimeInfoEntries
135 ) {
136 this._mergeWithCollected(changedFiles, removedFiles);
137 this.compiler.fileTimestamps = fileTimeInfoEntries;
138 this.compiler.contextTimestamps = contextTimeInfoEntries;
139 } else if (this.pausedWatcher) {
140 if (this.pausedWatcher.getInfo) {
141 const {
142 changes,
143 removals,
144 fileTimeInfoEntries,
145 contextTimeInfoEntries
146 } = this.pausedWatcher.getInfo();
147 this._mergeWithCollected(changes, removals);
148 this.compiler.fileTimestamps = fileTimeInfoEntries;
149 this.compiler.contextTimestamps = contextTimeInfoEntries;
150 } else {
151 this._mergeWithCollected(
152 this.pausedWatcher.getAggregatedChanges &&
153 this.pausedWatcher.getAggregatedChanges(),
154 this.pausedWatcher.getAggregatedRemovals &&
155 this.pausedWatcher.getAggregatedRemovals()
156 );
157 this.compiler.fileTimestamps =
158 this.pausedWatcher.getFileTimeInfoEntries();
159 this.compiler.contextTimestamps =
160 this.pausedWatcher.getContextTimeInfoEntries();
161 }
162 }
163 this.compiler.modifiedFiles = this._collectedChangedFiles;
164 this._collectedChangedFiles = undefined;
165 this.compiler.removedFiles = this._collectedRemovedFiles;
166 this._collectedRemovedFiles = undefined;
167
168 const run = () => {
169 if (this.compiler.idle) {
170 return this.compiler.cache.endIdle((err) => {
171 if (err) return this._done(err);
172 this.compiler.idle = false;
173 run();
174 });
175 }
176 if (this._needRecords) {
177 return this.compiler.readRecords((err) => {
178 if (err) return this._done(err);
179
180 this._needRecords = false;
181 run();
182 });
183 }
184 this.invalid = false;
185 this._invalidReported = false;
186 this.compiler.hooks.watchRun.callAsync(this.compiler, (err) => {
187 if (err) return this._done(err);
188 /**
189 * Processes the provided err.
190 * @param {Error | null} err error
191 * @param {Compilation=} _compilation compilation
192 * @returns {void}
193 */
194 const onCompiled = (err, _compilation) => {
195 if (err) return this._done(err, _compilation);
196
197 const compilation = /** @type {Compilation} */ (_compilation);
198
199 if (this.compiler.hooks.shouldEmit.call(compilation) === false) {
200 return this._done(null, compilation);
201 }
202
203 process.nextTick(() => {
204 const logger = compilation.getLogger("webpack.Compiler");
205 logger.time("emitAssets");
206 this.compiler.emitAssets(compilation, (err) => {
207 logger.timeEnd("emitAssets");
208 if (err) return this._done(err, compilation);
209 if (this.invalid) return this._done(null, compilation);
210
211 logger.time("emitRecords");
212 this.compiler.emitRecords((err) => {
213 logger.timeEnd("emitRecords");
214 if (err) return this._done(err, compilation);
215
216 if (compilation.hooks.needAdditionalPass.call()) {
217 compilation.needAdditionalPass = true;
218
219 compilation.startTime = /** @type {number} */ (
220 this.startTime
221 );
222 compilation.endTime = Date.now();
223 logger.time("done hook");
224 const stats = new Stats(compilation);
225 this.compiler.hooks.done.callAsync(stats, (err) => {
226 logger.timeEnd("done hook");
227 if (err) return this._done(err, compilation);
228
229 this.compiler.hooks.additionalPass.callAsync((err) => {
230 if (err) return this._done(err, compilation);
231 this.compiler.compile(onCompiled);
232 });
233 });
234 return;
235 }
236 return this._done(null, compilation);
237 });
238 });
239 });
240 };
241 this.compiler.compile(onCompiled);
242 });
243 };
244
245 run();
246 }
247
248 /**
249 * Returns the compilation stats.
250 * @param {Compilation} compilation the compilation
251 * @returns {Stats} the compilation stats
252 */
253 _getStats(compilation) {
254 const stats = new Stats(compilation);
255 return stats;
256 }
257
258 /**
259 * Processes the provided err.
260 * @param {(Error | null)=} err an optional error
261 * @param {Compilation=} compilation the compilation
262 * @returns {void}
263 */
264 _done(err, compilation) {
265 this.running = false;
266
267 const logger =
268 /** @type {Logger} */
269 (compilation && compilation.getLogger("webpack.Watching"));
270
271 /** @type {Stats | undefined} */
272 let stats;
273
274 /**
275 * Processes the provided err.
276 * @param {Error} err error
277 * @param {ErrorCallback[]=} cbs callbacks
278 */
279 const handleError = (err, cbs) => {
280 this.compiler.hooks.failed.call(err);
281 this.compiler.cache.beginIdle();
282 this.compiler.idle = true;
283 this.handler(err, /** @type {Stats} */ (stats));
284 if (!cbs) {
285 cbs = this.callbacks;
286 this.callbacks = [];
287 }
288 for (const cb of cbs) cb(err);
289 };
290
291 if (
292 this.invalid &&
293 !this.suspended &&
294 !this.blocked &&
295 !(this._isBlocked() && (this.blocked = true))
296 ) {
297 if (compilation) {
298 logger.time("storeBuildDependencies");
299 this.compiler.cache.storeBuildDependencies(
300 compilation.buildDependencies,
301 (err) => {
302 logger.timeEnd("storeBuildDependencies");
303 if (err) return handleError(err);
304 this._go();
305 }
306 );
307 } else {
308 this._go();
309 }
310 return;
311 }
312
313 if (compilation) {
314 compilation.startTime = /** @type {number} */ (this.startTime);
315 compilation.endTime = Date.now();
316 stats = new Stats(compilation);
317 }
318 this.startTime = null;
319 if (err) return handleError(err);
320
321 const cbs = this.callbacks;
322 this.callbacks = [];
323 logger.time("done hook");
324 this.compiler.hooks.done.callAsync(/** @type {Stats} */ (stats), (err) => {
325 logger.timeEnd("done hook");
326 if (err) return handleError(err, cbs);
327 this.handler(null, stats);
328 logger.time("storeBuildDependencies");
329 this.compiler.cache.storeBuildDependencies(
330 /** @type {Compilation} */
331 (compilation).buildDependencies,
332 (err) => {
333 logger.timeEnd("storeBuildDependencies");
334 if (err) return handleError(err, cbs);
335 logger.time("beginIdle");
336 this.compiler.cache.beginIdle();
337 this.compiler.idle = true;
338 logger.timeEnd("beginIdle");
339 process.nextTick(() => {
340 if (!this.closed) {
341 this.watch(
342 /** @type {Compilation} */
343 (compilation).fileDependencies,
344 /** @type {Compilation} */
345 (compilation).contextDependencies,
346 /** @type {Compilation} */
347 (compilation).missingDependencies
348 );
349 }
350 });
351 for (const cb of cbs) cb(null);
352 this.compiler.hooks.afterDone.call(/** @type {Stats} */ (stats));
353 }
354 );
355 });
356 }
357
358 /**
359 * Processes the provided file.
360 * @param {Iterable<string>} files watched files
361 * @param {Iterable<string>} dirs watched directories
362 * @param {Iterable<string>} missing watched existence entries
363 * @returns {void}
364 */
365 watch(files, dirs, missing) {
366 this.pausedWatcher = null;
367 this.watcher =
368 /** @type {WatchFileSystem} */
369 (this.compiler.watchFileSystem).watch(
370 files,
371 dirs,
372 missing,
373 /** @type {number} */ (this.lastWatcherStartTime),
374 this.watchOptions,
375 (
376 err,
377 fileTimeInfoEntries,
378 contextTimeInfoEntries,
379 changedFiles,
380 removedFiles
381 ) => {
382 if (err) {
383 this.compiler.modifiedFiles = undefined;
384 this.compiler.removedFiles = undefined;
385 this.compiler.fileTimestamps = undefined;
386 this.compiler.contextTimestamps = undefined;
387 this.compiler.fsStartTime = undefined;
388 return this.handler(err);
389 }
390 this._invalidate(
391 fileTimeInfoEntries,
392 contextTimeInfoEntries,
393 changedFiles,
394 removedFiles
395 );
396 this._onChange();
397 },
398 (fileName, changeTime) => {
399 if (!this._invalidReported) {
400 this._invalidReported = true;
401 this.compiler.hooks.invalid.call(fileName, changeTime);
402 }
403 this._onInvalid();
404 }
405 );
406 }
407
408 /**
409 * Processes the provided error callback.
410 * @param {ErrorCallback=} callback signals when the build has completed again
411 * @returns {void}
412 */
413 invalidate(callback) {
414 if (callback) {
415 this.callbacks.push(callback);
416 }
417 if (!this._invalidReported) {
418 this._invalidReported = true;
419 this.compiler.hooks.invalid.call(null, Date.now());
420 }
421 this._onChange();
422 this._invalidate();
423 }
424
425 /**
426 * Processes the provided file time info entries.
427 * @param {TimeInfoEntries=} fileTimeInfoEntries info for files
428 * @param {TimeInfoEntries=} contextTimeInfoEntries info for directories
429 * @param {ReadonlySet<string>=} changedFiles changed files
430 * @param {ReadonlySet<string>=} removedFiles removed files
431 * @returns {void}
432 */
433 _invalidate(
434 fileTimeInfoEntries,
435 contextTimeInfoEntries,
436 changedFiles,
437 removedFiles
438 ) {
439 if (this.suspended || (this._isBlocked() && (this.blocked = true))) {
440 this._mergeWithCollected(changedFiles, removedFiles);
441 return;
442 }
443
444 if (this.running) {
445 this._mergeWithCollected(changedFiles, removedFiles);
446 this.invalid = true;
447 } else {
448 this._go(
449 fileTimeInfoEntries,
450 contextTimeInfoEntries,
451 changedFiles,
452 removedFiles
453 );
454 }
455 }
456
457 suspend() {
458 this.suspended = true;
459 }
460
461 resume() {
462 if (this.suspended) {
463 this.suspended = false;
464 this._invalidate();
465 }
466 }
467
468 /**
469 * Processes the provided error callback.
470 * @param {ErrorCallback} callback signals when the watcher is closed
471 * @returns {void}
472 */
473 close(callback) {
474 if (this._closeCallbacks) {
475 if (callback) {
476 this._closeCallbacks.push(callback);
477 }
478 return;
479 }
480 /**
481 * Processes the provided err.
482 * @param {Error | null} err error if any
483 * @param {Compilation=} compilation compilation if any
484 */
485 const finalCallback = (err, compilation) => {
486 this.running = false;
487 this.compiler.running = false;
488 this.compiler.watching = undefined;
489 this.compiler.watchMode = false;
490 this.compiler.modifiedFiles = undefined;
491 this.compiler.removedFiles = undefined;
492 this.compiler.fileTimestamps = undefined;
493 this.compiler.contextTimestamps = undefined;
494 this.compiler.fsStartTime = undefined;
495 /**
496 * Processes the provided err.
497 * @param {Error | null} err error if any
498 */
499 const shutdown = (err) => {
500 this.compiler.hooks.watchClose.call();
501 const closeCallbacks =
502 /** @type {ErrorCallback[]} */
503 (this._closeCallbacks);
504 this._closeCallbacks = undefined;
505 for (const cb of closeCallbacks) cb(err);
506 };
507 if (compilation) {
508 const logger = compilation.getLogger("webpack.Watching");
509 logger.time("storeBuildDependencies");
510 this.compiler.cache.storeBuildDependencies(
511 compilation.buildDependencies,
512 (err2) => {
513 logger.timeEnd("storeBuildDependencies");
514 shutdown(err || err2);
515 }
516 );
517 } else {
518 shutdown(err);
519 }
520 };
521
522 this.closed = true;
523 if (this.watcher) {
524 this.watcher.close();
525 this.watcher = null;
526 }
527 if (this.pausedWatcher) {
528 this.pausedWatcher.close();
529 this.pausedWatcher = null;
530 }
531 this._closeCallbacks = [];
532 if (callback) {
533 this._closeCallbacks.push(callback);
534 }
535 if (this.running) {
536 this.invalid = true;
537 this._done = finalCallback;
538 } else {
539 finalCallback(null);
540 }
541 }
542}
543
544module.exports = Watching;
Note: See TracBrowser for help on using the repository browser.