| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | /* | 
					
						
							|  |  |  | 	MIT License http://www.opensource.org/licenses/mit-license.php
 | 
					
						
							|  |  |  | 	Author Tobias Koppers @sokra | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | "use strict"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-11 22:25:03 +08:00
										 |  |  | const { create: createResolver } = require("enhanced-resolve"); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | const asyncLib = require("neo-async"); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | const AsyncQueue = require("./util/AsyncQueue"); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | const createHash = require("./util/createHash"); | 
					
						
							| 
									
										
										
										
											2019-08-20 19:50:14 +08:00
										 |  |  | const { join, dirname, relative } = require("./util/fs"); | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | const makeSerializable = require("./util/makeSerializable"); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | /** @typedef {import("./WebpackError")} WebpackError */ | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | /** @typedef {import("./logging/Logger").Logger} Logger */ | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | /** @typedef {import("./util/fs").InputFileSystem} InputFileSystem */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-11 22:25:03 +08:00
										 |  |  | const resolveContext = createResolver({ | 
					
						
							| 
									
										
										
										
											2020-08-11 01:24:11 +08:00
										 |  |  | 	resolveToContext: true, | 
					
						
							|  |  |  | 	exportsFields: [] | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | }); | 
					
						
							| 
									
										
										
										
											2019-11-11 22:25:03 +08:00
										 |  |  | const resolve = createResolver({ | 
					
						
							| 
									
										
										
										
											2020-08-11 01:24:11 +08:00
										 |  |  | 	extensions: [".js", ".json", ".node"], | 
					
						
							|  |  |  | 	conditionNames: ["require"] | 
					
						
							| 
									
										
										
										
											2019-11-11 22:25:03 +08:00
										 |  |  | }); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | let FS_ACCURACY = 2000; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | const EMPTY_SET = new Set(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-13 23:21:19 +08:00
										 |  |  | const RBDT_RESOLVE = 0; | 
					
						
							|  |  |  | const RBDT_RESOLVE_DIRECTORY = 1; | 
					
						
							| 
									
										
										
										
											2019-08-20 19:50:14 +08:00
										 |  |  | const RBDT_RESOLVE_FILE = 2; | 
					
						
							|  |  |  | const RBDT_DIRECTORY = 3; | 
					
						
							|  |  |  | const RBDT_FILE = 4; | 
					
						
							|  |  |  | const RBDT_DIRECTORY_DEPENDENCIES = 5; | 
					
						
							|  |  |  | const RBDT_FILE_DEPENDENCIES = 6; | 
					
						
							| 
									
										
										
										
											2019-08-13 23:21:19 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | const INVALID = Symbol("invalid"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @typedef {Object} FileSystemInfoEntry | 
					
						
							|  |  |  |  * @property {number} safeTime | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  |  * @property {number=} timestamp | 
					
						
							|  |  |  |  * @property {string=} timestampHash | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @typedef {Object} TimestampAndHash | 
					
						
							|  |  |  |  * @property {number} safeTime | 
					
						
							|  |  |  |  * @property {number=} timestamp | 
					
						
							|  |  |  |  * @property {string=} timestampHash | 
					
						
							|  |  |  |  * @property {string} hash | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-31 17:59:28 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @typedef {Object} SnapshotOptimizationEntry | 
					
						
							|  |  |  |  * @property {Snapshot} snapshot | 
					
						
							|  |  |  |  * @property {number} shared | 
					
						
							|  |  |  |  * @property {Set<string>} snapshotContent | 
					
						
							|  |  |  |  * @property {Set<SnapshotOptimizationEntry>} children | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @typedef {Object} ResolveBuildDependenciesResult | 
					
						
							|  |  |  |  * @property {Set<string>} files list of files | 
					
						
							|  |  |  |  * @property {Set<string>} directories list of directories | 
					
						
							|  |  |  |  * @property {Set<string>} missing list of missing entries | 
					
						
							|  |  |  |  * @property {Map<string, string>} resolveResults stored resolve results | 
					
						
							|  |  |  |  * @property {Object} resolveDependencies dependencies of the resolving | 
					
						
							|  |  |  |  * @property {Set<string>} resolveDependencies.files list of files | 
					
						
							|  |  |  |  * @property {Set<string>} resolveDependencies.directories list of directories | 
					
						
							|  |  |  |  * @property {Set<string>} resolveDependencies.missing list of missing entries | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | const DONE_ITERATOR_RESULT = new Set().keys().next(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | // cspell:word tshs
 | 
					
						
							|  |  |  | // Tsh = Timestamp + Hash
 | 
					
						
							|  |  |  | // Tshs = Timestamp + Hash combinations
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | class Snapshot { | 
					
						
							|  |  |  | 	constructor() { | 
					
						
							|  |  |  | 		this._flags = 0; | 
					
						
							|  |  |  | 		/** @type {number | undefined} */ | 
					
						
							|  |  |  | 		this.startTime = undefined; | 
					
						
							|  |  |  | 		/** @type {Map<string, FileSystemInfoEntry> | undefined} */ | 
					
						
							|  |  |  | 		this.fileTimestamps = undefined; | 
					
						
							|  |  |  | 		/** @type {Map<string, string> | undefined} */ | 
					
						
							|  |  |  | 		this.fileHashes = undefined; | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		/** @type {Map<string, TimestampAndHash | string> | undefined} */ | 
					
						
							|  |  |  | 		this.fileTshs = undefined; | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 		/** @type {Map<string, FileSystemInfoEntry> | undefined} */ | 
					
						
							|  |  |  | 		this.contextTimestamps = undefined; | 
					
						
							|  |  |  | 		/** @type {Map<string, string> | undefined} */ | 
					
						
							|  |  |  | 		this.contextHashes = undefined; | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		/** @type {Map<string, TimestampAndHash | string> | undefined} */ | 
					
						
							|  |  |  | 		this.contextTshs = undefined; | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 		/** @type {Map<string, boolean> | undefined} */ | 
					
						
							|  |  |  | 		this.missingExistence = undefined; | 
					
						
							|  |  |  | 		/** @type {Map<string, string> | undefined} */ | 
					
						
							|  |  |  | 		this.managedItemInfo = undefined; | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 		/** @type {Set<string> | undefined} */ | 
					
						
							|  |  |  | 		this.managedFiles = undefined; | 
					
						
							|  |  |  | 		/** @type {Set<string> | undefined} */ | 
					
						
							|  |  |  | 		this.managedContexts = undefined; | 
					
						
							|  |  |  | 		/** @type {Set<string> | undefined} */ | 
					
						
							|  |  |  | 		this.managedMissing = undefined; | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 		/** @type {Set<Snapshot> | undefined} */ | 
					
						
							|  |  |  | 		this.children = undefined; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hasStartTime() { | 
					
						
							|  |  |  | 		return (this._flags & 1) !== 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	setStartTime(value) { | 
					
						
							|  |  |  | 		this._flags = this._flags | 1; | 
					
						
							|  |  |  | 		this.startTime = value; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	setMergedStartTime(value, snapshot) { | 
					
						
							|  |  |  | 		if (value) { | 
					
						
							|  |  |  | 			if (snapshot.hasStartTime()) { | 
					
						
							|  |  |  | 				this.setStartTime(Math.min(value, snapshot.startTime)); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				this.setStartTime(value); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			if (snapshot.hasStartTime()) this.setStartTime(snapshot.startTime); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hasFileTimestamps() { | 
					
						
							|  |  |  | 		return (this._flags & 2) !== 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	setFileTimestamps(value) { | 
					
						
							|  |  |  | 		this._flags = this._flags | 2; | 
					
						
							|  |  |  | 		this.fileTimestamps = value; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hasFileHashes() { | 
					
						
							|  |  |  | 		return (this._flags & 4) !== 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	setFileHashes(value) { | 
					
						
							|  |  |  | 		this._flags = this._flags | 4; | 
					
						
							|  |  |  | 		this.fileHashes = value; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 	hasFileTshs() { | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 		return (this._flags & 8) !== 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 	setFileTshs(value) { | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 		this._flags = this._flags | 8; | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		this.fileTshs = value; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hasContextTimestamps() { | 
					
						
							|  |  |  | 		return (this._flags & 0x10) !== 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	setContextTimestamps(value) { | 
					
						
							|  |  |  | 		this._flags = this._flags | 0x10; | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 		this.contextTimestamps = value; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hasContextHashes() { | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		return (this._flags & 0x20) !== 0; | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	setContextHashes(value) { | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		this._flags = this._flags | 0x20; | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 		this.contextHashes = value; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 	hasContextTshs() { | 
					
						
							|  |  |  | 		return (this._flags & 0x40) !== 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	setContextTshs(value) { | 
					
						
							|  |  |  | 		this._flags = this._flags | 0x40; | 
					
						
							|  |  |  | 		this.contextTshs = value; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 	hasMissingExistence() { | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		return (this._flags & 0x80) !== 0; | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	setMissingExistence(value) { | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		this._flags = this._flags | 0x80; | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 		this.missingExistence = value; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hasManagedItemInfo() { | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		return (this._flags & 0x100) !== 0; | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	setManagedItemInfo(value) { | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		this._flags = this._flags | 0x100; | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 		this.managedItemInfo = value; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 	hasManagedFiles() { | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		return (this._flags & 0x200) !== 0; | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 	setManagedFiles(value) { | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		this._flags = this._flags | 0x200; | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 		this.managedFiles = value; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hasManagedContexts() { | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		return (this._flags & 0x400) !== 0; | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	setManagedContexts(value) { | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		this._flags = this._flags | 0x400; | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 		this.managedContexts = value; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hasManagedMissing() { | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		return (this._flags & 0x800) !== 0; | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	setManagedMissing(value) { | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		this._flags = this._flags | 0x800; | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 		this.managedMissing = value; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hasChildren() { | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		return (this._flags & 0x1000) !== 0; | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	setChildren(value) { | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		this._flags = this._flags | 0x1000; | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 		this.children = value; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	addChild(child) { | 
					
						
							|  |  |  | 		if (!this.hasChildren()) { | 
					
						
							|  |  |  | 			this.setChildren(new Set()); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		this.children.add(child); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	serialize({ write }) { | 
					
						
							|  |  |  | 		write(this._flags); | 
					
						
							|  |  |  | 		if (this.hasStartTime()) write(this.startTime); | 
					
						
							|  |  |  | 		if (this.hasFileTimestamps()) write(this.fileTimestamps); | 
					
						
							|  |  |  | 		if (this.hasFileHashes()) write(this.fileHashes); | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		if (this.hasFileTshs()) write(this.fileTshs); | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 		if (this.hasContextTimestamps()) write(this.contextTimestamps); | 
					
						
							|  |  |  | 		if (this.hasContextHashes()) write(this.contextHashes); | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		if (this.hasContextTshs()) write(this.contextTshs); | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 		if (this.hasMissingExistence()) write(this.missingExistence); | 
					
						
							|  |  |  | 		if (this.hasManagedItemInfo()) write(this.managedItemInfo); | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 		if (this.hasManagedFiles()) write(this.managedFiles); | 
					
						
							|  |  |  | 		if (this.hasManagedContexts()) write(this.managedContexts); | 
					
						
							|  |  |  | 		if (this.hasManagedMissing()) write(this.managedMissing); | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 		if (this.hasChildren()) write(this.children); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	deserialize({ read }) { | 
					
						
							|  |  |  | 		this._flags = read(); | 
					
						
							|  |  |  | 		if (this.hasStartTime()) this.startTime = read(); | 
					
						
							|  |  |  | 		if (this.hasFileTimestamps()) this.fileTimestamps = read(); | 
					
						
							|  |  |  | 		if (this.hasFileHashes()) this.fileHashes = read(); | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		if (this.hasFileTshs()) this.fileTshs = read(); | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 		if (this.hasContextTimestamps()) this.contextTimestamps = read(); | 
					
						
							|  |  |  | 		if (this.hasContextHashes()) this.contextHashes = read(); | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		if (this.hasContextTshs()) this.contextTshs = read(); | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 		if (this.hasMissingExistence()) this.missingExistence = read(); | 
					
						
							|  |  |  | 		if (this.hasManagedItemInfo()) this.managedItemInfo = read(); | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 		if (this.hasManagedFiles()) this.managedFiles = read(); | 
					
						
							|  |  |  | 		if (this.hasManagedContexts()) this.managedContexts = read(); | 
					
						
							|  |  |  | 		if (this.hasManagedMissing()) this.managedMissing = read(); | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 		if (this.hasChildren()) this.children = read(); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 	 * @param {function(Snapshot): (Map<string, any> | Set<string>)[]} getMaps first | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 	 * @returns {Iterable<string>} iterable | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 	_createIterable(getMaps) { | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 		let snapshot = this; | 
					
						
							|  |  |  | 		return { | 
					
						
							|  |  |  | 			[Symbol.iterator]() { | 
					
						
							|  |  |  | 				let state = 0; | 
					
						
							|  |  |  | 				/** @type {IterableIterator<string>} */ | 
					
						
							|  |  |  | 				let it; | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 				let maps = getMaps(snapshot); | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 				const queue = []; | 
					
						
							|  |  |  | 				return { | 
					
						
							|  |  |  | 					next() { | 
					
						
							|  |  |  | 						for (;;) { | 
					
						
							|  |  |  | 							switch (state) { | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 								case 0: | 
					
						
							|  |  |  | 									if (maps.length > 0) { | 
					
						
							|  |  |  | 										const map = maps.pop(); | 
					
						
							|  |  |  | 										if (map !== undefined) { | 
					
						
							|  |  |  | 											it = map.keys(); | 
					
						
							|  |  |  | 											state = 1; | 
					
						
							|  |  |  | 										} else { | 
					
						
							|  |  |  | 											break; | 
					
						
							|  |  |  | 										} | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 									} else { | 
					
						
							|  |  |  | 										state = 2; | 
					
						
							|  |  |  | 										break; | 
					
						
							|  |  |  | 									} | 
					
						
							|  |  |  | 								/* falls through */ | 
					
						
							|  |  |  | 								case 1: { | 
					
						
							|  |  |  | 									const result = it.next(); | 
					
						
							|  |  |  | 									if (!result.done) return result; | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 									state = 0; | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 								} | 
					
						
							|  |  |  | 								/* falls through */ | 
					
						
							|  |  |  | 								case 2: { | 
					
						
							|  |  |  | 									const children = snapshot.children; | 
					
						
							|  |  |  | 									if (children !== undefined) { | 
					
						
							|  |  |  | 										for (const child of children) { | 
					
						
							|  |  |  | 											queue.push(child); | 
					
						
							|  |  |  | 										} | 
					
						
							|  |  |  | 									} | 
					
						
							|  |  |  | 									if (queue.length > 0) { | 
					
						
							|  |  |  | 										snapshot = queue.pop(); | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 										maps = getMaps(snapshot); | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 										state = 0; | 
					
						
							|  |  |  | 										break; | 
					
						
							|  |  |  | 									} else { | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 										state = 3; | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 									} | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 								/* falls through */ | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 								case 3: | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 									return DONE_ITERATOR_RESULT; | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				}; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							|  |  |  | 	 * @returns {Iterable<string>} iterable | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	getFileIterable() { | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		return this._createIterable(s => [ | 
					
						
							|  |  |  | 			s.fileTimestamps, | 
					
						
							|  |  |  | 			s.fileHashes, | 
					
						
							|  |  |  | 			s.fileTshs, | 
					
						
							|  |  |  | 			s.managedFiles | 
					
						
							|  |  |  | 		]); | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							|  |  |  | 	 * @returns {Iterable<string>} iterable | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	getContextIterable() { | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		return this._createIterable(s => [ | 
					
						
							|  |  |  | 			s.contextTimestamps, | 
					
						
							|  |  |  | 			s.contextHashes, | 
					
						
							|  |  |  | 			s.contextTshs, | 
					
						
							|  |  |  | 			s.managedContexts | 
					
						
							|  |  |  | 		]); | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							|  |  |  | 	 * @returns {Iterable<string>} iterable | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	getMissingIterable() { | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		return this._createIterable(s => [s.missingExistence, s.managedMissing]); | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | makeSerializable(Snapshot, "webpack/lib/FileSystemInfo", "Snapshot"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | const MIN_COMMON_SNAPSHOT_SIZE = 3; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @template T | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | class SnapshotOptimization { | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {function(Snapshot): boolean} has has value | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 	 * @param {function(Snapshot): Map<string, T> | Set<string>} get get value | 
					
						
							|  |  |  | 	 * @param {function(Snapshot, Map<string, T> | Set<string>): void} set set value | 
					
						
							|  |  |  | 	 * @param {boolean=} isSet value is an Set instead of a Map | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 	constructor(has, get, set, isSet = false) { | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 		this._has = has; | 
					
						
							|  |  |  | 		this._get = get; | 
					
						
							|  |  |  | 		this._set = set; | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 		this._isSet = isSet; | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | 		/** @type {Map<string, SnapshotOptimizationEntry>} */ | 
					
						
							|  |  |  | 		this._map = new Map(); | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 		this._statItemsShared = 0; | 
					
						
							|  |  |  | 		this._statItemsUnshared = 0; | 
					
						
							|  |  |  | 		this._statSharedSnapshots = 0; | 
					
						
							| 
									
										
										
										
											2020-08-22 17:15:07 +08:00
										 |  |  | 		this._statReusedSharedSnapshots = 0; | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	getStatisticMessage() { | 
					
						
							|  |  |  | 		const total = this._statItemsShared + this._statItemsUnshared; | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 		if (total === 0) return undefined; | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 		return `${ | 
					
						
							|  |  |  | 			this._statItemsShared && Math.round((this._statItemsShared * 100) / total) | 
					
						
							|  |  |  | 		}% (${this._statItemsShared}/${total}) entries shared via ${ | 
					
						
							|  |  |  | 			this._statSharedSnapshots | 
					
						
							| 
									
										
										
										
											2020-08-22 17:15:07 +08:00
										 |  |  | 		} shared snapshots (${ | 
					
						
							|  |  |  | 			this._statReusedSharedSnapshots + this._statSharedSnapshots | 
					
						
							|  |  |  | 		} times referenced)`;
 | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	storeUnsharedSnapshot(snapshot, locations) { | 
					
						
							|  |  |  | 		if (locations === undefined) return; | 
					
						
							|  |  |  | 		const optimizationEntry = { | 
					
						
							|  |  |  | 			snapshot, | 
					
						
							|  |  |  | 			shared: 0, | 
					
						
							|  |  |  | 			snapshotContent: undefined, | 
					
						
							|  |  |  | 			children: undefined | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 		for (const path of locations) { | 
					
						
							|  |  |  | 			this._map.set(path, optimizationEntry); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	optimize(capturedFiles, startTime, children) { | 
					
						
							|  |  |  | 		/** @type {Set<string>} */ | 
					
						
							|  |  |  | 		const unsetOptimizationEntries = new Set(); | 
					
						
							|  |  |  | 		/** @type {Set<SnapshotOptimizationEntry>} */ | 
					
						
							|  |  |  | 		const checkedOptimizationEntries = new Set(); | 
					
						
							|  |  |  | 		/** | 
					
						
							|  |  |  | 		 * @param {SnapshotOptimizationEntry} entry optimization entry | 
					
						
							|  |  |  | 		 * @returns {void} | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		const increaseSharedAndStoreOptimizationEntry = entry => { | 
					
						
							|  |  |  | 			if (entry.children !== undefined) { | 
					
						
							|  |  |  | 				entry.children.forEach(increaseSharedAndStoreOptimizationEntry); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			entry.shared++; | 
					
						
							|  |  |  | 			storeOptimizationEntry(entry); | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 		/** | 
					
						
							|  |  |  | 		 * @param {SnapshotOptimizationEntry} entry optimization entry | 
					
						
							|  |  |  | 		 * @returns {void} | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		const storeOptimizationEntry = entry => { | 
					
						
							|  |  |  | 			for (const path of entry.snapshotContent) { | 
					
						
							|  |  |  | 				const old = this._map.get(path); | 
					
						
							|  |  |  | 				if (old.shared < entry.shared) { | 
					
						
							|  |  |  | 					this._map.set(path, entry); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				capturedFiles.delete(path); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 		const capturedFilesSize = capturedFiles.size; | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | 		capturedFiles: for (const path of capturedFiles) { | 
					
						
							|  |  |  | 			const optimizationEntry = this._map.get(path); | 
					
						
							|  |  |  | 			if (optimizationEntry === undefined) { | 
					
						
							|  |  |  | 				unsetOptimizationEntries.add(path); | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if (checkedOptimizationEntries.has(optimizationEntry)) continue; | 
					
						
							|  |  |  | 			const snapshot = optimizationEntry.snapshot; | 
					
						
							|  |  |  | 			if (optimizationEntry.shared > 0) { | 
					
						
							|  |  |  | 				// It's a shared snapshot
 | 
					
						
							|  |  |  | 				// We can't change it, so we can only use it when all files match
 | 
					
						
							|  |  |  | 				// and startTime is compatible
 | 
					
						
							|  |  |  | 				if ( | 
					
						
							|  |  |  | 					startTime && | 
					
						
							|  |  |  | 					(!snapshot.startTime || snapshot.startTime > startTime) | 
					
						
							|  |  |  | 				) { | 
					
						
							|  |  |  | 					continue; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				const nonSharedFiles = new Set(); | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 				const snapshotContent = optimizationEntry.snapshotContent; | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 				const snapshotEntries = this._get(snapshot); | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 				for (const path of snapshotContent) { | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | 					if (!capturedFiles.has(path)) { | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 						if (!snapshotEntries.has(path)) { | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | 							// File is not shared and can't be removed from the snapshot
 | 
					
						
							|  |  |  | 							// because it's in a child of the snapshot
 | 
					
						
							|  |  |  | 							checkedOptimizationEntries.add(optimizationEntry); | 
					
						
							|  |  |  | 							continue capturedFiles; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						nonSharedFiles.add(path); | 
					
						
							|  |  |  | 						continue; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if (nonSharedFiles.size === 0) { | 
					
						
							|  |  |  | 					// The complete snapshot is shared
 | 
					
						
							|  |  |  | 					// add it as child
 | 
					
						
							|  |  |  | 					children.add(snapshot); | 
					
						
							|  |  |  | 					increaseSharedAndStoreOptimizationEntry(optimizationEntry); | 
					
						
							| 
									
										
										
										
											2020-08-22 17:15:07 +08:00
										 |  |  | 					this._statReusedSharedSnapshots++; | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | 				} else { | 
					
						
							|  |  |  | 					// Only a part of the snapshot is shared
 | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 					const sharedCount = snapshotContent.size - nonSharedFiles.size; | 
					
						
							|  |  |  | 					if (sharedCount < MIN_COMMON_SNAPSHOT_SIZE) { | 
					
						
							|  |  |  | 						// Common part it too small
 | 
					
						
							|  |  |  | 						checkedOptimizationEntries.add(optimizationEntry); | 
					
						
							|  |  |  | 						continue capturedFiles; | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | 					// Extract common timestamps from both snapshots
 | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 					let commonMap; | 
					
						
							|  |  |  | 					if (this._isSet) { | 
					
						
							|  |  |  | 						commonMap = new Set(); | 
					
						
							|  |  |  | 						for (const path of /** @type {Set<string>} */ (snapshotEntries)) { | 
					
						
							|  |  |  | 							if (nonSharedFiles.has(path)) continue; | 
					
						
							|  |  |  | 							commonMap.add(path); | 
					
						
							|  |  |  | 							snapshotEntries.delete(path); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						commonMap = new Map(); | 
					
						
							|  |  |  | 						const map = /** @type {Map<string, T>} */ (snapshotEntries); | 
					
						
							|  |  |  | 						for (const [path, value] of map) { | 
					
						
							|  |  |  | 							if (nonSharedFiles.has(path)) continue; | 
					
						
							|  |  |  | 							commonMap.set(path, value); | 
					
						
							|  |  |  | 							snapshotEntries.delete(path); | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 					// Create and attach snapshot
 | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 					const commonSnapshot = new Snapshot(); | 
					
						
							|  |  |  | 					commonSnapshot.setMergedStartTime(startTime, snapshot); | 
					
						
							|  |  |  | 					this._set(commonSnapshot, commonMap); | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | 					children.add(commonSnapshot); | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 					snapshot.addChild(commonSnapshot); | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | 					// Create optimization entry
 | 
					
						
							|  |  |  | 					const newEntry = { | 
					
						
							|  |  |  | 						snapshot: commonSnapshot, | 
					
						
							|  |  |  | 						shared: optimizationEntry.shared + 1, | 
					
						
							|  |  |  | 						snapshotContent: new Set(commonMap.keys()), | 
					
						
							|  |  |  | 						children: undefined | 
					
						
							|  |  |  | 					}; | 
					
						
							|  |  |  | 					if (optimizationEntry.children === undefined) | 
					
						
							|  |  |  | 						optimizationEntry.children = new Set(); | 
					
						
							|  |  |  | 					optimizationEntry.children.add(newEntry); | 
					
						
							|  |  |  | 					storeOptimizationEntry(newEntry); | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 					this._statSharedSnapshots++; | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				// It's a unshared snapshot
 | 
					
						
							|  |  |  | 				// We can extract a common shared snapshot
 | 
					
						
							|  |  |  | 				// with all common files
 | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 				const snapshotEntries = this._get(snapshot); | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 				let commonMap; | 
					
						
							|  |  |  | 				if (this._isSet) { | 
					
						
							|  |  |  | 					commonMap = new Set(); | 
					
						
							|  |  |  | 					const set = /** @type {Set<string>} */ (snapshotEntries); | 
					
						
							|  |  |  | 					if (capturedFiles.size < set.size) { | 
					
						
							|  |  |  | 						for (const path of capturedFiles) { | 
					
						
							|  |  |  | 							if (set.has(path)) commonMap.add(path); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						for (const path of set) { | 
					
						
							|  |  |  | 							if (capturedFiles.has(path)) commonMap.add(path); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					commonMap = new Map(); | 
					
						
							|  |  |  | 					const map = /** @type {Map<string, T>} */ (snapshotEntries); | 
					
						
							|  |  |  | 					for (const path of capturedFiles) { | 
					
						
							|  |  |  | 						const ts = map.get(path); | 
					
						
							|  |  |  | 						if (ts === undefined) continue; | 
					
						
							|  |  |  | 						commonMap.set(path, ts); | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 				if (commonMap.size < MIN_COMMON_SNAPSHOT_SIZE) { | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | 					// Common part it too small
 | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 					checkedOptimizationEntries.add(optimizationEntry); | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | 					continue capturedFiles; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				// Create and attach snapshot
 | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 				const commonSnapshot = new Snapshot(); | 
					
						
							|  |  |  | 				commonSnapshot.setMergedStartTime(startTime, snapshot); | 
					
						
							|  |  |  | 				this._set(commonSnapshot, commonMap); | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | 				children.add(commonSnapshot); | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 				snapshot.addChild(commonSnapshot); | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | 				// Remove files from snapshot
 | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 				for (const path of commonMap.keys()) snapshotEntries.delete(path); | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 				const sharedCount = commonMap.size; | 
					
						
							|  |  |  | 				this._statItemsUnshared -= sharedCount; | 
					
						
							|  |  |  | 				this._statItemsShared += sharedCount; | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | 				// Create optimization entry
 | 
					
						
							|  |  |  | 				storeOptimizationEntry({ | 
					
						
							|  |  |  | 					snapshot: commonSnapshot, | 
					
						
							|  |  |  | 					shared: 2, | 
					
						
							|  |  |  | 					snapshotContent: new Set(commonMap.keys()), | 
					
						
							|  |  |  | 					children: undefined | 
					
						
							|  |  |  | 				}); | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 				this._statSharedSnapshots++; | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 			checkedOptimizationEntries.add(optimizationEntry); | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 		const unshared = capturedFiles.size; | 
					
						
							|  |  |  | 		this._statItemsUnshared += unshared; | 
					
						
							|  |  |  | 		this._statItemsShared += capturedFilesSize - unshared; | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | 		return unsetOptimizationEntries; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-23 16:48:36 +08:00
										 |  |  | /* istanbul ignore next */ | 
					
						
							| 
									
										
										
										
											2019-10-20 17:35:36 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @param {number} mtime mtime | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | const applyMtime = mtime => { | 
					
						
							|  |  |  | 	if (FS_ACCURACY > 1 && mtime % 2 !== 0) FS_ACCURACY = 1; | 
					
						
							|  |  |  | 	else if (FS_ACCURACY > 10 && mtime % 20 !== 0) FS_ACCURACY = 10; | 
					
						
							|  |  |  | 	else if (FS_ACCURACY > 100 && mtime % 200 !== 0) FS_ACCURACY = 100; | 
					
						
							|  |  |  | 	else if (FS_ACCURACY > 1000 && mtime % 2000 !== 0) FS_ACCURACY = 1000; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-20 17:35:36 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @template T | 
					
						
							|  |  |  |  * @template K | 
					
						
							|  |  |  |  * @param {Map<T, K>} a source map | 
					
						
							|  |  |  |  * @param {Map<T, K>} b joining map | 
					
						
							|  |  |  |  * @returns {Map<T, K>} joined map | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | const mergeMaps = (a, b) => { | 
					
						
							| 
									
										
										
										
											2019-08-14 00:48:15 +08:00
										 |  |  | 	if (!b || b.size === 0) return a; | 
					
						
							|  |  |  | 	if (!a || a.size === 0) return b; | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 	const map = new Map(a); | 
					
						
							|  |  |  | 	for (const [key, value] of b) { | 
					
						
							|  |  |  | 		map.set(key, value); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return map; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @template T | 
					
						
							|  |  |  |  * @template K | 
					
						
							|  |  |  |  * @param {Set<T, K>} a source map | 
					
						
							|  |  |  |  * @param {Set<T, K>} b joining map | 
					
						
							|  |  |  |  * @returns {Set<T, K>} joined map | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | const mergeSets = (a, b) => { | 
					
						
							|  |  |  | 	if (!b || b.size === 0) return a; | 
					
						
							|  |  |  | 	if (!a || a.size === 0) return b; | 
					
						
							|  |  |  | 	const map = new Set(a); | 
					
						
							|  |  |  | 	for (const item of b) { | 
					
						
							|  |  |  | 		map.add(item); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return map; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-20 17:35:36 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Finding file or directory to manage | 
					
						
							|  |  |  |  * @param {string} managedPath path that is managing by {@link FileSystemInfo} | 
					
						
							|  |  |  |  * @param {string} path path to file or directory | 
					
						
							|  |  |  |  * @returns {string|null} managed item | 
					
						
							|  |  |  |  * @example | 
					
						
							|  |  |  |  * getManagedItem( | 
					
						
							| 
									
										
										
										
											2019-10-20 20:53:17 +08:00
										 |  |  |  *   '/Users/user/my-project/node_modules/', | 
					
						
							| 
									
										
										
										
											2019-10-20 17:35:36 +08:00
										 |  |  |  *   '/Users/user/my-project/node_modules/package/index.js' | 
					
						
							|  |  |  |  * ) === '/Users/user/my-project/node_modules/package' | 
					
						
							|  |  |  |  * getManagedItem( | 
					
						
							| 
									
										
										
										
											2019-10-20 20:53:17 +08:00
										 |  |  |  *   '/Users/user/my-project/node_modules/', | 
					
						
							|  |  |  |  *   '/Users/user/my-project/node_modules/package1/node_modules/package2' | 
					
						
							|  |  |  |  * ) === '/Users/user/my-project/node_modules/package1/node_modules/package2' | 
					
						
							|  |  |  |  * getManagedItem( | 
					
						
							|  |  |  |  *   '/Users/user/my-project/node_modules/', | 
					
						
							|  |  |  |  *   '/Users/user/my-project/node_modules/.bin/script.js' | 
					
						
							| 
									
										
										
										
											2019-10-20 17:35:36 +08:00
										 |  |  |  * ) === null // hidden files are disallowed as managed items
 | 
					
						
							|  |  |  |  * getManagedItem( | 
					
						
							| 
									
										
										
										
											2019-10-20 20:53:17 +08:00
										 |  |  |  *   '/Users/user/my-project/node_modules/', | 
					
						
							|  |  |  |  *   '/Users/user/my-project/node_modules/package' | 
					
						
							|  |  |  |  * ) === '/Users/user/my-project/node_modules/package' | 
					
						
							| 
									
										
										
										
											2019-10-20 17:35:36 +08:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | const getManagedItem = (managedPath, path) => { | 
					
						
							| 
									
										
										
										
											2019-08-13 23:15:47 +08:00
										 |  |  | 	let i = managedPath.length; | 
					
						
							| 
									
										
										
										
											2019-08-13 23:58:51 +08:00
										 |  |  | 	let slashes = 1; | 
					
						
							| 
									
										
										
										
											2019-09-08 21:26:40 +08:00
										 |  |  | 	let startingPosition = true; | 
					
						
							| 
									
										
										
										
											2019-08-13 23:15:47 +08:00
										 |  |  | 	loop: while (i < path.length) { | 
					
						
							|  |  |  | 		switch (path.charCodeAt(i)) { | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			case 47: // slash
 | 
					
						
							|  |  |  | 			case 92: // backslash
 | 
					
						
							|  |  |  | 				if (--slashes === 0) break loop; | 
					
						
							| 
									
										
										
										
											2019-09-08 21:26:40 +08:00
										 |  |  | 				startingPosition = true; | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			case 46: // .
 | 
					
						
							|  |  |  | 				// hidden files are disallowed as managed items
 | 
					
						
							|  |  |  | 				// it's probably .yarn-integrity or .cache
 | 
					
						
							|  |  |  | 				if (startingPosition) return null; | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 				break; | 
					
						
							|  |  |  | 			case 64: // @
 | 
					
						
							| 
									
										
										
										
											2019-09-08 21:26:40 +08:00
										 |  |  | 				if (!startingPosition) return null; | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 				slashes++; | 
					
						
							|  |  |  | 				break; | 
					
						
							| 
									
										
										
										
											2019-09-08 21:26:40 +08:00
										 |  |  | 			default: | 
					
						
							|  |  |  | 				startingPosition = false; | 
					
						
							|  |  |  | 				break; | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		i++; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 	if (i === path.length) slashes--; | 
					
						
							|  |  |  | 	// return null when path is incomplete
 | 
					
						
							|  |  |  | 	if (slashes !== 0) return null; | 
					
						
							| 
									
										
										
										
											2019-08-13 23:58:51 +08:00
										 |  |  | 	// if (path.slice(i + 1, i + 13) === "node_modules")
 | 
					
						
							|  |  |  | 	if ( | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 		path.length >= i + 13 && | 
					
						
							| 
									
										
										
										
											2019-08-13 23:58:51 +08:00
										 |  |  | 		path.charCodeAt(i + 1) === 110 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 2) === 111 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 3) === 100 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 4) === 101 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 5) === 95 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 6) === 109 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 7) === 111 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 8) === 100 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 9) === 117 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 10) === 108 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 11) === 101 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 12) === 115 | 
					
						
							|  |  |  | 	) { | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 		// if this is the end of the path
 | 
					
						
							|  |  |  | 		if (path.length === i + 13) { | 
					
						
							|  |  |  | 			// return the node_modules directory
 | 
					
						
							|  |  |  | 			// it's special
 | 
					
						
							|  |  |  | 			return path; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-13 23:58:51 +08:00
										 |  |  | 		const c = path.charCodeAt(i + 13); | 
					
						
							| 
									
										
										
										
											2019-10-20 17:35:36 +08:00
										 |  |  | 		// if next symbol is slash or backslash
 | 
					
						
							| 
									
										
										
										
											2019-08-13 23:58:51 +08:00
										 |  |  | 		if (c === 47 || c === 92) { | 
					
						
							|  |  |  | 			// Managed subpath
 | 
					
						
							|  |  |  | 			return getManagedItem(path.slice(0, i + 14), path); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-08-13 23:15:47 +08:00
										 |  |  | 	return path.slice(0, i); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-14 23:49:45 +08:00
										 |  |  | /** | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  |  * @param {FileSystemInfoEntry} entry file system info entry | 
					
						
							| 
									
										
										
										
											2020-03-10 09:59:46 +08:00
										 |  |  |  * @returns {boolean} existence flag | 
					
						
							| 
									
										
										
										
											2019-11-14 23:49:45 +08:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2020-03-13 19:09:14 +08:00
										 |  |  | const toExistence = entry => { | 
					
						
							| 
									
										
										
										
											2019-11-14 23:49:45 +08:00
										 |  |  | 	return Boolean(entry); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-20 17:35:36 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Used to access information about the filesystem in a cached way | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | class FileSystemInfo { | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {InputFileSystem} fs file system | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 	 * @param {Object} options options | 
					
						
							|  |  |  | 	 * @param {Iterable<string>=} options.managedPaths paths that are only managed by a package manager | 
					
						
							| 
									
										
										
										
											2019-08-16 17:55:10 +08:00
										 |  |  | 	 * @param {Iterable<string>=} options.immutablePaths paths that are immutable | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 	 * @param {Logger=} options.logger logger used to log invalid snapshots | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 	constructor(fs, { managedPaths = [], immutablePaths = [], logger } = {}) { | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		this.fs = fs; | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 		this.logger = logger; | 
					
						
							|  |  |  | 		this._remainingLogs = logger ? 40 : 0; | 
					
						
							|  |  |  | 		this._loggedPaths = logger ? new Set() : undefined; | 
					
						
							| 
									
										
										
										
											2019-10-27 14:38:06 +08:00
										 |  |  | 		/** @type {WeakMap<Snapshot, boolean | (function(WebpackError=, boolean=): void)[]>} */ | 
					
						
							|  |  |  | 		this._snapshotCache = new WeakMap(); | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | 		this._fileTimestampsOptimization = new SnapshotOptimization( | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 			s => s.hasFileTimestamps(), | 
					
						
							|  |  |  | 			s => s.fileTimestamps, | 
					
						
							|  |  |  | 			(s, v) => s.setFileTimestamps(v) | 
					
						
							|  |  |  | 		); | 
					
						
							|  |  |  | 		this._fileHashesOptimization = new SnapshotOptimization( | 
					
						
							|  |  |  | 			s => s.hasFileHashes(), | 
					
						
							|  |  |  | 			s => s.fileHashes, | 
					
						
							|  |  |  | 			(s, v) => s.setFileHashes(v) | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | 		); | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		this._fileTshsOptimization = new SnapshotOptimization( | 
					
						
							|  |  |  | 			s => s.hasFileTshs(), | 
					
						
							|  |  |  | 			s => s.fileTshs, | 
					
						
							|  |  |  | 			(s, v) => s.setFileTshs(v) | 
					
						
							|  |  |  | 		); | 
					
						
							| 
									
										
										
										
											2020-08-20 17:41:20 +08:00
										 |  |  | 		this._contextTimestampsOptimization = new SnapshotOptimization( | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 			s => s.hasContextTimestamps(), | 
					
						
							|  |  |  | 			s => s.contextTimestamps, | 
					
						
							|  |  |  | 			(s, v) => s.setContextTimestamps(v) | 
					
						
							|  |  |  | 		); | 
					
						
							|  |  |  | 		this._contextHashesOptimization = new SnapshotOptimization( | 
					
						
							|  |  |  | 			s => s.hasContextHashes(), | 
					
						
							|  |  |  | 			s => s.contextHashes, | 
					
						
							|  |  |  | 			(s, v) => s.setContextHashes(v) | 
					
						
							| 
									
										
										
										
											2020-08-20 17:41:20 +08:00
										 |  |  | 		); | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		this._contextTshsOptimization = new SnapshotOptimization( | 
					
						
							|  |  |  | 			s => s.hasContextTshs(), | 
					
						
							|  |  |  | 			s => s.contextTshs, | 
					
						
							|  |  |  | 			(s, v) => s.setContextTshs(v) | 
					
						
							|  |  |  | 		); | 
					
						
							| 
									
										
										
										
											2020-08-20 17:41:20 +08:00
										 |  |  | 		this._missingExistenceOptimization = new SnapshotOptimization( | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 			s => s.hasMissingExistence(), | 
					
						
							|  |  |  | 			s => s.missingExistence, | 
					
						
							|  |  |  | 			(s, v) => s.setMissingExistence(v) | 
					
						
							| 
									
										
										
										
											2020-08-20 17:41:20 +08:00
										 |  |  | 		); | 
					
						
							|  |  |  | 		this._managedItemInfoOptimization = new SnapshotOptimization( | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 			s => s.hasManagedItemInfo(), | 
					
						
							|  |  |  | 			s => s.managedItemInfo, | 
					
						
							|  |  |  | 			(s, v) => s.setManagedItemInfo(v) | 
					
						
							| 
									
										
										
										
											2020-08-20 17:41:20 +08:00
										 |  |  | 		); | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 		this._managedFilesOptimization = new SnapshotOptimization( | 
					
						
							|  |  |  | 			s => s.hasManagedFiles(), | 
					
						
							|  |  |  | 			s => s.managedFiles, | 
					
						
							|  |  |  | 			(s, v) => s.setManagedFiles(v), | 
					
						
							|  |  |  | 			true | 
					
						
							|  |  |  | 		); | 
					
						
							|  |  |  | 		this._managedContextsOptimization = new SnapshotOptimization( | 
					
						
							|  |  |  | 			s => s.hasManagedContexts(), | 
					
						
							|  |  |  | 			s => s.managedContexts, | 
					
						
							|  |  |  | 			(s, v) => s.setManagedContexts(v), | 
					
						
							|  |  |  | 			true | 
					
						
							|  |  |  | 		); | 
					
						
							|  |  |  | 		this._managedMissingOptimization = new SnapshotOptimization( | 
					
						
							|  |  |  | 			s => s.hasManagedMissing(), | 
					
						
							|  |  |  | 			s => s.managedMissing, | 
					
						
							|  |  |  | 			(s, v) => s.setManagedMissing(v), | 
					
						
							|  |  |  | 			true | 
					
						
							|  |  |  | 		); | 
					
						
							| 
									
										
										
										
											2019-11-06 06:46:34 +08:00
										 |  |  | 		/** @type {Map<string, FileSystemInfoEntry | "ignore" | null>} */ | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		this._fileTimestamps = new Map(); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		/** @type {Map<string, string>} */ | 
					
						
							|  |  |  | 		this._fileHashes = new Map(); | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		/** @type {Map<string, TimestampAndHash | string>} */ | 
					
						
							|  |  |  | 		this._fileTshs = new Map(); | 
					
						
							| 
									
										
										
										
											2019-11-06 06:46:34 +08:00
										 |  |  | 		/** @type {Map<string, FileSystemInfoEntry | "ignore" | null>} */ | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		this._contextTimestamps = new Map(); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		/** @type {Map<string, string>} */ | 
					
						
							|  |  |  | 		this._contextHashes = new Map(); | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		/** @type {Map<string, TimestampAndHash | string>} */ | 
					
						
							|  |  |  | 		this._contextTshs = new Map(); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		/** @type {Map<string, string>} */ | 
					
						
							|  |  |  | 		this._managedItems = new Map(); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		/** @type {AsyncQueue<string, string, FileSystemInfoEntry | null>} */ | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		this.fileTimestampQueue = new AsyncQueue({ | 
					
						
							|  |  |  | 			name: "file timestamp", | 
					
						
							|  |  |  | 			parallelism: 30, | 
					
						
							|  |  |  | 			processor: this._readFileTimestamp.bind(this) | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		/** @type {AsyncQueue<string, string, string | null>} */ | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		this.fileHashQueue = new AsyncQueue({ | 
					
						
							|  |  |  | 			name: "file hash", | 
					
						
							|  |  |  | 			parallelism: 10, | 
					
						
							|  |  |  | 			processor: this._readFileHash.bind(this) | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		/** @type {AsyncQueue<string, string, FileSystemInfoEntry | null>} */ | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		this.contextTimestampQueue = new AsyncQueue({ | 
					
						
							|  |  |  | 			name: "context timestamp", | 
					
						
							|  |  |  | 			parallelism: 2, | 
					
						
							|  |  |  | 			processor: this._readContextTimestamp.bind(this) | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		/** @type {AsyncQueue<string, string, string | null>} */ | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		this.contextHashQueue = new AsyncQueue({ | 
					
						
							|  |  |  | 			name: "context hash", | 
					
						
							|  |  |  | 			parallelism: 2, | 
					
						
							|  |  |  | 			processor: this._readContextHash.bind(this) | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		/** @type {AsyncQueue<string, string, string | null>} */ | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		this.managedItemQueue = new AsyncQueue({ | 
					
						
							|  |  |  | 			name: "managed item info", | 
					
						
							|  |  |  | 			parallelism: 10, | 
					
						
							|  |  |  | 			processor: this._getManagedItemInfo.bind(this) | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		/** @type {AsyncQueue<string, string, Set<string>>} */ | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 		this.managedItemDirectoryQueue = new AsyncQueue({ | 
					
						
							|  |  |  | 			name: "managed item directory info", | 
					
						
							|  |  |  | 			parallelism: 10, | 
					
						
							|  |  |  | 			processor: this._getManagedItemDirectoryInfo.bind(this) | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		this.managedPaths = Array.from(managedPaths); | 
					
						
							|  |  |  | 		this.managedPathsWithSlash = this.managedPaths.map(p => | 
					
						
							|  |  |  | 			join(fs, p, "_").slice(0, -1) | 
					
						
							|  |  |  | 		); | 
					
						
							| 
									
										
										
										
											2019-08-16 17:55:10 +08:00
										 |  |  | 		this.immutablePaths = Array.from(immutablePaths); | 
					
						
							|  |  |  | 		this.immutablePathsWithSlash = this.immutablePaths.map(p => | 
					
						
							|  |  |  | 			join(fs, p, "_").slice(0, -1) | 
					
						
							|  |  |  | 		); | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-15 20:39:09 +08:00
										 |  |  | 		this._cachedDeprecatedFileTimestamps = undefined; | 
					
						
							|  |  |  | 		this._cachedDeprecatedContextTimestamps = undefined; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 		this._statCreatedSnapshots = 0; | 
					
						
							|  |  |  | 		this._statTestedSnapshotsCached = 0; | 
					
						
							|  |  |  | 		this._statTestedSnapshotsNotCached = 0; | 
					
						
							|  |  |  | 		this._statTestedChildrenCached = 0; | 
					
						
							|  |  |  | 		this._statTestedChildrenNotCached = 0; | 
					
						
							|  |  |  | 		this._statTestedEntries = 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	logStatistics() { | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 		const logWhenMessage = (header, message) => { | 
					
						
							|  |  |  | 			if (message) { | 
					
						
							|  |  |  | 				this.logger.log(`${header}: ${message}`); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 		this.logger.log(`${this._statCreatedSnapshots} new snapshots created`); | 
					
						
							|  |  |  | 		this.logger.log( | 
					
						
							|  |  |  | 			`${ | 
					
						
							| 
									
										
										
										
											2020-08-22 17:15:07 +08:00
										 |  |  | 				this._statTestedSnapshotsNotCached && | 
					
						
							|  |  |  | 				Math.round( | 
					
						
							|  |  |  | 					(this._statTestedSnapshotsNotCached * 100) / | 
					
						
							|  |  |  | 						(this._statTestedSnapshotsCached + | 
					
						
							|  |  |  | 							this._statTestedSnapshotsNotCached) | 
					
						
							|  |  |  | 				) | 
					
						
							|  |  |  | 			}% root snapshot uncached (${this._statTestedSnapshotsNotCached} / ${ | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 				this._statTestedSnapshotsCached + this._statTestedSnapshotsNotCached | 
					
						
							| 
									
										
										
										
											2020-08-22 17:15:07 +08:00
										 |  |  | 			})`
 | 
					
						
							|  |  |  | 		); | 
					
						
							|  |  |  | 		this.logger.log( | 
					
						
							|  |  |  | 			`${ | 
					
						
							|  |  |  | 				this._statTestedChildrenNotCached && | 
					
						
							|  |  |  | 				Math.round( | 
					
						
							|  |  |  | 					(this._statTestedChildrenNotCached * 100) / | 
					
						
							|  |  |  | 						(this._statTestedChildrenCached + this._statTestedChildrenNotCached) | 
					
						
							|  |  |  | 				) | 
					
						
							|  |  |  | 			}% children snapshot uncached (${this._statTestedChildrenNotCached} / ${ | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 				this._statTestedChildrenCached + this._statTestedChildrenNotCached | 
					
						
							| 
									
										
										
										
											2020-08-22 17:15:07 +08:00
										 |  |  | 			})`
 | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 		); | 
					
						
							| 
									
										
										
										
											2020-08-22 17:15:07 +08:00
										 |  |  | 		this.logger.log(`${this._statTestedEntries} entries tested`); | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 		this.logger.log( | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 			`File info in cache: ${this._fileTimestamps.size} timestamps ${this._fileHashes.size} hashes ${this._fileTshs.size} timestamp hash combinations` | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 		); | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 		logWhenMessage( | 
					
						
							|  |  |  | 			`File timestamp snapshot optimization`, | 
					
						
							|  |  |  | 			this._fileTimestampsOptimization.getStatisticMessage() | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 		); | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 		logWhenMessage( | 
					
						
							|  |  |  | 			`File hash snapshot optimization`, | 
					
						
							|  |  |  | 			this._fileHashesOptimization.getStatisticMessage() | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 		); | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		logWhenMessage( | 
					
						
							|  |  |  | 			`File timestamp hash combination snapshot optimization`, | 
					
						
							|  |  |  | 			this._fileTshsOptimization.getStatisticMessage() | 
					
						
							|  |  |  | 		); | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 		this.logger.log( | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 			`Directory info in cache: ${this._contextTimestamps.size} timestamps ${this._contextHashes.size} hashes ${this._contextTshs.size} timestamp hash combinations` | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 		); | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 		logWhenMessage( | 
					
						
							|  |  |  | 			`Directory timestamp snapshot optimization`, | 
					
						
							|  |  |  | 			this._contextTimestampsOptimization.getStatisticMessage() | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 		); | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 		logWhenMessage( | 
					
						
							|  |  |  | 			`Directory hash snapshot optimization`, | 
					
						
							|  |  |  | 			this._contextHashesOptimization.getStatisticMessage() | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 		); | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		logWhenMessage( | 
					
						
							|  |  |  | 			`Directory timestamp hash combination snapshot optimization`, | 
					
						
							|  |  |  | 			this._contextTshsOptimization.getStatisticMessage() | 
					
						
							|  |  |  | 		); | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 		logWhenMessage( | 
					
						
							|  |  |  | 			`Missing items snapshot optimization`, | 
					
						
							|  |  |  | 			this._missingExistenceOptimization.getStatisticMessage() | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 		); | 
					
						
							|  |  |  | 		this.logger.log( | 
					
						
							|  |  |  | 			`Managed items info in cache: ${this._managedItems.size} items` | 
					
						
							|  |  |  | 		); | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 		logWhenMessage( | 
					
						
							|  |  |  | 			`Managed items snapshot optimization`, | 
					
						
							|  |  |  | 			this._managedItemInfoOptimization.getStatisticMessage() | 
					
						
							|  |  |  | 		); | 
					
						
							|  |  |  | 		logWhenMessage( | 
					
						
							|  |  |  | 			`Managed files snapshot optimization`, | 
					
						
							|  |  |  | 			this._managedFilesOptimization.getStatisticMessage() | 
					
						
							|  |  |  | 		); | 
					
						
							|  |  |  | 		logWhenMessage( | 
					
						
							|  |  |  | 			`Managed contexts snapshot optimization`, | 
					
						
							|  |  |  | 			this._managedContextsOptimization.getStatisticMessage() | 
					
						
							|  |  |  | 		); | 
					
						
							|  |  |  | 		logWhenMessage( | 
					
						
							|  |  |  | 			`Managed missing snapshot optimization`, | 
					
						
							|  |  |  | 			this._managedMissingOptimization.getStatisticMessage() | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 		); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-15 00:28:10 +08:00
										 |  |  | 	_log(path, reason, ...args) { | 
					
						
							|  |  |  | 		const key = path + reason; | 
					
						
							|  |  |  | 		if (this._loggedPaths.has(key)) return; | 
					
						
							|  |  |  | 		this._loggedPaths.add(key); | 
					
						
							|  |  |  | 		this.logger.debug(`${path} invalidated because ${reason}`, ...args); | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 		if (--this._remainingLogs === 0) { | 
					
						
							|  |  |  | 			this.logger.debug( | 
					
						
							| 
									
										
										
										
											2020-03-10 09:59:46 +08:00
										 |  |  | 				"Logging limit has been reached and no further logging will be emitted by FileSystemInfo" | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	/** | 
					
						
							| 
									
										
										
										
											2019-11-06 06:46:34 +08:00
										 |  |  | 	 * @param {Map<string, FileSystemInfoEntry | "ignore" | null>} map timestamps | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	addFileTimestamps(map) { | 
					
						
							|  |  |  | 		for (const [path, ts] of map) { | 
					
						
							|  |  |  | 			this._fileTimestamps.set(path, ts); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-09-15 20:39:09 +08:00
										 |  |  | 		this._cachedDeprecatedFileTimestamps = undefined; | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							| 
									
										
										
										
											2019-11-06 06:46:34 +08:00
										 |  |  | 	 * @param {Map<string, FileSystemInfoEntry | "ignore" | null>} map timestamps | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	addContextTimestamps(map) { | 
					
						
							|  |  |  | 		for (const [path, ts] of map) { | 
					
						
							|  |  |  | 			this._contextTimestamps.set(path, ts); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-09-15 20:39:09 +08:00
										 |  |  | 		this._cachedDeprecatedContextTimestamps = undefined; | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {string} path file path | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 	 * @param {function(WebpackError=, (FileSystemInfoEntry | "ignore" | null)=): void} callback callback function | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	getFileTimestamp(path, callback) { | 
					
						
							|  |  |  | 		const cache = this._fileTimestamps.get(path); | 
					
						
							| 
									
										
										
										
											2018-09-28 03:28:07 +08:00
										 |  |  | 		if (cache !== undefined) return callback(null, cache); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		this.fileTimestampQueue.add(path, callback); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {string} path context path | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 	 * @param {function(WebpackError=, (FileSystemInfoEntry | "ignore" | null)=): void} callback callback function | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	getContextTimestamp(path, callback) { | 
					
						
							|  |  |  | 		const cache = this._contextTimestamps.get(path); | 
					
						
							| 
									
										
										
										
											2018-09-28 03:28:07 +08:00
										 |  |  | 		if (cache !== undefined) return callback(null, cache); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		this.contextTimestampQueue.add(path, callback); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {string} path file path | 
					
						
							|  |  |  | 	 * @param {function(WebpackError=, string=): void} callback callback function | 
					
						
							|  |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	getFileHash(path, callback) { | 
					
						
							|  |  |  | 		const cache = this._fileHashes.get(path); | 
					
						
							|  |  |  | 		if (cache !== undefined) return callback(null, cache); | 
					
						
							|  |  |  | 		this.fileHashQueue.add(path, callback); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {string} path context path | 
					
						
							|  |  |  | 	 * @param {function(WebpackError=, string=): void} callback callback function | 
					
						
							|  |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	getContextHash(path, callback) { | 
					
						
							|  |  |  | 		const cache = this._contextHashes.get(path); | 
					
						
							|  |  |  | 		if (cache !== undefined) return callback(null, cache); | 
					
						
							|  |  |  | 		this.contextHashQueue.add(path, callback); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {string} context context directory | 
					
						
							|  |  |  | 	 * @param {Iterable<string>} deps dependencies | 
					
						
							|  |  |  | 	 * @param {function(Error=, ResolveBuildDependenciesResult=): void} callback callback function | 
					
						
							|  |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 	resolveBuildDependencies(context, deps, callback) { | 
					
						
							| 
									
										
										
										
											2019-10-20 17:35:36 +08:00
										 |  |  | 		/** @type {Set<string>} */ | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		const files = new Set(); | 
					
						
							| 
									
										
										
										
											2019-10-20 17:35:36 +08:00
										 |  |  | 		/** @type {Set<string>} */ | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		const directories = new Set(); | 
					
						
							| 
									
										
										
										
											2019-10-20 17:35:36 +08:00
										 |  |  | 		/** @type {Set<string>} */ | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		const missing = new Set(); | 
					
						
							| 
									
										
										
										
											2019-10-20 17:35:36 +08:00
										 |  |  | 		/** @type {Set<string>} */ | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | 		const resolveFiles = new Set(); | 
					
						
							| 
									
										
										
										
											2019-10-20 17:35:36 +08:00
										 |  |  | 		/** @type {Set<string>} */ | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | 		const resolveDirectories = new Set(); | 
					
						
							| 
									
										
										
										
											2019-10-20 17:35:36 +08:00
										 |  |  | 		/** @type {Set<string>} */ | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | 		const resolveMissing = new Set(); | 
					
						
							| 
									
										
										
										
											2019-10-20 17:35:36 +08:00
										 |  |  | 		/** @type {Map<string, string>} */ | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | 		const resolveResults = new Map(); | 
					
						
							| 
									
										
										
										
											2019-08-20 19:50:14 +08:00
										 |  |  | 		/** @type {asyncLib.QueueObject<{type: number, path: string, context?: string, expected?: string }, Error>} */ | 
					
						
							|  |  |  | 		const queue = asyncLib.queue( | 
					
						
							|  |  |  | 			({ type, context, path, expected }, callback) => { | 
					
						
							|  |  |  | 				const resolveDirectory = path => { | 
					
						
							|  |  |  | 					const key = `d\n${context}\n${path}`; | 
					
						
							|  |  |  | 					if (resolveResults.has(key)) { | 
					
						
							|  |  |  | 						return callback(); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					resolveContext( | 
					
						
							|  |  |  | 						context, | 
					
						
							|  |  |  | 						path, | 
					
						
							|  |  |  | 						{ | 
					
						
							|  |  |  | 							fileDependencies: resolveFiles, | 
					
						
							|  |  |  | 							contextDependencies: resolveDirectories, | 
					
						
							|  |  |  | 							missingDependencies: resolveMissing | 
					
						
							|  |  |  | 						}, | 
					
						
							|  |  |  | 						(err, result) => { | 
					
						
							|  |  |  | 							if (err) { | 
					
						
							|  |  |  | 								if ( | 
					
						
							|  |  |  | 									err.code === "ENOENT" || | 
					
						
							|  |  |  | 									err.code === "UNDECLARED_DEPENDENCY" | 
					
						
							| 
									
										
										
										
											2020-08-11 01:24:11 +08:00
										 |  |  | 								) { | 
					
						
							| 
									
										
										
										
											2019-08-20 19:50:14 +08:00
										 |  |  | 									return callback(); | 
					
						
							| 
									
										
										
										
											2020-08-11 01:24:11 +08:00
										 |  |  | 								} | 
					
						
							|  |  |  | 								err.message += `\nwhile resolving '${path}' in ${context} to a directory`; | 
					
						
							| 
									
										
										
										
											2019-08-20 19:50:14 +08:00
										 |  |  | 								return callback(err); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 							resolveResults.set(key, result); | 
					
						
							|  |  |  | 							queue.push({ | 
					
						
							|  |  |  | 								type: RBDT_DIRECTORY, | 
					
						
							|  |  |  | 								path: result | 
					
						
							|  |  |  | 							}); | 
					
						
							|  |  |  | 							callback(); | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2019-08-20 19:50:14 +08:00
										 |  |  | 					); | 
					
						
							|  |  |  | 				}; | 
					
						
							|  |  |  | 				const resolveFile = path => { | 
					
						
							|  |  |  | 					const key = `f\n${context}\n${path}`; | 
					
						
							|  |  |  | 					if (resolveResults.has(key)) { | 
					
						
							|  |  |  | 						return callback(); | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-08-20 19:50:14 +08:00
										 |  |  | 					resolve( | 
					
						
							|  |  |  | 						context, | 
					
						
							|  |  |  | 						path, | 
					
						
							|  |  |  | 						{ | 
					
						
							|  |  |  | 							fileDependencies: resolveFiles, | 
					
						
							|  |  |  | 							contextDependencies: resolveDirectories, | 
					
						
							|  |  |  | 							missingDependencies: resolveMissing | 
					
						
							|  |  |  | 						}, | 
					
						
							|  |  |  | 						(err, result) => { | 
					
						
							|  |  |  | 							if (expected) { | 
					
						
							|  |  |  | 								if (result === expected) { | 
					
						
							|  |  |  | 									resolveResults.set(key, result); | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 							} else { | 
					
						
							|  |  |  | 								if (err) { | 
					
						
							|  |  |  | 									if ( | 
					
						
							|  |  |  | 										err.code === "ENOENT" || | 
					
						
							|  |  |  | 										err.code === "UNDECLARED_DEPENDENCY" | 
					
						
							| 
									
										
										
										
											2020-08-11 01:24:11 +08:00
										 |  |  | 									) { | 
					
						
							| 
									
										
										
										
											2019-08-20 19:50:14 +08:00
										 |  |  | 										return callback(); | 
					
						
							| 
									
										
										
										
											2020-08-11 01:24:11 +08:00
										 |  |  | 									} | 
					
						
							|  |  |  | 									err.message += `\nwhile resolving '${path}' in ${context} as file`; | 
					
						
							| 
									
										
										
										
											2019-08-20 19:50:14 +08:00
										 |  |  | 									return callback(err); | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 								resolveResults.set(key, result); | 
					
						
							|  |  |  | 								queue.push({ | 
					
						
							|  |  |  | 									type: RBDT_FILE, | 
					
						
							|  |  |  | 									path: result | 
					
						
							|  |  |  | 								}); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 							callback(); | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2019-08-20 19:50:14 +08:00
										 |  |  | 					); | 
					
						
							|  |  |  | 				}; | 
					
						
							|  |  |  | 				switch (type) { | 
					
						
							|  |  |  | 					case RBDT_RESOLVE: { | 
					
						
							|  |  |  | 						const isDirectory = /[\\/]$/.test(path); | 
					
						
							|  |  |  | 						if (isDirectory) { | 
					
						
							|  |  |  | 							resolveDirectory(path.slice(0, path.length - 1)); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							resolveFile(path); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						break; | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-08-20 19:50:14 +08:00
										 |  |  | 					case RBDT_RESOLVE_DIRECTORY: { | 
					
						
							|  |  |  | 						resolveDirectory(path); | 
					
						
							|  |  |  | 						break; | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-08-20 19:50:14 +08:00
										 |  |  | 					case RBDT_RESOLVE_FILE: { | 
					
						
							|  |  |  | 						resolveFile(path); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 						break; | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-08-20 19:50:14 +08:00
										 |  |  | 					case RBDT_FILE: { | 
					
						
							|  |  |  | 						if (files.has(path)) { | 
					
						
							|  |  |  | 							callback(); | 
					
						
							|  |  |  | 							break; | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2019-08-20 19:50:14 +08:00
										 |  |  | 						this.fs.realpath(path, (err, realPath) => { | 
					
						
							|  |  |  | 							if (err) return callback(err); | 
					
						
							|  |  |  | 							if (realPath !== path) { | 
					
						
							|  |  |  | 								resolveFiles.add(path); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 							if (!files.has(realPath)) { | 
					
						
							|  |  |  | 								files.add(realPath); | 
					
						
							|  |  |  | 								queue.push({ | 
					
						
							|  |  |  | 									type: RBDT_FILE_DEPENDENCIES, | 
					
						
							|  |  |  | 									path: realPath | 
					
						
							|  |  |  | 								}); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 							callback(); | 
					
						
							|  |  |  | 						}); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 						break; | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-08-20 19:50:14 +08:00
										 |  |  | 					case RBDT_DIRECTORY: { | 
					
						
							|  |  |  | 						if (directories.has(path)) { | 
					
						
							|  |  |  | 							callback(); | 
					
						
							|  |  |  | 							break; | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2019-08-20 19:50:14 +08:00
										 |  |  | 						this.fs.realpath(path, (err, realPath) => { | 
					
						
							|  |  |  | 							if (err) return callback(err); | 
					
						
							|  |  |  | 							if (realPath !== path) { | 
					
						
							|  |  |  | 								resolveFiles.add(path); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 							if (!directories.has(realPath)) { | 
					
						
							|  |  |  | 								directories.add(realPath); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 								queue.push({ | 
					
						
							| 
									
										
										
										
											2019-08-20 19:50:14 +08:00
										 |  |  | 									type: RBDT_DIRECTORY_DEPENDENCIES, | 
					
						
							|  |  |  | 									path: realPath | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 								}); | 
					
						
							|  |  |  | 							} | 
					
						
							| 
									
										
										
										
											2019-08-20 19:50:14 +08:00
										 |  |  | 							callback(); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 						}); | 
					
						
							| 
									
										
										
										
											2019-08-20 19:50:14 +08:00
										 |  |  | 						break; | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-08-20 19:50:14 +08:00
										 |  |  | 					case RBDT_FILE_DEPENDENCIES: { | 
					
						
							| 
									
										
										
										
											2020-08-11 01:24:11 +08:00
										 |  |  | 						// TODO this probably doesn't work correctly with ESM dependencies
 | 
					
						
							| 
									
										
										
										
											2019-08-20 19:50:14 +08:00
										 |  |  | 						/** @type {NodeModule} */ | 
					
						
							|  |  |  | 						const module = require.cache[path]; | 
					
						
							|  |  |  | 						if (module && Array.isArray(module.children)) { | 
					
						
							|  |  |  | 							children: for (const child of module.children) { | 
					
						
							|  |  |  | 								let childPath = child.filename; | 
					
						
							|  |  |  | 								if (childPath) { | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 									queue.push({ | 
					
						
							| 
									
										
										
										
											2019-08-20 19:50:14 +08:00
										 |  |  | 										type: RBDT_FILE, | 
					
						
							|  |  |  | 										path: childPath | 
					
						
							|  |  |  | 									}); | 
					
						
							|  |  |  | 									if (childPath.endsWith(".js")) | 
					
						
							|  |  |  | 										childPath = childPath.slice(0, -3); | 
					
						
							|  |  |  | 									const context = dirname(this.fs, path); | 
					
						
							|  |  |  | 									for (const modulePath of module.paths) { | 
					
						
							|  |  |  | 										if (childPath.startsWith(modulePath)) { | 
					
						
							|  |  |  | 											const request = childPath.slice(modulePath.length + 1); | 
					
						
							|  |  |  | 											queue.push({ | 
					
						
							|  |  |  | 												type: RBDT_RESOLVE_FILE, | 
					
						
							|  |  |  | 												context, | 
					
						
							|  |  |  | 												path: request, | 
					
						
							|  |  |  | 												expected: childPath | 
					
						
							|  |  |  | 											}); | 
					
						
							|  |  |  | 											continue children; | 
					
						
							|  |  |  | 										} | 
					
						
							|  |  |  | 									} | 
					
						
							|  |  |  | 									let request = relative(this.fs, context, childPath); | 
					
						
							|  |  |  | 									request = request.replace(/\\/g, "/"); | 
					
						
							|  |  |  | 									if (!request.startsWith("../")) request = `./${request}`; | 
					
						
							|  |  |  | 									queue.push({ | 
					
						
							|  |  |  | 										type: RBDT_RESOLVE_FILE, | 
					
						
							|  |  |  | 										context, | 
					
						
							|  |  |  | 										path: request, | 
					
						
							|  |  |  | 										expected: child.filename | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 									}); | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 							} | 
					
						
							| 
									
										
										
										
											2019-08-20 19:50:14 +08:00
										 |  |  | 						} else { | 
					
						
							|  |  |  | 							// Unable to get dependencies from module system
 | 
					
						
							|  |  |  | 							// This may be because of an incomplete require.cache implementation like in jest
 | 
					
						
							|  |  |  | 							// Assume requires stay in directory and add the whole directory
 | 
					
						
							|  |  |  | 							const directory = dirname(this.fs, path); | 
					
						
							|  |  |  | 							queue.push({ | 
					
						
							|  |  |  | 								type: RBDT_DIRECTORY, | 
					
						
							|  |  |  | 								path: directory | 
					
						
							|  |  |  | 							}); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2020-09-17 16:57:47 +08:00
										 |  |  | 						process.nextTick(callback); | 
					
						
							| 
									
										
										
										
											2019-08-20 19:50:14 +08:00
										 |  |  | 						break; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					case RBDT_DIRECTORY_DEPENDENCIES: { | 
					
						
							|  |  |  | 						const match = /(^.+[\\/]node_modules[\\/](?:@[^\\/]+[\\/])?[^\\/]+)/.exec( | 
					
						
							|  |  |  | 							path | 
					
						
							|  |  |  | 						); | 
					
						
							|  |  |  | 						const packagePath = match ? match[1] : path; | 
					
						
							|  |  |  | 						const packageJson = join(this.fs, packagePath, "package.json"); | 
					
						
							|  |  |  | 						this.fs.readFile(packageJson, (err, content) => { | 
					
						
							|  |  |  | 							if (err) { | 
					
						
							|  |  |  | 								if (err.code === "ENOENT") { | 
					
						
							|  |  |  | 									resolveMissing.add(packageJson); | 
					
						
							|  |  |  | 									const parent = dirname(this.fs, packagePath); | 
					
						
							|  |  |  | 									if (parent !== packagePath) { | 
					
						
							|  |  |  | 										queue.push({ | 
					
						
							|  |  |  | 											type: RBDT_DIRECTORY_DEPENDENCIES, | 
					
						
							|  |  |  | 											path: parent | 
					
						
							|  |  |  | 										}); | 
					
						
							|  |  |  | 									} | 
					
						
							|  |  |  | 									callback(); | 
					
						
							|  |  |  | 									return; | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 								return callback(err); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 							resolveFiles.add(packageJson); | 
					
						
							|  |  |  | 							let packageData; | 
					
						
							|  |  |  | 							try { | 
					
						
							|  |  |  | 								packageData = JSON.parse(content.toString("utf-8")); | 
					
						
							|  |  |  | 							} catch (e) { | 
					
						
							|  |  |  | 								return callback(e); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 							const depsObject = packageData.dependencies; | 
					
						
							|  |  |  | 							if (typeof depsObject === "object" && depsObject) { | 
					
						
							|  |  |  | 								for (const dep of Object.keys(depsObject)) { | 
					
						
							|  |  |  | 									queue.push({ | 
					
						
							|  |  |  | 										type: RBDT_RESOLVE_DIRECTORY, | 
					
						
							|  |  |  | 										context: packagePath, | 
					
						
							|  |  |  | 										path: dep | 
					
						
							|  |  |  | 									}); | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 							callback(); | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-08-20 19:50:14 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			50 | 
					
						
							|  |  |  | 		); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		queue.drain = () => { | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | 			callback(null, { | 
					
						
							|  |  |  | 				files, | 
					
						
							|  |  |  | 				directories, | 
					
						
							|  |  |  | 				missing, | 
					
						
							|  |  |  | 				resolveResults, | 
					
						
							|  |  |  | 				resolveDependencies: { | 
					
						
							|  |  |  | 					files: resolveFiles, | 
					
						
							|  |  |  | 					directories: resolveDirectories, | 
					
						
							|  |  |  | 					missing: resolveMissing | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		}; | 
					
						
							|  |  |  | 		queue.error = err => { | 
					
						
							|  |  |  | 			callback(err); | 
					
						
							|  |  |  | 			callback = () => {}; | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 		let jobQueued = false; | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		for (const dep of deps) { | 
					
						
							|  |  |  | 			queue.push({ | 
					
						
							| 
									
										
										
										
											2019-08-13 23:21:19 +08:00
										 |  |  | 				type: RBDT_RESOLVE, | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 				context, | 
					
						
							|  |  |  | 				path: dep | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 			jobQueued = true; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (!jobQueued) { | 
					
						
							|  |  |  | 			// queue won't call drain when no jobs are queue
 | 
					
						
							|  |  |  | 			queue.drain(); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {Map<string, string>} resolveResults results from resolving | 
					
						
							|  |  |  | 	 * @param {function(Error=, boolean=): void} callback callback with true when resolveResults resolve the same way | 
					
						
							|  |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	checkResolveResultsValid(resolveResults, callback) { | 
					
						
							|  |  |  | 		asyncLib.eachLimit( | 
					
						
							|  |  |  | 			resolveResults, | 
					
						
							|  |  |  | 			20, | 
					
						
							|  |  |  | 			([key, expectedResult], callback) => { | 
					
						
							|  |  |  | 				const [type, context, path] = key.split("\n"); | 
					
						
							|  |  |  | 				switch (type) { | 
					
						
							|  |  |  | 					case "d": | 
					
						
							| 
									
										
										
										
											2019-11-11 22:25:03 +08:00
										 |  |  | 						resolveContext(context, path, {}, (err, result) => { | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | 							if (err) return callback(err); | 
					
						
							|  |  |  | 							if (result !== expectedResult) return callback(INVALID); | 
					
						
							|  |  |  | 							callback(); | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 					case "f": | 
					
						
							| 
									
										
										
										
											2019-11-11 22:25:03 +08:00
										 |  |  | 						resolve(context, path, {}, (err, result) => { | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | 							if (err) return callback(err); | 
					
						
							|  |  |  | 							if (result !== expectedResult) return callback(INVALID); | 
					
						
							|  |  |  | 							callback(); | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 					default: | 
					
						
							|  |  |  | 						callback(new Error("Unexpected type in resolve result key")); | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			/** | 
					
						
							|  |  |  | 			 * @param {Error | typeof INVALID=} err error or invalid flag | 
					
						
							|  |  |  | 			 * @returns {void} | 
					
						
							|  |  |  | 			 */ | 
					
						
							|  |  |  | 			err => { | 
					
						
							|  |  |  | 				if (err === INVALID) { | 
					
						
							|  |  |  | 					return callback(null, false); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if (err) { | 
					
						
							|  |  |  | 					return callback(err); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return callback(null, true); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * @param {number} startTime when processing the files has started | 
					
						
							|  |  |  | 	 * @param {Iterable<string>} files all files | 
					
						
							|  |  |  | 	 * @param {Iterable<string>} directories all directories | 
					
						
							|  |  |  | 	 * @param {Iterable<string>} missing all missing files or directories | 
					
						
							|  |  |  | 	 * @param {Object} options options object (for future extensions) | 
					
						
							| 
									
										
										
										
											2019-11-14 23:49:45 +08:00
										 |  |  | 	 * @param {boolean=} options.hash should use hash to snapshot | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 	 * @param {boolean=} options.timestamp should use timestamp to snapshot | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 	 * @param {function(WebpackError=, Snapshot=): void} callback callback function | 
					
						
							|  |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 	createSnapshot(startTime, files, directories, missing, options, callback) { | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		/** @type {Map<string, FileSystemInfoEntry>} */ | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		const fileTimestamps = new Map(); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		/** @type {Map<string, string>} */ | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		const fileHashes = new Map(); | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		/** @type {Map<string, TimestampAndHash | string>} */ | 
					
						
							|  |  |  | 		const fileTshs = new Map(); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		/** @type {Map<string, FileSystemInfoEntry>} */ | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		const contextTimestamps = new Map(); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		/** @type {Map<string, string>} */ | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		const contextHashes = new Map(); | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		/** @type {Map<string, TimestampAndHash | string>} */ | 
					
						
							|  |  |  | 		const contextTshs = new Map(); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		/** @type {Map<string, boolean>} */ | 
					
						
							| 
									
										
										
										
											2020-03-13 19:09:14 +08:00
										 |  |  | 		const missingExistence = new Map(); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		/** @type {Map<string, string>} */ | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		const managedItemInfo = new Map(); | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 		/** @type {Set<string>} */ | 
					
						
							|  |  |  | 		const managedFiles = new Set(); | 
					
						
							|  |  |  | 		/** @type {Set<string>} */ | 
					
						
							|  |  |  | 		const managedContexts = new Set(); | 
					
						
							|  |  |  | 		/** @type {Set<string>} */ | 
					
						
							|  |  |  | 		const managedMissing = new Set(); | 
					
						
							| 
									
										
										
										
											2020-01-31 17:59:28 +08:00
										 |  |  | 		/** @type {Set<Snapshot>} */ | 
					
						
							|  |  |  | 		const children = new Set(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/** @type {Set<string>} */ | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | 		let unsharedFileTimestamps; | 
					
						
							| 
									
										
										
										
											2020-08-20 17:41:20 +08:00
										 |  |  | 		/** @type {Set<string>} */ | 
					
						
							|  |  |  | 		let unsharedFileHashes; | 
					
						
							|  |  |  | 		/** @type {Set<string>} */ | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		let unsharedFileTshs; | 
					
						
							|  |  |  | 		/** @type {Set<string>} */ | 
					
						
							| 
									
										
										
										
											2020-08-20 17:41:20 +08:00
										 |  |  | 		let unsharedContextTimestamps; | 
					
						
							|  |  |  | 		/** @type {Set<string>} */ | 
					
						
							|  |  |  | 		let unsharedContextHashes; | 
					
						
							|  |  |  | 		/** @type {Set<string>} */ | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		let unsharedContextTshs; | 
					
						
							|  |  |  | 		/** @type {Set<string>} */ | 
					
						
							| 
									
										
										
										
											2020-08-20 17:41:20 +08:00
										 |  |  | 		let unsharedMissingExistence; | 
					
						
							|  |  |  | 		/** @type {Set<string>} */ | 
					
						
							|  |  |  | 		let unsharedManagedItemInfo; | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		/** @type {Set<string>} */ | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		const managedItems = new Set(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		/** 1 = timestamp, 2 = hash, 3 = timestamp + hash */ | 
					
						
							|  |  |  | 		const mode = options && options.hash ? (options.timestamp ? 3 : 2) : 1; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		let jobs = 1; | 
					
						
							|  |  |  | 		const jobDone = () => { | 
					
						
							|  |  |  | 			if (--jobs === 0) { | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 				const snapshot = new Snapshot(); | 
					
						
							|  |  |  | 				if (startTime) snapshot.setStartTime(startTime); | 
					
						
							| 
									
										
										
										
											2020-08-20 17:41:20 +08:00
										 |  |  | 				if (fileTimestamps.size !== 0) { | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 					snapshot.setFileTimestamps(fileTimestamps); | 
					
						
							| 
									
										
										
										
											2020-08-20 17:41:20 +08:00
										 |  |  | 					this._fileTimestampsOptimization.storeUnsharedSnapshot( | 
					
						
							|  |  |  | 						snapshot, | 
					
						
							|  |  |  | 						unsharedFileTimestamps | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if (fileHashes.size !== 0) { | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 					snapshot.setFileHashes(fileHashes); | 
					
						
							| 
									
										
										
										
											2020-08-20 17:41:20 +08:00
										 |  |  | 					this._fileHashesOptimization.storeUnsharedSnapshot( | 
					
						
							|  |  |  | 						snapshot, | 
					
						
							|  |  |  | 						unsharedFileHashes | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 				if (fileTshs.size !== 0) { | 
					
						
							|  |  |  | 					snapshot.setFileTshs(fileTshs); | 
					
						
							|  |  |  | 					this._fileTshsOptimization.storeUnsharedSnapshot( | 
					
						
							|  |  |  | 						snapshot, | 
					
						
							|  |  |  | 						unsharedFileTshs | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-08-20 17:41:20 +08:00
										 |  |  | 				if (contextTimestamps.size !== 0) { | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 					snapshot.setContextTimestamps(contextTimestamps); | 
					
						
							| 
									
										
										
										
											2020-08-20 17:41:20 +08:00
										 |  |  | 					this._contextTimestampsOptimization.storeUnsharedSnapshot( | 
					
						
							|  |  |  | 						snapshot, | 
					
						
							|  |  |  | 						unsharedContextTimestamps | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if (contextHashes.size !== 0) { | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 					snapshot.setContextHashes(contextHashes); | 
					
						
							| 
									
										
										
										
											2020-08-20 17:41:20 +08:00
										 |  |  | 					this._contextHashesOptimization.storeUnsharedSnapshot( | 
					
						
							|  |  |  | 						snapshot, | 
					
						
							|  |  |  | 						unsharedContextHashes | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 				if (contextTshs.size !== 0) { | 
					
						
							|  |  |  | 					snapshot.setContextTshs(contextTshs); | 
					
						
							|  |  |  | 					this._contextTshsOptimization.storeUnsharedSnapshot( | 
					
						
							|  |  |  | 						snapshot, | 
					
						
							|  |  |  | 						unsharedContextTshs | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-08-20 17:41:20 +08:00
										 |  |  | 				if (missingExistence.size !== 0) { | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 					snapshot.setMissingExistence(missingExistence); | 
					
						
							| 
									
										
										
										
											2020-08-20 17:41:20 +08:00
										 |  |  | 					this._missingExistenceOptimization.storeUnsharedSnapshot( | 
					
						
							|  |  |  | 						snapshot, | 
					
						
							|  |  |  | 						unsharedMissingExistence | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if (managedItemInfo.size !== 0) { | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 					snapshot.setManagedItemInfo(managedItemInfo); | 
					
						
							| 
									
										
										
										
											2020-08-20 17:41:20 +08:00
										 |  |  | 					this._managedItemInfoOptimization.storeUnsharedSnapshot( | 
					
						
							|  |  |  | 						snapshot, | 
					
						
							|  |  |  | 						unsharedManagedItemInfo | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 				const unsharedManagedFiles = this._managedFilesOptimization.optimize( | 
					
						
							|  |  |  | 					managedFiles, | 
					
						
							|  |  |  | 					undefined, | 
					
						
							|  |  |  | 					children | 
					
						
							|  |  |  | 				); | 
					
						
							|  |  |  | 				if (managedFiles.size !== 0) { | 
					
						
							|  |  |  | 					snapshot.setManagedFiles(managedFiles); | 
					
						
							|  |  |  | 					this._managedFilesOptimization.storeUnsharedSnapshot( | 
					
						
							|  |  |  | 						snapshot, | 
					
						
							|  |  |  | 						unsharedManagedFiles | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				const unsharedManagedContexts = this._managedContextsOptimization.optimize( | 
					
						
							|  |  |  | 					managedContexts, | 
					
						
							|  |  |  | 					undefined, | 
					
						
							|  |  |  | 					children | 
					
						
							|  |  |  | 				); | 
					
						
							|  |  |  | 				if (managedContexts.size !== 0) { | 
					
						
							|  |  |  | 					snapshot.setManagedContexts(managedContexts); | 
					
						
							|  |  |  | 					this._managedContextsOptimization.storeUnsharedSnapshot( | 
					
						
							|  |  |  | 						snapshot, | 
					
						
							|  |  |  | 						unsharedManagedContexts | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				const unsharedManagedMissing = this._managedMissingOptimization.optimize( | 
					
						
							|  |  |  | 					managedMissing, | 
					
						
							|  |  |  | 					undefined, | 
					
						
							|  |  |  | 					children | 
					
						
							|  |  |  | 				); | 
					
						
							|  |  |  | 				if (managedMissing.size !== 0) { | 
					
						
							|  |  |  | 					snapshot.setManagedMissing(managedMissing); | 
					
						
							|  |  |  | 					this._managedMissingOptimization.storeUnsharedSnapshot( | 
					
						
							|  |  |  | 						snapshot, | 
					
						
							|  |  |  | 						unsharedManagedMissing | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-08-20 17:41:20 +08:00
										 |  |  | 				if (children.size !== 0) { | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 					snapshot.setChildren(children); | 
					
						
							| 
									
										
										
										
											2020-08-20 17:41:20 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-11-08 00:31:18 +08:00
										 |  |  | 				this._snapshotCache.set(snapshot, true); | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 				this._statCreatedSnapshots++; | 
					
						
							| 
									
										
										
										
											2020-08-20 17:41:20 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 				callback(null, snapshot); | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		const jobError = () => { | 
					
						
							|  |  |  | 			if (jobs > 0) { | 
					
						
							|  |  |  | 				// large negative number instead of NaN or something else to keep jobs to stay a SMI (v8)
 | 
					
						
							|  |  |  | 				jobs = -100000000; | 
					
						
							|  |  |  | 				callback(null, null); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 		const checkManaged = (path, managedSet) => { | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | 			for (const immutablePath of this.immutablePathsWithSlash) { | 
					
						
							|  |  |  | 				if (path.startsWith(immutablePath)) { | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 					managedSet.add(path); | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | 					return true; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			for (const managedPath of this.managedPathsWithSlash) { | 
					
						
							|  |  |  | 				if (path.startsWith(managedPath)) { | 
					
						
							|  |  |  | 					const managedItem = getManagedItem(managedPath, path); | 
					
						
							|  |  |  | 					if (managedItem) { | 
					
						
							|  |  |  | 						managedItems.add(managedItem); | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 						managedSet.add(path); | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | 						return true; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return false; | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 		const captureNonManaged = (items, managedSet) => { | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | 			const capturedItems = new Set(); | 
					
						
							|  |  |  | 			for (const path of items) { | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 				if (!checkManaged(path, managedSet)) capturedItems.add(path); | 
					
						
							| 
									
										
										
										
											2020-08-20 17:17:55 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			return capturedItems; | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 		if (files) { | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 			const capturedFiles = captureNonManaged(files, managedFiles); | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 			switch (mode) { | 
					
						
							|  |  |  | 				case 3: | 
					
						
							|  |  |  | 					unsharedFileTshs = this._fileTshsOptimization.optimize( | 
					
						
							|  |  |  | 						capturedFiles, | 
					
						
							|  |  |  | 						undefined, | 
					
						
							|  |  |  | 						children | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 					for (const path of capturedFiles) { | 
					
						
							|  |  |  | 						const cache = this._fileTshs.get(path); | 
					
						
							|  |  |  | 						if (cache !== undefined) { | 
					
						
							|  |  |  | 							fileTshs.set(path, cache); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							jobs++; | 
					
						
							|  |  |  | 							this._getFileTimestampAndHash(path, (err, entry) => { | 
					
						
							|  |  |  | 								if (err) { | 
					
						
							|  |  |  | 									if (this.logger) { | 
					
						
							|  |  |  | 										this.logger.debug( | 
					
						
							|  |  |  | 											`Error snapshotting file timestamp hash combination of ${path}: ${err}` | 
					
						
							|  |  |  | 										); | 
					
						
							|  |  |  | 									} | 
					
						
							|  |  |  | 									jobError(); | 
					
						
							|  |  |  | 								} else { | 
					
						
							|  |  |  | 									fileTshs.set(path, entry); | 
					
						
							|  |  |  | 									jobDone(); | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 								} | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 							}); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				case 2: | 
					
						
							|  |  |  | 					unsharedFileHashes = this._fileHashesOptimization.optimize( | 
					
						
							|  |  |  | 						capturedFiles, | 
					
						
							|  |  |  | 						undefined, | 
					
						
							|  |  |  | 						children | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 					for (const path of capturedFiles) { | 
					
						
							|  |  |  | 						const cache = this._fileHashes.get(path); | 
					
						
							|  |  |  | 						if (cache !== undefined) { | 
					
						
							|  |  |  | 							fileHashes.set(path, cache); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							jobs++; | 
					
						
							|  |  |  | 							this.fileHashQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 								if (err) { | 
					
						
							|  |  |  | 									if (this.logger) { | 
					
						
							|  |  |  | 										this.logger.debug( | 
					
						
							|  |  |  | 											`Error snapshotting file hash of ${path}: ${err}` | 
					
						
							|  |  |  | 										); | 
					
						
							|  |  |  | 									} | 
					
						
							|  |  |  | 									jobError(); | 
					
						
							|  |  |  | 								} else { | 
					
						
							|  |  |  | 									fileHashes.set(path, entry); | 
					
						
							|  |  |  | 									jobDone(); | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 								} | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 							}); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				case 1: | 
					
						
							|  |  |  | 					unsharedFileTimestamps = this._fileTimestampsOptimization.optimize( | 
					
						
							|  |  |  | 						capturedFiles, | 
					
						
							|  |  |  | 						startTime, | 
					
						
							|  |  |  | 						children | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 					for (const path of capturedFiles) { | 
					
						
							|  |  |  | 						const cache = this._fileTimestamps.get(path); | 
					
						
							|  |  |  | 						if (cache !== undefined) { | 
					
						
							|  |  |  | 							if (cache !== "ignore") { | 
					
						
							|  |  |  | 								fileTimestamps.set(path, cache); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 							} | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 						} else { | 
					
						
							|  |  |  | 							jobs++; | 
					
						
							|  |  |  | 							this.fileTimestampQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 								if (err) { | 
					
						
							|  |  |  | 									if (this.logger) { | 
					
						
							|  |  |  | 										this.logger.debug( | 
					
						
							|  |  |  | 											`Error snapshotting file timestamp of ${path}: ${err}` | 
					
						
							|  |  |  | 										); | 
					
						
							|  |  |  | 									} | 
					
						
							|  |  |  | 									jobError(); | 
					
						
							|  |  |  | 								} else { | 
					
						
							|  |  |  | 									fileTimestamps.set(path, entry); | 
					
						
							|  |  |  | 									jobDone(); | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 							}); | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 					break; | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 		if (directories) { | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 			const capturedDirectories = captureNonManaged( | 
					
						
							|  |  |  | 				directories, | 
					
						
							|  |  |  | 				managedContexts | 
					
						
							|  |  |  | 			); | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 			switch (mode) { | 
					
						
							|  |  |  | 				case 3: | 
					
						
							|  |  |  | 					unsharedContextTshs = this._contextTshsOptimization.optimize( | 
					
						
							|  |  |  | 						capturedDirectories, | 
					
						
							|  |  |  | 						undefined, | 
					
						
							|  |  |  | 						children | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 					for (const path of capturedDirectories) { | 
					
						
							|  |  |  | 						const cache = this._contextTshs.get(path); | 
					
						
							|  |  |  | 						if (cache !== undefined) { | 
					
						
							|  |  |  | 							contextTshs.set(path, cache); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							jobs++; | 
					
						
							|  |  |  | 							this._getContextTimestampAndHash(path, (err, entry) => { | 
					
						
							|  |  |  | 								if (err) { | 
					
						
							|  |  |  | 									if (this.logger) { | 
					
						
							|  |  |  | 										this.logger.debug( | 
					
						
							|  |  |  | 											`Error snapshotting context timestamp hash combination of ${path}: ${err}` | 
					
						
							|  |  |  | 										); | 
					
						
							|  |  |  | 									} | 
					
						
							|  |  |  | 									jobError(); | 
					
						
							|  |  |  | 								} else { | 
					
						
							|  |  |  | 									contextTshs.set(path, entry); | 
					
						
							|  |  |  | 									jobDone(); | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 								} | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 							}); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				case 2: | 
					
						
							|  |  |  | 					unsharedContextHashes = this._contextHashesOptimization.optimize( | 
					
						
							|  |  |  | 						capturedDirectories, | 
					
						
							|  |  |  | 						undefined, | 
					
						
							|  |  |  | 						children | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 					for (const path of capturedDirectories) { | 
					
						
							|  |  |  | 						const cache = this._contextHashes.get(path); | 
					
						
							|  |  |  | 						if (cache !== undefined) { | 
					
						
							|  |  |  | 							contextHashes.set(path, cache); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							jobs++; | 
					
						
							|  |  |  | 							this.contextHashQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 								if (err) { | 
					
						
							|  |  |  | 									if (this.logger) { | 
					
						
							|  |  |  | 										this.logger.debug( | 
					
						
							|  |  |  | 											`Error snapshotting context hash of ${path}: ${err}` | 
					
						
							|  |  |  | 										); | 
					
						
							|  |  |  | 									} | 
					
						
							|  |  |  | 									jobError(); | 
					
						
							|  |  |  | 								} else { | 
					
						
							|  |  |  | 									contextHashes.set(path, entry); | 
					
						
							|  |  |  | 									jobDone(); | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 								} | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 							}); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				case 1: | 
					
						
							|  |  |  | 					unsharedContextTimestamps = this._contextTimestampsOptimization.optimize( | 
					
						
							|  |  |  | 						capturedDirectories, | 
					
						
							|  |  |  | 						startTime, | 
					
						
							|  |  |  | 						children | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 					for (const path of capturedDirectories) { | 
					
						
							|  |  |  | 						const cache = this._contextTimestamps.get(path); | 
					
						
							|  |  |  | 						if (cache !== undefined) { | 
					
						
							|  |  |  | 							if (cache !== "ignore") { | 
					
						
							|  |  |  | 								contextTimestamps.set(path, cache); | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 							} | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 						} else { | 
					
						
							|  |  |  | 							jobs++; | 
					
						
							|  |  |  | 							this.contextTimestampQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 								if (err) { | 
					
						
							|  |  |  | 									if (this.logger) { | 
					
						
							|  |  |  | 										this.logger.debug( | 
					
						
							|  |  |  | 											`Error snapshotting context timestamp of ${path}: ${err}` | 
					
						
							|  |  |  | 										); | 
					
						
							|  |  |  | 									} | 
					
						
							|  |  |  | 									jobError(); | 
					
						
							|  |  |  | 								} else { | 
					
						
							|  |  |  | 									contextTimestamps.set(path, entry); | 
					
						
							|  |  |  | 									jobDone(); | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 							}); | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 					break; | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 		if (missing) { | 
					
						
							| 
									
										
										
										
											2020-08-23 03:54:34 +08:00
										 |  |  | 			const capturedMissing = captureNonManaged(missing, managedMissing); | 
					
						
							| 
									
										
										
										
											2020-08-20 17:41:20 +08:00
										 |  |  | 			unsharedMissingExistence = this._missingExistenceOptimization.optimize( | 
					
						
							|  |  |  | 				capturedMissing, | 
					
						
							|  |  |  | 				startTime, | 
					
						
							|  |  |  | 				children | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 			for (const path of capturedMissing) { | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 				const cache = this._fileTimestamps.get(path); | 
					
						
							|  |  |  | 				if (cache !== undefined) { | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 					if (cache !== "ignore") { | 
					
						
							| 
									
										
										
										
											2020-03-13 19:09:14 +08:00
										 |  |  | 						missingExistence.set(path, toExistence(cache)); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 				} else { | 
					
						
							|  |  |  | 					jobs++; | 
					
						
							|  |  |  | 					this.fileTimestampQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 						if (err) { | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 							if (this.logger) { | 
					
						
							|  |  |  | 								this.logger.debug( | 
					
						
							|  |  |  | 									`Error snapshotting missing timestamp of ${path}: ${err}` | 
					
						
							|  |  |  | 								); | 
					
						
							|  |  |  | 							} | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 							jobError(); | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 						} else { | 
					
						
							| 
									
										
										
										
											2020-03-13 19:09:14 +08:00
										 |  |  | 							missingExistence.set(path, toExistence(entry)); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 							jobDone(); | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 						} | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-08-20 17:41:20 +08:00
										 |  |  | 		unsharedManagedItemInfo = this._managedItemInfoOptimization.optimize( | 
					
						
							|  |  |  | 			managedItems, | 
					
						
							|  |  |  | 			undefined, | 
					
						
							|  |  |  | 			children | 
					
						
							|  |  |  | 		); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		for (const path of managedItems) { | 
					
						
							|  |  |  | 			const cache = this._managedItems.get(path); | 
					
						
							|  |  |  | 			if (cache !== undefined) { | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 				managedItemInfo.set(path, cache); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			} else { | 
					
						
							|  |  |  | 				jobs++; | 
					
						
							|  |  |  | 				this.managedItemQueue.add(path, (err, entry) => { | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 					if (err) { | 
					
						
							|  |  |  | 						if (this.logger) { | 
					
						
							|  |  |  | 							this.logger.debug( | 
					
						
							|  |  |  | 								`Error snapshotting managed item ${path}: ${err}` | 
					
						
							|  |  |  | 							); | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 						jobError(); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					} else { | 
					
						
							|  |  |  | 						managedItemInfo.set(path, entry); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 						jobDone(); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		jobDone(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {Snapshot} snapshot1 a snapshot | 
					
						
							|  |  |  | 	 * @param {Snapshot} snapshot2 a snapshot | 
					
						
							|  |  |  | 	 * @returns {Snapshot} merged snapshot | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	mergeSnapshots(snapshot1, snapshot2) { | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		const snapshot = new Snapshot(); | 
					
						
							|  |  |  | 		if (snapshot1.hasStartTime() && snapshot2.hasStartTime()) | 
					
						
							|  |  |  | 			snapshot.setStartTime(Math.min(snapshot1.startTime, snapshot2.startTime)); | 
					
						
							|  |  |  | 		else if (snapshot2.hasStartTime()) snapshot.startTime = snapshot2.startTime; | 
					
						
							|  |  |  | 		else if (snapshot1.hasStartTime()) snapshot.startTime = snapshot1.startTime; | 
					
						
							|  |  |  | 		if (snapshot1.hasFileTimestamps() || snapshot2.hasFileTimestamps()) { | 
					
						
							|  |  |  | 			snapshot.setFileTimestamps( | 
					
						
							|  |  |  | 				mergeMaps(snapshot1.fileTimestamps, snapshot2.fileTimestamps) | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (snapshot1.hasFileHashes() || snapshot2.hasFileHashes()) { | 
					
						
							|  |  |  | 			snapshot.setFileHashes( | 
					
						
							|  |  |  | 				mergeMaps(snapshot1.fileHashes, snapshot2.fileHashes) | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (snapshot1.hasFileTshs() || snapshot2.hasFileTshs()) { | 
					
						
							|  |  |  | 			snapshot.setFileTshs(mergeMaps(snapshot1.fileTshs, snapshot2.fileTshs)); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (snapshot1.hasContextTimestamps() || snapshot2.hasContextTimestamps()) { | 
					
						
							|  |  |  | 			snapshot.setContextTimestamps( | 
					
						
							|  |  |  | 				mergeMaps(snapshot1.contextTimestamps, snapshot2.contextTimestamps) | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (snapshot1.hasContextHashes() || snapshot2.hasContextHashes()) { | 
					
						
							|  |  |  | 			snapshot.setContextHashes( | 
					
						
							|  |  |  | 				mergeMaps(snapshot1.contextHashes, snapshot2.contextHashes) | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		if (snapshot1.hasContextTshs() || snapshot2.hasContextTshs()) { | 
					
						
							|  |  |  | 			snapshot.setContextTshs( | 
					
						
							|  |  |  | 				mergeMaps(snapshot1.contextTshs, snapshot2.contextTshs) | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		if (snapshot1.hasMissingExistence() || snapshot2.hasMissingExistence()) { | 
					
						
							|  |  |  | 			snapshot.setMissingExistence( | 
					
						
							|  |  |  | 				mergeMaps(snapshot1.missingExistence, snapshot2.missingExistence) | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		if (snapshot1.hasManagedItemInfo() || snapshot2.hasManagedItemInfo()) { | 
					
						
							|  |  |  | 			snapshot.setManagedItemInfo( | 
					
						
							|  |  |  | 				mergeMaps(snapshot1.managedItemInfo, snapshot2.managedItemInfo) | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		if (snapshot1.hasManagedFiles() || snapshot2.hasManagedFiles()) { | 
					
						
							|  |  |  | 			snapshot.setManagedFiles( | 
					
						
							|  |  |  | 				mergeSets(snapshot1.managedFiles, snapshot2.managedFiles) | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		if (snapshot1.hasManagedContexts() || snapshot2.hasManagedContexts()) { | 
					
						
							|  |  |  | 			snapshot.setManagedContexts( | 
					
						
							|  |  |  | 				mergeSets(snapshot1.managedContexts, snapshot2.managedContexts) | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		if (snapshot1.hasManagedMissing() || snapshot2.hasManagedMissing()) { | 
					
						
							|  |  |  | 			snapshot.setManagedMissing( | 
					
						
							|  |  |  | 				mergeSets(snapshot1.managedMissing, snapshot2.managedMissing) | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (snapshot1.hasChildren() || snapshot2.hasChildren()) { | 
					
						
							|  |  |  | 			snapshot.setChildren(mergeSets(snapshot1.children, snapshot2.children)); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-11-08 00:31:18 +08:00
										 |  |  | 		if ( | 
					
						
							|  |  |  | 			this._snapshotCache.get(snapshot1) === true && | 
					
						
							|  |  |  | 			this._snapshotCache.get(snapshot2) === true | 
					
						
							|  |  |  | 		) { | 
					
						
							|  |  |  | 			this._snapshotCache.set(snapshot, true); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		return snapshot; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {Snapshot} snapshot the snapshot made | 
					
						
							|  |  |  | 	 * @param {function(WebpackError=, boolean=): void} callback callback function | 
					
						
							|  |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 	checkSnapshotValid(snapshot, callback) { | 
					
						
							| 
									
										
										
										
											2019-10-27 14:38:06 +08:00
										 |  |  | 		const cachedResult = this._snapshotCache.get(snapshot); | 
					
						
							|  |  |  | 		if (cachedResult !== undefined) { | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 			this._statTestedSnapshotsCached++; | 
					
						
							| 
									
										
										
										
											2019-10-27 14:38:06 +08:00
										 |  |  | 			if (typeof cachedResult === "boolean") { | 
					
						
							|  |  |  | 				callback(null, cachedResult); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				cachedResult.push(callback); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 		this._statTestedSnapshotsNotCached++; | 
					
						
							| 
									
										
										
										
											2020-01-31 17:59:28 +08:00
										 |  |  | 		this._checkSnapshotValidNoCache(snapshot, callback); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {Snapshot} snapshot the snapshot made | 
					
						
							|  |  |  | 	 * @param {function(WebpackError=, boolean=): void} callback callback function | 
					
						
							|  |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2020-01-31 17:59:28 +08:00
										 |  |  | 	_checkSnapshotValidNoCache(snapshot, callback) { | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 		/** @type {number | undefined} */ | 
					
						
							|  |  |  | 		let startTime = undefined; | 
					
						
							|  |  |  | 		if (snapshot.hasStartTime()) { | 
					
						
							|  |  |  | 			startTime = snapshot.startTime; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		let jobs = 1; | 
					
						
							|  |  |  | 		const jobDone = () => { | 
					
						
							|  |  |  | 			if (--jobs === 0) { | 
					
						
							| 
									
										
										
										
											2019-10-27 14:38:06 +08:00
										 |  |  | 				this._snapshotCache.set(snapshot, true); | 
					
						
							| 
									
										
										
										
											2019-11-01 19:16:30 +08:00
										 |  |  | 				callback(null, true); | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 		const invalid = () => { | 
					
						
							|  |  |  | 			if (jobs > 0) { | 
					
						
							| 
									
										
										
										
											2019-11-01 19:16:30 +08:00
										 |  |  | 				// large negative number instead of NaN or something else to keep jobs to stay a SMI (v8)
 | 
					
						
							|  |  |  | 				jobs = -100000000; | 
					
						
							| 
									
										
										
										
											2019-10-27 14:38:06 +08:00
										 |  |  | 				this._snapshotCache.set(snapshot, false); | 
					
						
							| 
									
										
										
										
											2019-11-01 19:16:30 +08:00
										 |  |  | 				callback(null, false); | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 		const invalidWithError = (path, err) => { | 
					
						
							|  |  |  | 			if (this._remainingLogs > 0) { | 
					
						
							| 
									
										
										
										
											2020-03-10 09:59:46 +08:00
										 |  |  | 				this._log(path, `error occurred: %s`, err); | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			invalid(); | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 		/** | 
					
						
							|  |  |  | 		 * @param {string} path file path | 
					
						
							|  |  |  | 		 * @param {string} current current hash | 
					
						
							|  |  |  | 		 * @param {string} snap snapshot hash | 
					
						
							|  |  |  | 		 * @returns {boolean} true, if ok | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		const checkHash = (path, current, snap) => { | 
					
						
							|  |  |  | 			if (current !== snap) { | 
					
						
							|  |  |  | 				// If hash differ it's invalid
 | 
					
						
							|  |  |  | 				if (this._remainingLogs > 0) { | 
					
						
							| 
									
										
										
										
											2019-11-15 00:28:10 +08:00
										 |  |  | 					this._log(path, `hashes differ (%s != %s)`, current, snap); | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				return false; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return true; | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 		/** | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 		 * @param {string} path file path | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		 * @param {boolean} current current entry | 
					
						
							|  |  |  | 		 * @param {boolean} snap entry from snapshot | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 		 * @returns {boolean} true, if ok | 
					
						
							|  |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2020-03-13 19:09:14 +08:00
										 |  |  | 		const checkExistence = (path, current, snap) => { | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 			if (!current !== !snap) { | 
					
						
							| 
									
										
										
										
											2020-03-10 09:59:46 +08:00
										 |  |  | 				// If existence of item differs
 | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 				// it's invalid
 | 
					
						
							|  |  |  | 				if (this._remainingLogs > 0) { | 
					
						
							|  |  |  | 					this._log( | 
					
						
							|  |  |  | 						path, | 
					
						
							|  |  |  | 						current ? "it didn't exist before" : "it does no longer exist" | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return false; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return true; | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 		/** | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 		 * @param {string} path file path | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		 * @param {FileSystemInfoEntry} current current entry | 
					
						
							|  |  |  | 		 * @param {FileSystemInfoEntry} snap entry from snapshot | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		 * @param {boolean} log log reason | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 		 * @returns {boolean} true, if ok | 
					
						
							|  |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		const checkFile = (path, current, snap, log = true) => { | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 			if (current === snap) return true; | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 			if (!current !== !snap) { | 
					
						
							| 
									
										
										
										
											2020-03-10 09:59:46 +08:00
										 |  |  | 				// If existence of item differs
 | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 				// it's invalid
 | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 				if (log && this._remainingLogs > 0) { | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 					this._log( | 
					
						
							|  |  |  | 						path, | 
					
						
							|  |  |  | 						current ? "it didn't exist before" : "it does no longer exist" | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 				return false; | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 			if (current) { | 
					
						
							|  |  |  | 				// For existing items only
 | 
					
						
							| 
									
										
										
										
											2020-05-31 03:09:53 +08:00
										 |  |  | 				if (typeof startTime === "number" && current.safeTime > startTime) { | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 					// If a change happened after starting reading the item
 | 
					
						
							|  |  |  | 					// this may no longer be valid
 | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 					if (log && this._remainingLogs > 0) { | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 						this._log( | 
					
						
							|  |  |  | 							path, | 
					
						
							|  |  |  | 							`it may have changed (%d) after the start time of the snapshot (%d)`, | 
					
						
							|  |  |  | 							current.safeTime, | 
					
						
							|  |  |  | 							startTime | 
					
						
							|  |  |  | 						); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					return false; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 				if ( | 
					
						
							|  |  |  | 					snap.timestamp !== undefined && | 
					
						
							|  |  |  | 					current.timestamp !== snap.timestamp | 
					
						
							|  |  |  | 				) { | 
					
						
							|  |  |  | 					// If we have a timestamp (it was a file or symlink) and it differs from current timestamp
 | 
					
						
							|  |  |  | 					// it's invalid
 | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 					if (log && this._remainingLogs > 0) { | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 						this._log( | 
					
						
							|  |  |  | 							path, | 
					
						
							| 
									
										
										
										
											2019-11-15 00:28:10 +08:00
										 |  |  | 							`timestamps differ (%d != %d)`, | 
					
						
							|  |  |  | 							current.timestamp, | 
					
						
							|  |  |  | 							snap.timestamp | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 						); | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 					return false; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 				if ( | 
					
						
							|  |  |  | 					snap.timestampHash !== undefined && | 
					
						
							|  |  |  | 					current.timestampHash !== snap.timestampHash | 
					
						
							|  |  |  | 				) { | 
					
						
							|  |  |  | 					// If we have a timestampHash (it was a directory) and it differs from current timestampHash
 | 
					
						
							|  |  |  | 					// it's invalid
 | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 					if (log && this._remainingLogs > 0) { | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 						this._log( | 
					
						
							|  |  |  | 							path, | 
					
						
							| 
									
										
										
										
											2019-11-15 00:28:10 +08:00
										 |  |  | 							`timestamps hashes differ (%s != %s)`, | 
					
						
							|  |  |  | 							current.timestampHash, | 
					
						
							|  |  |  | 							snap.timestampHash | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 						); | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 					return false; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			return true; | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 		if (snapshot.hasChildren()) { | 
					
						
							| 
									
										
										
										
											2020-01-31 17:59:28 +08:00
										 |  |  | 			const childCallback = (err, result) => { | 
					
						
							|  |  |  | 				if (err || !result) return invalid(); | 
					
						
							|  |  |  | 				else jobDone(); | 
					
						
							|  |  |  | 			}; | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 			for (const child of snapshot.children) { | 
					
						
							| 
									
										
										
										
											2020-01-31 17:59:28 +08:00
										 |  |  | 				const cache = this._snapshotCache.get(child); | 
					
						
							|  |  |  | 				if (cache !== undefined) { | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 					this._statTestedChildrenCached++; | 
					
						
							|  |  |  | 					/* istanbul ignore else */ | 
					
						
							|  |  |  | 					if (typeof cache === "boolean") { | 
					
						
							|  |  |  | 						if (cache === false) { | 
					
						
							|  |  |  | 							invalid(); | 
					
						
							|  |  |  | 							return; | 
					
						
							| 
									
										
										
										
											2020-01-31 17:59:28 +08:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 					} else { | 
					
						
							|  |  |  | 						jobs++; | 
					
						
							|  |  |  | 						cache.push(childCallback); | 
					
						
							| 
									
										
										
										
											2020-01-31 17:59:28 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				} else { | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 					this._statTestedChildrenNotCached++; | 
					
						
							| 
									
										
										
										
											2020-01-31 17:59:28 +08:00
										 |  |  | 					jobs++; | 
					
						
							|  |  |  | 					this._checkSnapshotValidNoCache(child, childCallback); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 		if (snapshot.hasFileTimestamps()) { | 
					
						
							|  |  |  | 			const { fileTimestamps } = snapshot; | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 			this._statTestedEntries += fileTimestamps.size; | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			for (const [path, ts] of fileTimestamps) { | 
					
						
							|  |  |  | 				const cache = this._fileTimestamps.get(path); | 
					
						
							|  |  |  | 				if (cache !== undefined) { | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 					if (cache !== "ignore" && !checkFile(path, cache, ts)) { | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 						invalid(); | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 						return; | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					jobs++; | 
					
						
							|  |  |  | 					this.fileTimestampQueue.add(path, (err, entry) => { | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 						if (err) return invalidWithError(path, err); | 
					
						
							|  |  |  | 						if (!checkFile(path, entry, ts)) { | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 							invalid(); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					}); | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		const processFileHashSnapshot = (path, hash) => { | 
					
						
							|  |  |  | 			const cache = this._fileHashes.get(path); | 
					
						
							|  |  |  | 			if (cache !== undefined) { | 
					
						
							|  |  |  | 				if (cache !== "ignore" && !checkHash(path, cache, hash)) { | 
					
						
							|  |  |  | 					invalid(); | 
					
						
							|  |  |  | 					return; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				jobs++; | 
					
						
							|  |  |  | 				this.fileHashQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 					if (err) return invalidWithError(path, err); | 
					
						
							|  |  |  | 					if (!checkHash(path, entry, hash)) { | 
					
						
							|  |  |  | 						invalid(); | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						jobDone(); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 		if (snapshot.hasFileHashes()) { | 
					
						
							|  |  |  | 			const { fileHashes } = snapshot; | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 			this._statTestedEntries += fileHashes.size; | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			for (const [path, hash] of fileHashes) { | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 				processFileHashSnapshot(path, hash); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (snapshot.hasFileTshs()) { | 
					
						
							|  |  |  | 			const { fileTshs } = snapshot; | 
					
						
							|  |  |  | 			this._statTestedEntries += fileTshs.size; | 
					
						
							|  |  |  | 			for (const [path, tsh] of fileTshs) { | 
					
						
							|  |  |  | 				if (typeof tsh === "string") { | 
					
						
							|  |  |  | 					processFileHashSnapshot(path, tsh); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 				} else { | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 					const cache = this._fileTimestamps.get(path); | 
					
						
							|  |  |  | 					if (cache !== undefined) { | 
					
						
							|  |  |  | 						if (cache === "ignore" || !checkFile(path, cache, tsh, false)) { | 
					
						
							|  |  |  | 							processFileHashSnapshot(path, tsh.hash); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 					} else { | 
					
						
							|  |  |  | 						jobs++; | 
					
						
							|  |  |  | 						this.fileTimestampQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 							if (err) return invalidWithError(path, err); | 
					
						
							|  |  |  | 							if (!checkFile(path, entry, tsh, false)) { | 
					
						
							|  |  |  | 								processFileHashSnapshot(path, tsh.hash); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 		if (snapshot.hasContextTimestamps()) { | 
					
						
							|  |  |  | 			const { contextTimestamps } = snapshot; | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 			this._statTestedEntries += contextTimestamps.size; | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 			for (const [path, ts] of contextTimestamps) { | 
					
						
							|  |  |  | 				const cache = this._contextTimestamps.get(path); | 
					
						
							|  |  |  | 				if (cache !== undefined) { | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 					if (cache !== "ignore" && !checkFile(path, cache, ts)) { | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 						invalid(); | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 						return; | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					jobs++; | 
					
						
							|  |  |  | 					this.contextTimestampQueue.add(path, (err, entry) => { | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 						if (err) return invalidWithError(path, err); | 
					
						
							|  |  |  | 						if (!checkFile(path, entry, ts)) { | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 							invalid(); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 		const processContextHashSnapshot = (path, hash) => { | 
					
						
							|  |  |  | 			const cache = this._contextHashes.get(path); | 
					
						
							|  |  |  | 			if (cache !== undefined) { | 
					
						
							|  |  |  | 				if (cache !== "ignore" && !checkHash(path, cache, hash)) { | 
					
						
							|  |  |  | 					invalid(); | 
					
						
							|  |  |  | 					return; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				jobs++; | 
					
						
							|  |  |  | 				this.contextHashQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 					if (err) return invalidWithError(path, err); | 
					
						
							|  |  |  | 					if (!checkHash(path, entry, hash)) { | 
					
						
							|  |  |  | 						invalid(); | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						jobDone(); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 		if (snapshot.hasContextHashes()) { | 
					
						
							|  |  |  | 			const { contextHashes } = snapshot; | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 			this._statTestedEntries += contextHashes.size; | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			for (const [path, hash] of contextHashes) { | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 				processContextHashSnapshot(path, hash); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (snapshot.hasContextTshs()) { | 
					
						
							|  |  |  | 			const { contextTshs } = snapshot; | 
					
						
							|  |  |  | 			this._statTestedEntries += contextTshs.size; | 
					
						
							|  |  |  | 			for (const [path, tsh] of contextTshs) { | 
					
						
							|  |  |  | 				if (typeof tsh === "string") { | 
					
						
							|  |  |  | 					processContextHashSnapshot(path, tsh); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 				} else { | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 					const cache = this._contextTimestamps.get(path); | 
					
						
							|  |  |  | 					if (cache !== undefined) { | 
					
						
							|  |  |  | 						if (cache === "ignore" || !checkFile(path, cache, tsh, false)) { | 
					
						
							|  |  |  | 							processContextHashSnapshot(path, tsh.hash); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 					} else { | 
					
						
							|  |  |  | 						jobs++; | 
					
						
							|  |  |  | 						this.contextTimestampQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 							if (err) return invalidWithError(path, err); | 
					
						
							|  |  |  | 							if (!checkFile(path, entry, tsh, false)) { | 
					
						
							|  |  |  | 								processContextHashSnapshot(path, tsh.hash); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 		if (snapshot.hasMissingExistence()) { | 
					
						
							|  |  |  | 			const { missingExistence } = snapshot; | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 			this._statTestedEntries += missingExistence.size; | 
					
						
							| 
									
										
										
										
											2020-03-13 19:09:14 +08:00
										 |  |  | 			for (const [path, existence] of missingExistence) { | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 				const cache = this._fileTimestamps.get(path); | 
					
						
							|  |  |  | 				if (cache !== undefined) { | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 					if ( | 
					
						
							|  |  |  | 						cache !== "ignore" && | 
					
						
							| 
									
										
										
										
											2020-03-13 19:09:14 +08:00
										 |  |  | 						!checkExistence(path, toExistence(cache), existence) | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 					) { | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 						invalid(); | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 						return; | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 				} else { | 
					
						
							|  |  |  | 					jobs++; | 
					
						
							|  |  |  | 					this.fileTimestampQueue.add(path, (err, entry) => { | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 						if (err) return invalidWithError(path, err); | 
					
						
							| 
									
										
										
										
											2020-03-13 19:09:14 +08:00
										 |  |  | 						if (!checkExistence(path, toExistence(entry), existence)) { | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 							invalid(); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 		if (snapshot.hasManagedItemInfo()) { | 
					
						
							|  |  |  | 			const { managedItemInfo } = snapshot; | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 			this._statTestedEntries += managedItemInfo.size; | 
					
						
							| 
									
										
										
										
											2019-08-13 23:26:54 +08:00
										 |  |  | 			for (const [path, info] of managedItemInfo) { | 
					
						
							|  |  |  | 				const cache = this._managedItems.get(path); | 
					
						
							|  |  |  | 				if (cache !== undefined) { | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 					if (!checkHash(path, cache, info)) { | 
					
						
							| 
									
										
										
										
											2019-08-13 23:26:54 +08:00
										 |  |  | 						invalid(); | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 						return; | 
					
						
							| 
									
										
										
										
											2019-08-13 23:26:54 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					jobs++; | 
					
						
							|  |  |  | 					this.managedItemQueue.add(path, (err, entry) => { | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 						if (err) return invalidWithError(path, err); | 
					
						
							|  |  |  | 						if (!checkHash(path, entry, info)) { | 
					
						
							| 
									
										
										
										
											2019-08-13 23:26:54 +08:00
										 |  |  | 							invalid(); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		jobDone(); | 
					
						
							| 
									
										
										
										
											2019-11-01 19:16:30 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// if there was an async action
 | 
					
						
							|  |  |  | 		// try to join multiple concurrent request for this snapshot
 | 
					
						
							|  |  |  | 		if (jobs > 0) { | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | 			const callbacks = [callback]; | 
					
						
							| 
									
										
										
										
											2019-11-01 19:16:30 +08:00
										 |  |  | 			callback = (err, result) => { | 
					
						
							|  |  |  | 				for (const callback of callbacks) callback(err, result); | 
					
						
							|  |  |  | 			}; | 
					
						
							|  |  |  | 			this._snapshotCache.set(snapshot, callbacks); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	_readFileTimestamp(path, callback) { | 
					
						
							|  |  |  | 		this.fs.stat(path, (err, stat) => { | 
					
						
							|  |  |  | 			if (err) { | 
					
						
							|  |  |  | 				if (err.code === "ENOENT") { | 
					
						
							|  |  |  | 					this._fileTimestamps.set(path, null); | 
					
						
							| 
									
										
										
										
											2020-09-15 20:39:09 +08:00
										 |  |  | 					this._cachedDeprecatedFileTimestamps = undefined; | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 					return callback(null, null); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return callback(err); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-14 23:49:14 +08:00
										 |  |  | 			let ts; | 
					
						
							|  |  |  | 			if (stat.isDirectory()) { | 
					
						
							|  |  |  | 				ts = { | 
					
						
							|  |  |  | 					safeTime: 0, | 
					
						
							|  |  |  | 					timestamp: undefined | 
					
						
							|  |  |  | 				}; | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				const mtime = +stat.mtime; | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-14 23:49:14 +08:00
										 |  |  | 				if (mtime) applyMtime(mtime); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-14 23:49:14 +08:00
										 |  |  | 				ts = { | 
					
						
							|  |  |  | 					safeTime: mtime ? mtime + FS_ACCURACY : Infinity, | 
					
						
							|  |  |  | 					timestamp: mtime | 
					
						
							|  |  |  | 				}; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			this._fileTimestamps.set(path, ts); | 
					
						
							| 
									
										
										
										
											2020-09-15 20:39:09 +08:00
										 |  |  | 			this._cachedDeprecatedFileTimestamps = undefined; | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			callback(null, ts); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 	_readFileHash(path, callback) { | 
					
						
							|  |  |  | 		this.fs.readFile(path, (err, content) => { | 
					
						
							|  |  |  | 			if (err) { | 
					
						
							| 
									
										
										
										
											2020-08-21 03:57:44 +08:00
										 |  |  | 				if (err.code === "EISDIR") { | 
					
						
							|  |  |  | 					this._fileHashes.set(path, "directory"); | 
					
						
							|  |  |  | 					return callback(null, "directory"); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 				if (err.code === "ENOENT") { | 
					
						
							|  |  |  | 					this._fileHashes.set(path, null); | 
					
						
							|  |  |  | 					return callback(null, null); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return callback(err); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			const hash = createHash("md4"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			hash.update(content); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			const digest = /** @type {string} */ (hash.digest("hex")); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			this._fileHashes.set(path, digest); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			callback(null, digest); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 	_getFileTimestampAndHash(path, callback) { | 
					
						
							|  |  |  | 		const continueWithHash = hash => { | 
					
						
							|  |  |  | 			const cache = this._fileTimestamps.get(path); | 
					
						
							|  |  |  | 			if (cache !== undefined) { | 
					
						
							|  |  |  | 				if (cache !== "ignore") { | 
					
						
							|  |  |  | 					const result = { | 
					
						
							|  |  |  | 						...cache, | 
					
						
							|  |  |  | 						hash | 
					
						
							|  |  |  | 					}; | 
					
						
							|  |  |  | 					this._fileTshs.set(path, result); | 
					
						
							|  |  |  | 					return callback(null, result); | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					this._fileTshs.set(path, hash); | 
					
						
							|  |  |  | 					return callback(null, hash); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				this.fileTimestampQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 					if (err) { | 
					
						
							|  |  |  | 						return callback(err); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					const result = { | 
					
						
							|  |  |  | 						...entry, | 
					
						
							|  |  |  | 						hash | 
					
						
							|  |  |  | 					}; | 
					
						
							|  |  |  | 					this._fileTshs.set(path, result); | 
					
						
							|  |  |  | 					return callback(null, result); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		const cache = this._fileHashes.get(path); | 
					
						
							|  |  |  | 		if (cache !== undefined) { | 
					
						
							|  |  |  | 			continueWithHash(cache); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			this.fileHashQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 				if (err) { | 
					
						
							|  |  |  | 					return callback(err); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				continueWithHash(entry); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	_readContextTimestamp(path, callback) { | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 		this.fs.readdir(path, (err, files) => { | 
					
						
							|  |  |  | 			if (err) { | 
					
						
							|  |  |  | 				if (err.code === "ENOENT") { | 
					
						
							|  |  |  | 					this._contextTimestamps.set(path, null); | 
					
						
							| 
									
										
										
										
											2020-09-15 20:39:09 +08:00
										 |  |  | 					this._cachedDeprecatedContextTimestamps = undefined; | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 					return callback(null, null); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return callback(err); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			files = files | 
					
						
							|  |  |  | 				.map(file => file.normalize("NFC")) | 
					
						
							|  |  |  | 				.filter(file => !/^\./.test(file)) | 
					
						
							|  |  |  | 				.sort(); | 
					
						
							|  |  |  | 			asyncLib.map( | 
					
						
							|  |  |  | 				files, | 
					
						
							|  |  |  | 				(file, callback) => { | 
					
						
							|  |  |  | 					const child = join(this.fs, path, file); | 
					
						
							|  |  |  | 					this.fs.stat(child, (err, stat) => { | 
					
						
							|  |  |  | 						if (err) return callback(err); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 						for (const immutablePath of this.immutablePathsWithSlash) { | 
					
						
							|  |  |  | 							if (path.startsWith(immutablePath)) { | 
					
						
							|  |  |  | 								// ignore any immutable path for timestamping
 | 
					
						
							|  |  |  | 								return callback(null, null); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						for (const managedPath of this.managedPathsWithSlash) { | 
					
						
							|  |  |  | 							if (path.startsWith(managedPath)) { | 
					
						
							|  |  |  | 								const managedItem = getManagedItem(managedPath, child); | 
					
						
							|  |  |  | 								if (managedItem) { | 
					
						
							|  |  |  | 									// construct timestampHash from managed info
 | 
					
						
							| 
									
										
										
										
											2019-11-06 07:27:47 +08:00
										 |  |  | 									return this.managedItemQueue.add(managedItem, (err, info) => { | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 										if (err) return callback(err); | 
					
						
							|  |  |  | 										return callback(null, { | 
					
						
							|  |  |  | 											safeTime: 0, | 
					
						
							|  |  |  | 											timestampHash: info | 
					
						
							|  |  |  | 										}); | 
					
						
							|  |  |  | 									}); | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 						if (stat.isFile()) { | 
					
						
							|  |  |  | 							return this.getFileTimestamp(child, callback); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						if (stat.isDirectory()) { | 
					
						
							|  |  |  | 							this.contextTimestampQueue.increaseParallelism(); | 
					
						
							|  |  |  | 							this.getContextTimestamp(child, (err, tsEntry) => { | 
					
						
							|  |  |  | 								this.contextTimestampQueue.decreaseParallelism(); | 
					
						
							|  |  |  | 								callback(err, tsEntry); | 
					
						
							|  |  |  | 							}); | 
					
						
							|  |  |  | 							return; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						callback(null, null); | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				(err, tsEntries) => { | 
					
						
							| 
									
										
										
										
											2019-09-08 21:53:12 +08:00
										 |  |  | 					if (err) return callback(err); | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 					const hash = createHash("md4"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					for (const file of files) hash.update(file); | 
					
						
							|  |  |  | 					let safeTime = 0; | 
					
						
							|  |  |  | 					for (const entry of tsEntries) { | 
					
						
							|  |  |  | 						if (!entry) { | 
					
						
							|  |  |  | 							hash.update("n"); | 
					
						
							|  |  |  | 							continue; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						if (entry.timestamp) { | 
					
						
							|  |  |  | 							hash.update("f"); | 
					
						
							|  |  |  | 							hash.update(`${entry.timestamp}`); | 
					
						
							|  |  |  | 						} else if (entry.timestampHash) { | 
					
						
							|  |  |  | 							hash.update("d"); | 
					
						
							|  |  |  | 							hash.update(`${entry.timestampHash}`); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						if (entry.safeTime) { | 
					
						
							|  |  |  | 							safeTime = Math.max(safeTime, entry.safeTime); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					const digest = /** @type {string} */ (hash.digest("hex")); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					const result = { | 
					
						
							|  |  |  | 						safeTime, | 
					
						
							|  |  |  | 						timestampHash: digest | 
					
						
							|  |  |  | 					}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					this._contextTimestamps.set(path, result); | 
					
						
							| 
									
										
										
										
											2020-09-15 20:39:09 +08:00
										 |  |  | 					this._cachedDeprecatedContextTimestamps = undefined; | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 					callback(null, result); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 	_readContextHash(path, callback) { | 
					
						
							|  |  |  | 		this.fs.readdir(path, (err, files) => { | 
					
						
							|  |  |  | 			if (err) { | 
					
						
							|  |  |  | 				if (err.code === "ENOENT") { | 
					
						
							|  |  |  | 					this._contextHashes.set(path, null); | 
					
						
							|  |  |  | 					return callback(null, null); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return callback(err); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			files = files | 
					
						
							|  |  |  | 				.map(file => file.normalize("NFC")) | 
					
						
							|  |  |  | 				.filter(file => !/^\./.test(file)) | 
					
						
							|  |  |  | 				.sort(); | 
					
						
							|  |  |  | 			asyncLib.map( | 
					
						
							|  |  |  | 				files, | 
					
						
							|  |  |  | 				(file, callback) => { | 
					
						
							|  |  |  | 					const child = join(this.fs, path, file); | 
					
						
							|  |  |  | 					this.fs.stat(child, (err, stat) => { | 
					
						
							|  |  |  | 						if (err) return callback(err); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 						for (const immutablePath of this.immutablePathsWithSlash) { | 
					
						
							|  |  |  | 							if (path.startsWith(immutablePath)) { | 
					
						
							|  |  |  | 								// ignore any immutable path for hashing
 | 
					
						
							|  |  |  | 								return callback(null, ""); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						for (const managedPath of this.managedPathsWithSlash) { | 
					
						
							|  |  |  | 							if (path.startsWith(managedPath)) { | 
					
						
							|  |  |  | 								const managedItem = getManagedItem(managedPath, child); | 
					
						
							|  |  |  | 								if (managedItem) { | 
					
						
							|  |  |  | 									// construct hash from managed info
 | 
					
						
							| 
									
										
										
										
											2019-11-06 07:27:47 +08:00
										 |  |  | 									return this.managedItemQueue.add(managedItem, (err, info) => { | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 										if (err) return callback(err); | 
					
						
							|  |  |  | 										callback(null, info || ""); | 
					
						
							|  |  |  | 									}); | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 						if (stat.isFile()) { | 
					
						
							| 
									
										
										
										
											2019-09-08 21:35:53 +08:00
										 |  |  | 							return this.getFileHash(child, (err, hash) => { | 
					
						
							|  |  |  | 								callback(err, hash || ""); | 
					
						
							|  |  |  | 							}); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 						} | 
					
						
							|  |  |  | 						if (stat.isDirectory()) { | 
					
						
							|  |  |  | 							this.contextHashQueue.increaseParallelism(); | 
					
						
							|  |  |  | 							this.getContextHash(child, (err, hash) => { | 
					
						
							|  |  |  | 								this.contextHashQueue.decreaseParallelism(); | 
					
						
							|  |  |  | 								callback(err, hash || ""); | 
					
						
							|  |  |  | 							}); | 
					
						
							|  |  |  | 							return; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						callback(null, ""); | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				(err, fileHashes) => { | 
					
						
							| 
									
										
										
										
											2019-09-08 21:53:12 +08:00
										 |  |  | 					if (err) return callback(err); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					const hash = createHash("md4"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					for (const file of files) hash.update(file); | 
					
						
							|  |  |  | 					for (const h of fileHashes) hash.update(h); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					const digest = /** @type {string} */ (hash.digest("hex")); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					this._contextHashes.set(path, digest); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					callback(null, digest); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-26 06:36:16 +08:00
										 |  |  | 	_getContextTimestampAndHash(path, callback) { | 
					
						
							|  |  |  | 		const continueWithHash = hash => { | 
					
						
							|  |  |  | 			const cache = this._contextTimestamps.get(path); | 
					
						
							|  |  |  | 			if (cache !== undefined) { | 
					
						
							|  |  |  | 				if (cache !== "ignore") { | 
					
						
							|  |  |  | 					const result = { | 
					
						
							|  |  |  | 						...cache, | 
					
						
							|  |  |  | 						hash | 
					
						
							|  |  |  | 					}; | 
					
						
							|  |  |  | 					this._contextTshs.set(path, result); | 
					
						
							|  |  |  | 					return callback(null, result); | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					this._contextTshs.set(path, hash); | 
					
						
							|  |  |  | 					return callback(null, hash); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				this.contextTimestampQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 					if (err) { | 
					
						
							|  |  |  | 						return callback(err); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					const result = { | 
					
						
							|  |  |  | 						...entry, | 
					
						
							|  |  |  | 						hash | 
					
						
							|  |  |  | 					}; | 
					
						
							|  |  |  | 					this._contextTshs.set(path, result); | 
					
						
							|  |  |  | 					return callback(null, result); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		const cache = this._contextHashes.get(path); | 
					
						
							|  |  |  | 		if (cache !== undefined) { | 
					
						
							|  |  |  | 			continueWithHash(cache); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			this.contextHashQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 				if (err) { | 
					
						
							|  |  |  | 					return callback(err); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				continueWithHash(entry); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 	_getManagedItemDirectoryInfo(path, callback) { | 
					
						
							|  |  |  | 		this.fs.readdir(path, (err, elements) => { | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 			if (err) { | 
					
						
							| 
									
										
										
										
											2019-09-09 03:30:27 +08:00
										 |  |  | 				if (err.code === "ENOENT" || err.code === "ENOTDIR") { | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 					return callback(null, EMPTY_SET); | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				return callback(err); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 			const set = new Set( | 
					
						
							|  |  |  | 				elements.map(element => join(this.fs, path, element)) | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 			callback(null, set); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	_getManagedItemInfo(path, callback) { | 
					
						
							|  |  |  | 		const dir = dirname(this.fs, path); | 
					
						
							|  |  |  | 		this.managedItemDirectoryQueue.add(dir, (err, elements) => { | 
					
						
							|  |  |  | 			if (err) { | 
					
						
							|  |  |  | 				return callback(err); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if (!elements.has(path)) { | 
					
						
							|  |  |  | 				// file or directory doesn't exist
 | 
					
						
							|  |  |  | 				this._managedItems.set(path, "missing"); | 
					
						
							|  |  |  | 				return callback(null, "missing"); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// something exists
 | 
					
						
							|  |  |  | 			// it may be a file or directory
 | 
					
						
							|  |  |  | 			if ( | 
					
						
							|  |  |  | 				path.endsWith("node_modules") && | 
					
						
							|  |  |  | 				(path.endsWith("/node_modules") || path.endsWith("\\node_modules")) | 
					
						
							|  |  |  | 			) { | 
					
						
							| 
									
										
										
										
											2020-03-10 09:59:46 +08:00
										 |  |  | 				// we are only interested in existence of this special directory
 | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 				this._managedItems.set(path, "exists"); | 
					
						
							|  |  |  | 				return callback(null, "exists"); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// we assume it's a directory, as files shouldn't occur in managed paths
 | 
					
						
							|  |  |  | 			const packageJsonPath = join(this.fs, path, "package.json"); | 
					
						
							|  |  |  | 			this.fs.readFile(packageJsonPath, (err, content) => { | 
					
						
							|  |  |  | 				if (err) { | 
					
						
							|  |  |  | 					if (err.code === "ENOENT" || err.code === "ENOTDIR") { | 
					
						
							|  |  |  | 						// no package.json or path is not a directory
 | 
					
						
							| 
									
										
										
										
											2020-09-17 21:08:38 +08:00
										 |  |  | 						this.fs.readdir(path, (err, elements) => { | 
					
						
							|  |  |  | 							if ( | 
					
						
							|  |  |  | 								!err && | 
					
						
							|  |  |  | 								elements.length === 1 && | 
					
						
							|  |  |  | 								elements[0] === "node_modules" | 
					
						
							|  |  |  | 							) { | 
					
						
							|  |  |  | 								// This is only a grouping folder e. g. used by yarn
 | 
					
						
							|  |  |  | 								// we are only interested in existence of this special directory
 | 
					
						
							|  |  |  | 								this._managedItems.set(path, "nested"); | 
					
						
							|  |  |  | 								return callback(null, "nested"); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 							const problem = `Managed item ${path} isn't a directory or doesn't contain a package.json`; | 
					
						
							|  |  |  | 							this.logger.warn(problem); | 
					
						
							|  |  |  | 							return callback(new Error(problem)); | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 						return; | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 					return callback(err); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				let data; | 
					
						
							|  |  |  | 				try { | 
					
						
							|  |  |  | 					data = JSON.parse(content.toString("utf-8")); | 
					
						
							|  |  |  | 				} catch (e) { | 
					
						
							|  |  |  | 					return callback(e); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				const info = `${data.name || ""}@${data.version || ""}`; | 
					
						
							|  |  |  | 				this._managedItems.set(path, info); | 
					
						
							|  |  |  | 				callback(null, info); | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	getDeprecatedFileTimestamps() { | 
					
						
							| 
									
										
										
										
											2020-09-15 20:39:09 +08:00
										 |  |  | 		if (this._cachedDeprecatedFileTimestamps !== undefined) | 
					
						
							|  |  |  | 			return this._cachedDeprecatedFileTimestamps; | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		const map = new Map(); | 
					
						
							|  |  |  | 		for (const [path, info] of this._fileTimestamps) { | 
					
						
							| 
									
										
										
										
											2019-11-06 06:46:34 +08:00
										 |  |  | 			if (info) map.set(path, typeof info === "object" ? info.safeTime : null); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-09-15 20:39:09 +08:00
										 |  |  | 		return (this._cachedDeprecatedFileTimestamps = map); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	getDeprecatedContextTimestamps() { | 
					
						
							| 
									
										
										
										
											2020-09-15 20:39:09 +08:00
										 |  |  | 		if (this._cachedDeprecatedContextTimestamps !== undefined) | 
					
						
							|  |  |  | 			return this._cachedDeprecatedContextTimestamps; | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		const map = new Map(); | 
					
						
							|  |  |  | 		for (const [path, info] of this._contextTimestamps) { | 
					
						
							| 
									
										
										
										
											2019-11-06 06:46:34 +08:00
										 |  |  | 			if (info) map.set(path, typeof info === "object" ? info.safeTime : null); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-09-15 20:39:09 +08:00
										 |  |  | 		return (this._cachedDeprecatedContextTimestamps = map); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports = FileSystemInfo; | 
					
						
							| 
									
										
										
										
											2020-08-22 18:01:28 +08:00
										 |  |  | module.exports.Snapshot = Snapshot; |