| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | /* | 
					
						
							|  |  |  | 	MIT License http://www.opensource.org/licenses/mit-license.php
 | 
					
						
							|  |  |  | 	Author Tobias Koppers @sokra | 
					
						
							|  |  |  | */ | 
					
						
							| 
									
										
										
										
											2018-07-30 23:08:51 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | "use strict"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const Stats = require("./Stats"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-10 18:34:59 +08:00
										 |  |  | /** @typedef {import("../declarations/WebpackOptions").WatchOptions} WatchOptions */ | 
					
						
							|  |  |  | /** @typedef {import("./Compilation")} Compilation */ | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | /** @typedef {import("./Compiler")} Compiler */ | 
					
						
							| 
									
										
										
										
											2018-12-10 18:34:59 +08:00
										 |  |  | /** @typedef {import("./Stats")} Stats */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @template T | 
					
						
							|  |  |  |  * @callback Callback | 
					
						
							|  |  |  |  * @param {Error=} err | 
					
						
							|  |  |  |  * @param {T=} result | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | class Watching { | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {Compiler} compiler the compiler | 
					
						
							| 
									
										
										
										
											2018-12-10 18:34:59 +08:00
										 |  |  | 	 * @param {WatchOptions} watchOptions options | 
					
						
							|  |  |  | 	 * @param {Callback<Stats>} handler completion handler | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | 	constructor(compiler, watchOptions, handler) { | 
					
						
							|  |  |  | 		this.startTime = null; | 
					
						
							|  |  |  | 		this.invalid = false; | 
					
						
							|  |  |  | 		this.handler = handler; | 
					
						
							| 
									
										
										
										
											2018-12-10 18:34:59 +08:00
										 |  |  | 		/** @type {Callback<void>[]} */ | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | 		this.callbacks = []; | 
					
						
							|  |  |  | 		this.closed = false; | 
					
						
							| 
									
										
										
										
											2019-06-01 00:53:33 +08:00
										 |  |  | 		this.suspended = false; | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		if (typeof watchOptions === "number") { | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | 			this.watchOptions = { | 
					
						
							|  |  |  | 				aggregateTimeout: watchOptions | 
					
						
							|  |  |  | 			}; | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		} else if (watchOptions && typeof watchOptions === "object") { | 
					
						
							| 
									
										
										
										
											2019-06-19 19:16:05 +08:00
										 |  |  | 			this.watchOptions = { ...watchOptions }; | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			this.watchOptions = {}; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		this.watchOptions.aggregateTimeout = | 
					
						
							|  |  |  | 			this.watchOptions.aggregateTimeout || 200; | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | 		this.compiler = compiler; | 
					
						
							|  |  |  | 		this.running = true; | 
					
						
							|  |  |  | 		this.compiler.readRecords(err => { | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 			if (err) return this._done(err); | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			this._go(); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	_go() { | 
					
						
							|  |  |  | 		this.startTime = Date.now(); | 
					
						
							|  |  |  | 		this.running = true; | 
					
						
							|  |  |  | 		this.invalid = false; | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		this.compiler.cache.endIdle(err => { | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 			if (err) return this._done(err); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 			this.compiler.hooks.watchRun.callAsync(this.compiler, err => { | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 				if (err) return this._done(err); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 				const onCompiled = (err, compilation) => { | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 					if (err) return this._done(err); | 
					
						
							|  |  |  | 					if (this.invalid) return this._done(); | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 					if (this.compiler.hooks.shouldEmit.call(compilation) === false) { | 
					
						
							|  |  |  | 						return this._done(null, compilation); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-05 20:49:42 +08:00
										 |  |  | 					process.nextTick(() => { | 
					
						
							|  |  |  | 						this.compiler.emitAssets(compilation, err => { | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 							if (err) return this._done(err); | 
					
						
							| 
									
										
										
										
											2019-01-05 20:49:42 +08:00
										 |  |  | 							if (this.invalid) return this._done(); | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-05 20:49:42 +08:00
										 |  |  | 							this.compiler.emitRecords(err => { | 
					
						
							|  |  |  | 								if (err) return this._done(err); | 
					
						
							| 
									
										
										
										
											2018-01-25 20:56:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-05 20:49:42 +08:00
										 |  |  | 								if (compilation.hooks.needAdditionalPass.call()) { | 
					
						
							|  |  |  | 									compilation.needAdditionalPass = true; | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-05 20:49:42 +08:00
										 |  |  | 									const stats = new Stats(compilation); | 
					
						
							|  |  |  | 									stats.startTime = this.startTime; | 
					
						
							|  |  |  | 									stats.endTime = Date.now(); | 
					
						
							|  |  |  | 									this.compiler.hooks.done.callAsync(stats, err => { | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 										if (err) return this._done(err); | 
					
						
							| 
									
										
										
										
											2019-01-05 20:49:42 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 										this.compiler.hooks.additionalPass.callAsync(err => { | 
					
						
							|  |  |  | 											if (err) return this._done(err); | 
					
						
							|  |  |  | 											this.compiler.compile(onCompiled); | 
					
						
							|  |  |  | 										}); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 									}); | 
					
						
							| 
									
										
										
										
											2019-01-05 20:49:42 +08:00
										 |  |  | 									return; | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 								return this._done(null, compilation); | 
					
						
							|  |  |  | 							}); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 						}); | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | 					}); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 				}; | 
					
						
							|  |  |  | 				this.compiler.compile(onCompiled); | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-10 18:34:59 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {Compilation} compilation the compilation | 
					
						
							|  |  |  | 	 * @returns {Stats} the compilation stats | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | 	_getStats(compilation) { | 
					
						
							|  |  |  | 		const stats = new Stats(compilation); | 
					
						
							|  |  |  | 		stats.startTime = this.startTime; | 
					
						
							|  |  |  | 		stats.endTime = Date.now(); | 
					
						
							|  |  |  | 		return stats; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-10 18:34:59 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {Error=} err an optional error | 
					
						
							|  |  |  | 	 * @param {Compilation=} compilation the compilation | 
					
						
							|  |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | 	_done(err, compilation) { | 
					
						
							|  |  |  | 		this.running = false; | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		if (this.invalid) return this._go(); | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		const stats = compilation ? this._getStats(compilation) : null; | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		if (err) { | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | 			this.compiler.hooks.failed.call(err); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 			this.compiler.cache.beginIdle(); | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | 			this.handler(err, stats); | 
					
						
							|  |  |  | 			return; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-25 20:56:50 +08:00
										 |  |  | 		this.compiler.hooks.done.callAsync(stats, () => { | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 			this.compiler.cache.beginIdle(); | 
					
						
							| 
									
										
										
										
											2018-01-25 20:56:50 +08:00
										 |  |  | 			this.handler(null, stats); | 
					
						
							| 
									
										
										
										
											2019-01-05 20:49:42 +08:00
										 |  |  | 			process.nextTick(() => { | 
					
						
							|  |  |  | 				if (!this.closed) { | 
					
						
							|  |  |  | 					this.watch( | 
					
						
							|  |  |  | 						Array.from(compilation.fileDependencies), | 
					
						
							|  |  |  | 						Array.from(compilation.contextDependencies), | 
					
						
							|  |  |  | 						Array.from(compilation.missingDependencies) | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 			for (const cb of this.callbacks) cb(); | 
					
						
							| 
									
										
										
										
											2018-01-25 20:56:50 +08:00
										 |  |  | 			this.callbacks.length = 0; | 
					
						
							| 
									
										
										
										
											2018-12-26 21:56:19 +08:00
										 |  |  | 			this.compiler.hooks.afterDone.call(stats); | 
					
						
							| 
									
										
										
										
											2018-01-25 20:56:50 +08:00
										 |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	watch(files, dirs, missing) { | 
					
						
							|  |  |  | 		this.pausedWatcher = null; | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		this.watcher = this.compiler.watchFileSystem.watch( | 
					
						
							|  |  |  | 			files, | 
					
						
							|  |  |  | 			dirs, | 
					
						
							|  |  |  | 			missing, | 
					
						
							|  |  |  | 			this.startTime, | 
					
						
							|  |  |  | 			this.watchOptions, | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			(err, fileTimeInfoEntries, contextTimeInfoEntries, removedFiles) => { | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 				this.pausedWatcher = this.watcher; | 
					
						
							|  |  |  | 				this.watcher = null; | 
					
						
							| 
									
										
										
										
											2018-05-29 20:50:40 +08:00
										 |  |  | 				if (err) { | 
					
						
							|  |  |  | 					return this.handler(err); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 				this.compiler.fileTimestamps = fileTimeInfoEntries; | 
					
						
							|  |  |  | 				this.compiler.contextTimestamps = contextTimeInfoEntries; | 
					
						
							| 
									
										
										
										
											2018-10-19 06:58:58 +08:00
										 |  |  | 				this.compiler.removedFiles = removedFiles; | 
					
						
							| 
									
										
										
										
											2019-06-01 00:53:33 +08:00
										 |  |  | 				if (!this.suspended) { | 
					
						
							|  |  |  | 					this._invalidate(); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			(fileName, changeTime) => { | 
					
						
							|  |  |  | 				this.compiler.hooks.invalid.call(fileName, changeTime); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		); | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-10 18:34:59 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {Callback<void>=} callback signals when the build is invalidated | 
					
						
							|  |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | 	invalidate(callback) { | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		if (callback) { | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | 			this.callbacks.push(callback); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		if (this.watcher) { | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			this.compiler.fileTimestamps = this.watcher.getFileTimeInfoEntries(); | 
					
						
							|  |  |  | 			this.compiler.contextTimestamps = this.watcher.getContextTimeInfoEntries(); | 
					
						
							| 
									
										
										
										
											2018-01-03 19:48:46 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-12-10 18:34:59 +08:00
										 |  |  | 		this._invalidate(); | 
					
						
							| 
									
										
										
										
											2018-01-03 19:48:46 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	_invalidate() { | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		if (this.watcher) { | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | 			this.pausedWatcher = this.watcher; | 
					
						
							|  |  |  | 			this.watcher.pause(); | 
					
						
							|  |  |  | 			this.watcher = null; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-06-03 23:49:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		if (this.running) { | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | 			this.invalid = true; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			this._go(); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-01 00:53:33 +08:00
										 |  |  | 	suspend() { | 
					
						
							|  |  |  | 		this.suspended = true; | 
					
						
							| 
									
										
										
										
											2019-06-03 23:49:22 +08:00
										 |  |  | 		this.invalid = false; | 
					
						
							| 
									
										
										
										
											2019-06-01 00:53:33 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	resume() { | 
					
						
							|  |  |  | 		if (this.suspended) { | 
					
						
							|  |  |  | 			this.suspended = false; | 
					
						
							|  |  |  | 			this._invalidate(); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-10 18:34:59 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {Callback<void>} callback signals when the watcher is closed | 
					
						
							|  |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | 	close(callback) { | 
					
						
							| 
									
										
										
										
											2018-03-07 17:15:46 +08:00
										 |  |  | 		const finalCallback = () => { | 
					
						
							|  |  |  | 			this.compiler.running = false; | 
					
						
							| 
									
										
										
										
											2018-10-24 01:52:32 +08:00
										 |  |  | 			this.compiler.watchMode = false; | 
					
						
							| 
									
										
										
										
											2018-09-28 03:28:07 +08:00
										 |  |  | 			this.compiler.fileTimestamps = undefined; | 
					
						
							|  |  |  | 			this.compiler.contextTimestamps = undefined; | 
					
						
							| 
									
										
										
										
											2018-10-25 02:10:30 +08:00
										 |  |  | 			this.compiler.removedFiles = undefined; | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 			this.compiler.cache.shutdown(err => { | 
					
						
							|  |  |  | 				this.compiler.hooks.watchClose.call(); | 
					
						
							|  |  |  | 				if (callback !== undefined) callback(err); | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2018-03-07 17:15:46 +08:00
										 |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		this.closed = true; | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		if (this.watcher) { | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | 			this.watcher.close(); | 
					
						
							|  |  |  | 			this.watcher = null; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		if (this.pausedWatcher) { | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | 			this.pausedWatcher.close(); | 
					
						
							|  |  |  | 			this.pausedWatcher = null; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		if (this.running) { | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | 			this.invalid = true; | 
					
						
							| 
									
										
										
										
											2018-03-07 17:15:46 +08:00
										 |  |  | 			this._done = finalCallback; | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2018-03-07 17:15:46 +08:00
										 |  |  | 			finalCallback(); | 
					
						
							| 
									
										
										
										
											2017-12-31 23:19:18 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports = Watching; |