| 
									
										
										
										
											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-08-13 04:59:09 +08:00
										 |  |  | const resolve = require("enhanced-resolve"); | 
					
						
							|  |  |  | 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"); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | /** @typedef {import("./WebpackError")} WebpackError */ | 
					
						
							|  |  |  | /** @typedef {import("./util/fs").InputFileSystem} InputFileSystem */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | const resolveContext = resolve.create({ | 
					
						
							|  |  |  | 	resolveToContext: true | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | let FS_ACCURACY = 2000; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @typedef {Object} Snapshot | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  |  * @property {number=} startTime | 
					
						
							|  |  |  |  * @property {Map<string, FileSystemInfoEntry | "error">=} fileTimestamps | 
					
						
							|  |  |  |  * @property {Map<string, string | "error">=} fileHashes | 
					
						
							|  |  |  |  * @property {Map<string, FileSystemInfoEntry | "error">=} contextTimestamps | 
					
						
							|  |  |  |  * @property {Map<string, string | "error">=} contextHashes | 
					
						
							|  |  |  |  * @property {Map<string, FileSystemInfoEntry | "error">=} missingTimestamps | 
					
						
							|  |  |  |  * @property {Map<string, string | "error">=} managedItemInfo | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-20 17:35:36 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * istanbul ignore next | 
					
						
							|  |  |  |  * @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; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 ( | 
					
						
							|  |  |  | 		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 | 
					
						
							|  |  |  | 	) { | 
					
						
							|  |  |  | 		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-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-07-18 05:35:05 +08:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2019-08-16 17:55:10 +08:00
										 |  |  | 	constructor(fs, { managedPaths = [], immutablePaths = [] } = {}) { | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		this.fs = fs; | 
					
						
							| 
									
										
										
										
											2019-10-27 14:38:06 +08:00
										 |  |  | 		/** @type {WeakMap<Snapshot, boolean | (function(WebpackError=, boolean=): void)[]>} */ | 
					
						
							|  |  |  | 		this._snapshotCache = new WeakMap(); | 
					
						
							| 
									
										
										
										
											2019-01-05 02:17:37 +08:00
										 |  |  | 		/** @type {Map<string, FileSystemInfoEntry | 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(); | 
					
						
							| 
									
										
										
										
											2019-01-05 02:17:37 +08:00
										 |  |  | 		/** @type {Map<string, FileSystemInfoEntry | 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(); | 
					
						
							|  |  |  | 		/** @type {Map<string, string>} */ | 
					
						
							|  |  |  | 		this._managedItems = new Map(); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		this.fileTimestampQueue = new AsyncQueue({ | 
					
						
							|  |  |  | 			name: "file timestamp", | 
					
						
							|  |  |  | 			parallelism: 30, | 
					
						
							|  |  |  | 			processor: this._readFileTimestamp.bind(this) | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		this.fileHashQueue = new AsyncQueue({ | 
					
						
							|  |  |  | 			name: "file hash", | 
					
						
							|  |  |  | 			parallelism: 10, | 
					
						
							|  |  |  | 			processor: this._readFileHash.bind(this) | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		this.contextTimestampQueue = new AsyncQueue({ | 
					
						
							|  |  |  | 			name: "context timestamp", | 
					
						
							|  |  |  | 			parallelism: 2, | 
					
						
							|  |  |  | 			processor: this._readContextTimestamp.bind(this) | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		this.contextHashQueue = new AsyncQueue({ | 
					
						
							|  |  |  | 			name: "context hash", | 
					
						
							|  |  |  | 			parallelism: 2, | 
					
						
							|  |  |  | 			processor: this._readContextHash.bind(this) | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 		this.managedItemQueue = new AsyncQueue({ | 
					
						
							|  |  |  | 			name: "managed item info", | 
					
						
							|  |  |  | 			parallelism: 10, | 
					
						
							|  |  |  | 			processor: this._getManagedItemInfo.bind(this) | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 		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) | 
					
						
							|  |  |  | 		); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							| 
									
										
										
										
											2019-01-05 02:17:37 +08:00
										 |  |  | 	 * @param {Map<string, FileSystemInfoEntry | 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); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							| 
									
										
										
										
											2019-01-05 02:17:37 +08:00
										 |  |  | 	 * @param {Map<string, FileSystemInfoEntry | 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); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {string} path file path | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 	 * @param {function(WebpackError=, FileSystemInfoEntry=): 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 | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 	 * @param {function(WebpackError=, FileSystemInfoEntry=): 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" | 
					
						
							|  |  |  | 								) | 
					
						
							|  |  |  | 									return callback(); | 
					
						
							|  |  |  | 								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" | 
					
						
							|  |  |  | 									) | 
					
						
							|  |  |  | 										return callback(); | 
					
						
							|  |  |  | 									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: { | 
					
						
							|  |  |  | 						/** @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
										 |  |  | 						} | 
					
						
							|  |  |  | 						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": | 
					
						
							|  |  |  | 						resolveContext(context, path, (err, result) => { | 
					
						
							|  |  |  | 							if (err) return callback(err); | 
					
						
							|  |  |  | 							if (result !== expectedResult) return callback(INVALID); | 
					
						
							|  |  |  | 							callback(); | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 					case "f": | 
					
						
							|  |  |  | 						resolve(context, path, (err, result) => { | 
					
						
							|  |  |  | 							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) | 
					
						
							|  |  |  | 	 * @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) { | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 		/** @type {Map<string, FileSystemInfoEntry | "error">} */ | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		const fileTimestamps = new Map(); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		/** @type {Map<string, string | "error">} */ | 
					
						
							|  |  |  | 		const fileHashes = new Map(); | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 		/** @type {Map<string, FileSystemInfoEntry | "error">} */ | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		const contextTimestamps = new Map(); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		/** @type {Map<string, string | "error">} */ | 
					
						
							|  |  |  | 		const contextHashes = new Map(); | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 		/** @type {Map<string, FileSystemInfoEntry | "error">} */ | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		const missingTimestamps = new Map(); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		/** @type {Map<string, string | "error">} */ | 
					
						
							|  |  |  | 		const managedItemInfo = new Map(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		const managedItems = new Set(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		let jobs = 1; | 
					
						
							|  |  |  | 		const jobDone = () => { | 
					
						
							|  |  |  | 			if (--jobs === 0) { | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 				const snapshot = {}; | 
					
						
							|  |  |  | 				if (startTime) snapshot.startTime = startTime; | 
					
						
							|  |  |  | 				if (fileTimestamps.size !== 0) snapshot.fileTimestamps = fileTimestamps; | 
					
						
							|  |  |  | 				if (fileHashes.size !== 0) snapshot.fileHashes = fileHashes; | 
					
						
							|  |  |  | 				if (contextTimestamps.size !== 0) | 
					
						
							|  |  |  | 					snapshot.contextTimestamps = contextTimestamps; | 
					
						
							|  |  |  | 				if (contextHashes.size !== 0) snapshot.contextHashes = contextHashes; | 
					
						
							|  |  |  | 				if (missingTimestamps.size !== 0) | 
					
						
							|  |  |  | 					snapshot.missingTimestamps = missingTimestamps; | 
					
						
							|  |  |  | 				if (managedItemInfo.size !== 0) | 
					
						
							|  |  |  | 					snapshot.managedItemInfo = managedItemInfo; | 
					
						
							|  |  |  | 				callback(null, snapshot); | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 		if (files) { | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			if (options && options.hash) { | 
					
						
							|  |  |  | 				files: for (const path of files) { | 
					
						
							| 
									
										
										
										
											2019-08-16 17:55:10 +08:00
										 |  |  | 					for (const immutablePath of this.immutablePathsWithSlash) { | 
					
						
							|  |  |  | 						if (path.startsWith(immutablePath)) { | 
					
						
							|  |  |  | 							continue files; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					for (const managedPath of this.managedPathsWithSlash) { | 
					
						
							|  |  |  | 						if (path.startsWith(managedPath)) { | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 							const managedItem = getManagedItem(managedPath, path); | 
					
						
							|  |  |  | 							if (managedItem) { | 
					
						
							|  |  |  | 								managedItems.add(managedItem); | 
					
						
							|  |  |  | 								continue files; | 
					
						
							|  |  |  | 							} | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 					const cache = this._fileHashes.get(path); | 
					
						
							|  |  |  | 					if (cache !== undefined) { | 
					
						
							|  |  |  | 						fileHashes.set(path, cache); | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						jobs++; | 
					
						
							|  |  |  | 						this.fileHashQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 							if (err) { | 
					
						
							|  |  |  | 								fileHashes.set(path, "error"); | 
					
						
							|  |  |  | 							} else { | 
					
						
							|  |  |  | 								fileHashes.set(path, entry); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				files: for (const path of files) { | 
					
						
							| 
									
										
										
										
											2019-08-16 17:55:10 +08:00
										 |  |  | 					for (const immutablePath of this.immutablePathsWithSlash) { | 
					
						
							|  |  |  | 						if (path.startsWith(immutablePath)) { | 
					
						
							|  |  |  | 							continue files; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					for (const managedPath of this.managedPathsWithSlash) { | 
					
						
							|  |  |  | 						if (path.startsWith(managedPath)) { | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 							const managedItem = getManagedItem(managedPath, path); | 
					
						
							|  |  |  | 							if (managedItem) { | 
					
						
							|  |  |  | 								managedItems.add(managedItem); | 
					
						
							|  |  |  | 								continue files; | 
					
						
							|  |  |  | 							} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					const cache = this._fileTimestamps.get(path); | 
					
						
							|  |  |  | 					if (cache !== undefined) { | 
					
						
							|  |  |  | 						fileTimestamps.set(path, cache); | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						jobs++; | 
					
						
							|  |  |  | 						this.fileTimestampQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 							if (err) { | 
					
						
							|  |  |  | 								fileTimestamps.set(path, "error"); | 
					
						
							|  |  |  | 							} else { | 
					
						
							|  |  |  | 								fileTimestamps.set(path, entry); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											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 (directories) { | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			if (options && options.hash) { | 
					
						
							|  |  |  | 				directories: for (const path of directories) { | 
					
						
							| 
									
										
										
										
											2019-08-16 17:55:10 +08:00
										 |  |  | 					for (const immutablePath of this.immutablePathsWithSlash) { | 
					
						
							|  |  |  | 						if (path.startsWith(immutablePath)) { | 
					
						
							|  |  |  | 							continue directories; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					for (const managedPath of this.managedPathsWithSlash) { | 
					
						
							|  |  |  | 						if (path.startsWith(managedPath)) { | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 							const managedItem = getManagedItem(managedPath, path); | 
					
						
							|  |  |  | 							if (managedItem) { | 
					
						
							|  |  |  | 								managedItems.add(managedItem); | 
					
						
							|  |  |  | 								continue directories; | 
					
						
							|  |  |  | 							} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					const cache = this._contextHashes.get(path); | 
					
						
							|  |  |  | 					if (cache !== undefined) { | 
					
						
							|  |  |  | 						contextHashes.set(path, cache); | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						jobs++; | 
					
						
							|  |  |  | 						this.contextHashQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 							if (err) { | 
					
						
							|  |  |  | 								contextHashes.set(path, "error"); | 
					
						
							|  |  |  | 							} else { | 
					
						
							|  |  |  | 								contextHashes.set(path, entry); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				directories: for (const path of directories) { | 
					
						
							| 
									
										
										
										
											2019-08-16 17:55:10 +08:00
										 |  |  | 					for (const immutablePath of this.immutablePathsWithSlash) { | 
					
						
							|  |  |  | 						if (path.startsWith(immutablePath)) { | 
					
						
							|  |  |  | 							continue directories; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					for (const managedPath of this.managedPathsWithSlash) { | 
					
						
							|  |  |  | 						if (path.startsWith(managedPath)) { | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 							const managedItem = getManagedItem(managedPath, path); | 
					
						
							|  |  |  | 							if (managedItem) { | 
					
						
							|  |  |  | 								managedItems.add(managedItem); | 
					
						
							|  |  |  | 								continue directories; | 
					
						
							|  |  |  | 							} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 					const cache = this._contextTimestamps.get(path); | 
					
						
							|  |  |  | 					if (cache !== undefined) { | 
					
						
							|  |  |  | 						contextTimestamps.set(path, cache); | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						jobs++; | 
					
						
							|  |  |  | 						this.contextTimestampQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 							if (err) { | 
					
						
							|  |  |  | 								contextTimestamps.set(path, "error"); | 
					
						
							|  |  |  | 							} else { | 
					
						
							|  |  |  | 								contextTimestamps.set(path, entry); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											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) { | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			missing: for (const path of missing) { | 
					
						
							| 
									
										
										
										
											2019-08-16 17:55:10 +08:00
										 |  |  | 				for (const immutablePath of this.immutablePathsWithSlash) { | 
					
						
							|  |  |  | 					if (path.startsWith(immutablePath)) { | 
					
						
							|  |  |  | 						continue missing; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 				for (const managedPath of this.managedPathsWithSlash) { | 
					
						
							|  |  |  | 					if (path.startsWith(managedPath)) { | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 						const managedItem = getManagedItem(managedPath, path); | 
					
						
							|  |  |  | 						if (managedItem) { | 
					
						
							|  |  |  | 							managedItems.add(managedItem); | 
					
						
							|  |  |  | 							continue missing; | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 				const cache = this._fileTimestamps.get(path); | 
					
						
							|  |  |  | 				if (cache !== undefined) { | 
					
						
							|  |  |  | 					missingTimestamps.set(path, cache); | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					jobs++; | 
					
						
							|  |  |  | 					this.fileTimestampQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 						if (err) { | 
					
						
							|  |  |  | 							missingTimestamps.set(path, "error"); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							missingTimestamps.set(path, entry); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						jobDone(); | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		for (const path of managedItems) { | 
					
						
							|  |  |  | 			const cache = this._managedItems.get(path); | 
					
						
							|  |  |  | 			if (cache !== undefined) { | 
					
						
							| 
									
										
										
										
											2019-09-08 21:36:30 +08:00
										 |  |  | 				managedItemInfo.set(path, cache || "error"); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			} else { | 
					
						
							|  |  |  | 				jobs++; | 
					
						
							|  |  |  | 				this.managedItemQueue.add(path, (err, entry) => { | 
					
						
							| 
									
										
										
										
											2019-09-08 21:36:30 +08:00
										 |  |  | 					if (err || !entry) { | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 						managedItemInfo.set(path, "error"); | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						managedItemInfo.set(path, entry); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					jobDone(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											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) { | 
					
						
							|  |  |  | 		/** @type {Snapshot} */ | 
					
						
							|  |  |  | 		const snapshot = {}; | 
					
						
							|  |  |  | 		if (snapshot1.startTime && snapshot2.startTime) | 
					
						
							|  |  |  | 			snapshot.startTime = Math.min(snapshot1.startTime, snapshot2.startTime); | 
					
						
							|  |  |  | 		else if (snapshot2.startTime) snapshot.startTime = snapshot2.startTime; | 
					
						
							|  |  |  | 		else if (snapshot1.startTime) snapshot.startTime = snapshot1.startTime; | 
					
						
							|  |  |  | 		if (snapshot1.fileTimestamps || snapshot2.fileTimestamps) { | 
					
						
							|  |  |  | 			snapshot.fileTimestamps = mergeMaps( | 
					
						
							|  |  |  | 				snapshot1.fileTimestamps, | 
					
						
							|  |  |  | 				snapshot2.fileTimestamps | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (snapshot1.fileHashes || snapshot2.fileHashes) { | 
					
						
							|  |  |  | 			snapshot.fileHashes = mergeMaps( | 
					
						
							|  |  |  | 				snapshot1.fileHashes, | 
					
						
							|  |  |  | 				snapshot2.fileHashes | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (snapshot1.contextTimestamps || snapshot2.contextTimestamps) { | 
					
						
							|  |  |  | 			snapshot.contextTimestamps = mergeMaps( | 
					
						
							|  |  |  | 				snapshot1.contextTimestamps, | 
					
						
							|  |  |  | 				snapshot2.contextTimestamps | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (snapshot1.contextHashes || snapshot2.contextHashes) { | 
					
						
							|  |  |  | 			snapshot.contextHashes = mergeMaps( | 
					
						
							|  |  |  | 				snapshot1.contextHashes, | 
					
						
							|  |  |  | 				snapshot2.contextHashes | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (snapshot1.missingTimestamps || snapshot2.missingTimestamps) { | 
					
						
							|  |  |  | 			snapshot.missingTimestamps = mergeMaps( | 
					
						
							|  |  |  | 				snapshot1.missingTimestamps, | 
					
						
							|  |  |  | 				snapshot2.missingTimestamps | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (snapshot1.managedItemInfo || snapshot2.managedItemInfo) { | 
					
						
							|  |  |  | 			snapshot.managedItemInfo = mergeMaps( | 
					
						
							|  |  |  | 				snapshot1.managedItemInfo, | 
					
						
							|  |  |  | 				snapshot2.managedItemInfo | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		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) { | 
					
						
							|  |  |  | 			if (typeof cachedResult === "boolean") { | 
					
						
							|  |  |  | 				callback(null, cachedResult); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				cachedResult.push(callback); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		const callbacks = [callback]; | 
					
						
							|  |  |  | 		this._snapshotCache.set(snapshot, callbacks); | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		const { | 
					
						
							|  |  |  | 			startTime, | 
					
						
							|  |  |  | 			fileTimestamps, | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			fileHashes, | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			contextTimestamps, | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			contextHashes, | 
					
						
							| 
									
										
										
										
											2019-08-13 23:26:54 +08:00
										 |  |  | 			missingTimestamps, | 
					
						
							|  |  |  | 			managedItemInfo | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		} = snapshot; | 
					
						
							|  |  |  | 		let jobs = 1; | 
					
						
							|  |  |  | 		const jobDone = () => { | 
					
						
							|  |  |  | 			if (--jobs === 0) { | 
					
						
							| 
									
										
										
										
											2019-10-27 14:38:06 +08:00
										 |  |  | 				this._snapshotCache.set(snapshot, true); | 
					
						
							|  |  |  | 				for (const callback of callbacks) callback(null, true); | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 		const invalid = () => { | 
					
						
							|  |  |  | 			if (jobs > 0) { | 
					
						
							|  |  |  | 				jobs = NaN; | 
					
						
							| 
									
										
										
										
											2019-10-27 14:38:06 +08:00
										 |  |  | 				this._snapshotCache.set(snapshot, false); | 
					
						
							|  |  |  | 				for (const callback of callbacks) callback(null, false); | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		const checkHash = (current, snap) => { | 
					
						
							|  |  |  | 			if (snap === "error") { | 
					
						
							|  |  |  | 				// If there was an error while snapshotting (i. e. EBUSY)
 | 
					
						
							|  |  |  | 				// we can't compare further data and assume it's invalid
 | 
					
						
							|  |  |  | 				return false; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return current === snap; | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 		/** | 
					
						
							|  |  |  | 		 * @param {FileSystemInfoEntry} current current entry | 
					
						
							|  |  |  | 		 * @param {FileSystemInfoEntry | "error"} snap entry from snapshot | 
					
						
							|  |  |  | 		 * @returns {boolean} true, if ok | 
					
						
							|  |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		const checkExistance = (current, snap) => { | 
					
						
							|  |  |  | 			if (snap === "error") { | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 				// If there was an error while snapshotting (i. e. EBUSY)
 | 
					
						
							|  |  |  | 				// we can't compare further data and assume it's invalid
 | 
					
						
							|  |  |  | 				return false; | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			return !current === !snap; | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 		/** | 
					
						
							|  |  |  | 		 * @param {FileSystemInfoEntry} current current entry | 
					
						
							|  |  |  | 		 * @param {FileSystemInfoEntry | "error"} snap entry from snapshot | 
					
						
							|  |  |  | 		 * @returns {boolean} true, if ok | 
					
						
							|  |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		const checkFile = (current, snap) => { | 
					
						
							|  |  |  | 			if (snap === "error") { | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 				// If there was an error while snapshotting (i. e. EBUSY)
 | 
					
						
							|  |  |  | 				// we can't compare further data and assume it's invalid
 | 
					
						
							|  |  |  | 				return false; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if (current && current.safeTime > startTime) { | 
					
						
							|  |  |  | 				// If a change happened after starting reading the item
 | 
					
						
							|  |  |  | 				// this may no longer be valid
 | 
					
						
							|  |  |  | 				return false; | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 			if (!current !== !snap) { | 
					
						
							|  |  |  | 				// If existance of item differs
 | 
					
						
							|  |  |  | 				// it's invalid
 | 
					
						
							|  |  |  | 				return false; | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 			if (current) { | 
					
						
							|  |  |  | 				// For existing items only
 | 
					
						
							|  |  |  | 				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
 | 
					
						
							|  |  |  | 					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
 | 
					
						
							|  |  |  | 					return false; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			return true; | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		if (fileTimestamps) { | 
					
						
							|  |  |  | 			for (const [path, ts] of fileTimestamps) { | 
					
						
							|  |  |  | 				const cache = this._fileTimestamps.get(path); | 
					
						
							|  |  |  | 				if (cache !== undefined) { | 
					
						
							|  |  |  | 					if (!checkFile(cache, ts)) { | 
					
						
							|  |  |  | 						invalid(); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					jobs++; | 
					
						
							|  |  |  | 					this.fileTimestampQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 						if (err) return invalid(); | 
					
						
							|  |  |  | 						if (!checkFile(entry, ts)) { | 
					
						
							|  |  |  | 							invalid(); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					}); | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (fileHashes) { | 
					
						
							|  |  |  | 			for (const [path, hash] of fileHashes) { | 
					
						
							|  |  |  | 				const cache = this._fileHashes.get(path); | 
					
						
							|  |  |  | 				if (cache !== undefined) { | 
					
						
							|  |  |  | 					if (!checkHash(cache, hash)) { | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 						invalid(); | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 				} else { | 
					
						
							|  |  |  | 					jobs++; | 
					
						
							|  |  |  | 					this.fileHashQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 						if (err) return invalid(); | 
					
						
							|  |  |  | 						if (!checkHash(entry, hash)) { | 
					
						
							|  |  |  | 							invalid(); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		if (contextTimestamps && contextTimestamps.size > 0) { | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 			for (const [path, ts] of contextTimestamps) { | 
					
						
							|  |  |  | 				const cache = this._contextTimestamps.get(path); | 
					
						
							|  |  |  | 				if (cache !== undefined) { | 
					
						
							|  |  |  | 					if (!checkFile(cache, ts)) { | 
					
						
							|  |  |  | 						invalid(); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					jobs++; | 
					
						
							|  |  |  | 					this.contextTimestampQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 						if (err) return invalid(); | 
					
						
							|  |  |  | 						if (!checkFile(entry, ts)) { | 
					
						
							|  |  |  | 							invalid(); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		if (contextHashes) { | 
					
						
							|  |  |  | 			for (const [path, hash] of contextHashes) { | 
					
						
							|  |  |  | 				const cache = this._contextHashes.get(path); | 
					
						
							|  |  |  | 				if (cache !== undefined) { | 
					
						
							|  |  |  | 					if (!checkHash(cache, hash)) { | 
					
						
							|  |  |  | 						invalid(); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					jobs++; | 
					
						
							|  |  |  | 					this.contextHashQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 						if (err) return invalid(); | 
					
						
							|  |  |  | 						if (!checkHash(entry, hash)) { | 
					
						
							|  |  |  | 							invalid(); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					}); | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (missingTimestamps) { | 
					
						
							|  |  |  | 			for (const [path, ts] of missingTimestamps) { | 
					
						
							|  |  |  | 				const cache = this._fileTimestamps.get(path); | 
					
						
							|  |  |  | 				if (cache !== undefined) { | 
					
						
							|  |  |  | 					if (!checkExistance(cache, ts)) { | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 						invalid(); | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 				} else { | 
					
						
							|  |  |  | 					jobs++; | 
					
						
							|  |  |  | 					this.fileTimestampQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 						if (err) return invalid(); | 
					
						
							|  |  |  | 						if (!checkExistance(entry, ts)) { | 
					
						
							|  |  |  | 							invalid(); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-13 23:26:54 +08:00
										 |  |  | 		if (managedItemInfo) { | 
					
						
							|  |  |  | 			for (const [path, info] of managedItemInfo) { | 
					
						
							|  |  |  | 				const cache = this._managedItems.get(path); | 
					
						
							|  |  |  | 				if (cache !== undefined) { | 
					
						
							|  |  |  | 					if (!checkHash(cache, info)) { | 
					
						
							|  |  |  | 						invalid(); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					jobs++; | 
					
						
							|  |  |  | 					this.managedItemQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 						if (err) return invalid(); | 
					
						
							|  |  |  | 						if (!checkHash(entry, info)) { | 
					
						
							|  |  |  | 							invalid(); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		jobDone(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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); | 
					
						
							|  |  |  | 					return callback(null, null); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return callback(err); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 			const mtime = +stat.mtime; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (mtime) applyMtime(mtime); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			const ts = { | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 				safeTime: mtime ? mtime + FS_ACCURACY : Infinity, | 
					
						
							|  |  |  | 				timestamp: stat.isDirectory() ? undefined : mtime | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 			}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			this._fileTimestamps.set(path, ts); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			callback(null, ts); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 	_readFileHash(path, callback) { | 
					
						
							|  |  |  | 		this.fs.readFile(path, (err, content) => { | 
					
						
							|  |  |  | 			if (err) { | 
					
						
							|  |  |  | 				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); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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); | 
					
						
							|  |  |  | 					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
 | 
					
						
							|  |  |  | 									return this.managedItemQueue.add(child, (err, info) => { | 
					
						
							|  |  |  | 										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); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					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
 | 
					
						
							|  |  |  | 									return this.managedItemQueue.add(child, (err, info) => { | 
					
						
							|  |  |  | 										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); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	_getManagedItemInfo(path, callback) { | 
					
						
							|  |  |  | 		const packageJsonPath = join(this.fs, path, "package.json"); | 
					
						
							|  |  |  | 		this.fs.readFile(packageJsonPath, (err, content) => { | 
					
						
							| 
									
										
										
										
											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") { | 
					
						
							|  |  |  | 					// no package.json or path is not a directory
 | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 					this._managedItems.set(path, null); | 
					
						
							|  |  |  | 					return callback(null, null); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return callback(err); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			let data; | 
					
						
							|  |  |  | 			try { | 
					
						
							|  |  |  | 				data = JSON.parse(content.toString("utf-8")); | 
					
						
							|  |  |  | 			} catch (e) { | 
					
						
							|  |  |  | 				return callback(e); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			const info = `${data.name || ""}@${data.version || ""}`; | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 			this._managedItems.set(path, info); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			callback(null, info); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	getDeprecatedFileTimestamps() { | 
					
						
							|  |  |  | 		const map = new Map(); | 
					
						
							|  |  |  | 		for (const [path, info] of this._fileTimestamps) { | 
					
						
							|  |  |  | 			if (info) map.set(path, info.safeTime); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return map; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	getDeprecatedContextTimestamps() { | 
					
						
							|  |  |  | 		const map = new Map(); | 
					
						
							|  |  |  | 		for (const [path, info] of this._contextTimestamps) { | 
					
						
							|  |  |  | 			if (info) map.set(path, info.safeTime); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return map; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports = FileSystemInfo; |