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

main
Last change on this file was 79a0317, checked in by stefan toskovski <stefantoska84@…>, 3 days ago

F4 Finalna Verzija

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