| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | /* | 
					
						
							|  |  |  | 	MIT License http://www.opensource.org/licenses/mit-license.php
 | 
					
						
							|  |  |  | 	Author Tobias Koppers @sokra | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | "use strict"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-11 22:25:03 +08:00
										 |  |  | const { create: createResolver } = require("enhanced-resolve"); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | const asyncLib = require("neo-async"); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | const AsyncQueue = require("./util/AsyncQueue"); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | const createHash = require("./util/createHash"); | 
					
						
							| 
									
										
										
										
											2019-08-20 19:50:14 +08:00
										 |  |  | const { join, dirname, relative } = require("./util/fs"); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | /** @typedef {import("./WebpackError")} WebpackError */ | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | /** @typedef {import("./logging/Logger").Logger} Logger */ | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | /** @typedef {import("./util/fs").InputFileSystem} InputFileSystem */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-11 22:25:03 +08:00
										 |  |  | const resolveContext = createResolver({ | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 	resolveToContext: true | 
					
						
							|  |  |  | }); | 
					
						
							| 
									
										
										
										
											2019-11-11 22:25:03 +08:00
										 |  |  | const resolve = createResolver({ | 
					
						
							|  |  |  | 	extensions: [".js", ".json", ".node"] | 
					
						
							|  |  |  | }); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | let FS_ACCURACY = 2000; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | const EMPTY_SET = new Set(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-13 23:21:19 +08:00
										 |  |  | const RBDT_RESOLVE = 0; | 
					
						
							|  |  |  | const RBDT_RESOLVE_DIRECTORY = 1; | 
					
						
							| 
									
										
										
										
											2019-08-20 19:50:14 +08:00
										 |  |  | const RBDT_RESOLVE_FILE = 2; | 
					
						
							|  |  |  | const RBDT_DIRECTORY = 3; | 
					
						
							|  |  |  | const RBDT_FILE = 4; | 
					
						
							|  |  |  | const RBDT_DIRECTORY_DEPENDENCIES = 5; | 
					
						
							|  |  |  | const RBDT_FILE_DEPENDENCIES = 6; | 
					
						
							| 
									
										
										
										
											2019-08-13 23:21:19 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | const INVALID = Symbol("invalid"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @typedef {Object} FileSystemInfoEntry | 
					
						
							|  |  |  |  * @property {number} safeTime | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  |  * @property {number=} timestamp | 
					
						
							|  |  |  |  * @property {string=} timestampHash | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @typedef {Object} Snapshot | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  |  * @property {number=} startTime | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  |  * @property {Map<string, FileSystemInfoEntry>=} fileTimestamps | 
					
						
							|  |  |  |  * @property {Map<string, string>=} fileHashes | 
					
						
							|  |  |  |  * @property {Map<string, FileSystemInfoEntry>=} contextTimestamps | 
					
						
							|  |  |  |  * @property {Map<string, string>=} contextHashes | 
					
						
							|  |  |  |  * @property {Map<string, boolean>=} missingExistance | 
					
						
							|  |  |  |  * @property {Map<string, string>=} managedItemInfo | 
					
						
							| 
									
										
										
										
											2020-01-31 17:59:28 +08:00
										 |  |  |  * @property {Set<Snapshot>=} children | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @typedef {Object} SnapshotOptimizationEntry | 
					
						
							|  |  |  |  * @property {Snapshot} snapshot | 
					
						
							|  |  |  |  * @property {number} shared | 
					
						
							|  |  |  |  * @property {Set<string>} snapshotContent | 
					
						
							|  |  |  |  * @property {Set<SnapshotOptimizationEntry>} children | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @typedef {Object} ResolveBuildDependenciesResult | 
					
						
							|  |  |  |  * @property {Set<string>} files list of files | 
					
						
							|  |  |  |  * @property {Set<string>} directories list of directories | 
					
						
							|  |  |  |  * @property {Set<string>} missing list of missing entries | 
					
						
							|  |  |  |  * @property {Map<string, string>} resolveResults stored resolve results | 
					
						
							|  |  |  |  * @property {Object} resolveDependencies dependencies of the resolving | 
					
						
							|  |  |  |  * @property {Set<string>} resolveDependencies.files list of files | 
					
						
							|  |  |  |  * @property {Set<string>} resolveDependencies.directories list of directories | 
					
						
							|  |  |  |  * @property {Set<string>} resolveDependencies.missing list of missing entries | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 ( | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 		path.length >= i + 13 && | 
					
						
							| 
									
										
										
										
											2019-08-13 23:58:51 +08:00
										 |  |  | 		path.charCodeAt(i + 1) === 110 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 2) === 111 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 3) === 100 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 4) === 101 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 5) === 95 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 6) === 109 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 7) === 111 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 8) === 100 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 9) === 117 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 10) === 108 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 11) === 101 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 12) === 115 | 
					
						
							|  |  |  | 	) { | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 		// if this is the end of the path
 | 
					
						
							|  |  |  | 		if (path.length === i + 13) { | 
					
						
							|  |  |  | 			// return the node_modules directory
 | 
					
						
							|  |  |  | 			// it's special
 | 
					
						
							|  |  |  | 			return path; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-13 23:58:51 +08:00
										 |  |  | 		const c = path.charCodeAt(i + 13); | 
					
						
							| 
									
										
										
										
											2019-10-20 17:35:36 +08:00
										 |  |  | 		// if next symbol is slash or backslash
 | 
					
						
							| 
									
										
										
										
											2019-08-13 23:58:51 +08:00
										 |  |  | 		if (c === 47 || c === 92) { | 
					
						
							|  |  |  | 			// Managed subpath
 | 
					
						
							|  |  |  | 			return getManagedItem(path.slice(0, i + 14), path); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-08-13 23:15:47 +08:00
										 |  |  | 	return path.slice(0, i); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-14 23:49:45 +08:00
										 |  |  | /** | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  |  * @param {FileSystemInfoEntry} entry file system info entry | 
					
						
							|  |  |  |  * @returns {boolean} existance flag | 
					
						
							| 
									
										
										
										
											2019-11-14 23:49:45 +08:00
										 |  |  |  */ | 
					
						
							|  |  |  | const toExistance = entry => { | 
					
						
							|  |  |  | 	return Boolean(entry); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-20 17:35:36 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Used to access information about the filesystem in a cached way | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | class FileSystemInfo { | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {InputFileSystem} fs file system | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 	 * @param {Object} options options | 
					
						
							|  |  |  | 	 * @param {Iterable<string>=} options.managedPaths paths that are only managed by a package manager | 
					
						
							| 
									
										
										
										
											2019-08-16 17:55:10 +08:00
										 |  |  | 	 * @param {Iterable<string>=} options.immutablePaths paths that are immutable | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 	 * @param {Logger=} options.logger logger used to log invalid snapshots | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 	constructor(fs, { managedPaths = [], immutablePaths = [], logger } = {}) { | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		this.fs = fs; | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 		this.logger = logger; | 
					
						
							|  |  |  | 		this._remainingLogs = logger ? 40 : 0; | 
					
						
							|  |  |  | 		this._loggedPaths = logger ? new Set() : undefined; | 
					
						
							| 
									
										
										
										
											2019-10-27 14:38:06 +08:00
										 |  |  | 		/** @type {WeakMap<Snapshot, boolean | (function(WebpackError=, boolean=): void)[]>} */ | 
					
						
							|  |  |  | 		this._snapshotCache = new WeakMap(); | 
					
						
							| 
									
										
										
										
											2020-01-31 17:59:28 +08:00
										 |  |  | 		/** @type {Map<string, SnapshotOptimizationEntry>} */ | 
					
						
							|  |  |  | 		this._snapshotOptimization = new Map(); | 
					
						
							| 
									
										
										
										
											2019-11-06 06:46:34 +08:00
										 |  |  | 		/** @type {Map<string, FileSystemInfoEntry | "ignore" | null>} */ | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		this._fileTimestamps = new Map(); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		/** @type {Map<string, string>} */ | 
					
						
							|  |  |  | 		this._fileHashes = new Map(); | 
					
						
							| 
									
										
										
										
											2019-11-06 06:46:34 +08:00
										 |  |  | 		/** @type {Map<string, FileSystemInfoEntry | "ignore" | null>} */ | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		this._contextTimestamps = new Map(); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		/** @type {Map<string, string>} */ | 
					
						
							|  |  |  | 		this._contextHashes = new Map(); | 
					
						
							|  |  |  | 		/** @type {Map<string, string>} */ | 
					
						
							|  |  |  | 		this._managedItems = new Map(); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		/** @type {AsyncQueue<string, string, FileSystemInfoEntry | null>} */ | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		this.fileTimestampQueue = new AsyncQueue({ | 
					
						
							|  |  |  | 			name: "file timestamp", | 
					
						
							|  |  |  | 			parallelism: 30, | 
					
						
							|  |  |  | 			processor: this._readFileTimestamp.bind(this) | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		/** @type {AsyncQueue<string, string, string | null>} */ | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		this.fileHashQueue = new AsyncQueue({ | 
					
						
							|  |  |  | 			name: "file hash", | 
					
						
							|  |  |  | 			parallelism: 10, | 
					
						
							|  |  |  | 			processor: this._readFileHash.bind(this) | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		/** @type {AsyncQueue<string, string, FileSystemInfoEntry | null>} */ | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		this.contextTimestampQueue = new AsyncQueue({ | 
					
						
							|  |  |  | 			name: "context timestamp", | 
					
						
							|  |  |  | 			parallelism: 2, | 
					
						
							|  |  |  | 			processor: this._readContextTimestamp.bind(this) | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		/** @type {AsyncQueue<string, string, string | null>} */ | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		this.contextHashQueue = new AsyncQueue({ | 
					
						
							|  |  |  | 			name: "context hash", | 
					
						
							|  |  |  | 			parallelism: 2, | 
					
						
							|  |  |  | 			processor: this._readContextHash.bind(this) | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		/** @type {AsyncQueue<string, string, string | null>} */ | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		this.managedItemQueue = new AsyncQueue({ | 
					
						
							|  |  |  | 			name: "managed item info", | 
					
						
							|  |  |  | 			parallelism: 10, | 
					
						
							|  |  |  | 			processor: this._getManagedItemInfo.bind(this) | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		/** @type {AsyncQueue<string, string, Set<string>>} */ | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 		this.managedItemDirectoryQueue = new AsyncQueue({ | 
					
						
							|  |  |  | 			name: "managed item directory info", | 
					
						
							|  |  |  | 			parallelism: 10, | 
					
						
							|  |  |  | 			processor: this._getManagedItemDirectoryInfo.bind(this) | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		this.managedPaths = Array.from(managedPaths); | 
					
						
							|  |  |  | 		this.managedPathsWithSlash = this.managedPaths.map(p => | 
					
						
							|  |  |  | 			join(fs, p, "_").slice(0, -1) | 
					
						
							|  |  |  | 		); | 
					
						
							| 
									
										
										
										
											2019-08-16 17:55:10 +08:00
										 |  |  | 		this.immutablePaths = Array.from(immutablePaths); | 
					
						
							|  |  |  | 		this.immutablePathsWithSlash = this.immutablePaths.map(p => | 
					
						
							|  |  |  | 			join(fs, p, "_").slice(0, -1) | 
					
						
							|  |  |  | 		); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-15 00:28:10 +08:00
										 |  |  | 	_log(path, reason, ...args) { | 
					
						
							|  |  |  | 		const key = path + reason; | 
					
						
							|  |  |  | 		if (this._loggedPaths.has(key)) return; | 
					
						
							|  |  |  | 		this._loggedPaths.add(key); | 
					
						
							|  |  |  | 		this.logger.debug(`${path} invalidated because ${reason}`, ...args); | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 		if (--this._remainingLogs === 0) { | 
					
						
							|  |  |  | 			this.logger.debug( | 
					
						
							|  |  |  | 				"Logging limit has been reached and no futher logging will be emitted by FileSystemInfo" | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	/** | 
					
						
							| 
									
										
										
										
											2019-11-06 06:46:34 +08:00
										 |  |  | 	 * @param {Map<string, FileSystemInfoEntry | "ignore" | null>} map timestamps | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	addFileTimestamps(map) { | 
					
						
							|  |  |  | 		for (const [path, ts] of map) { | 
					
						
							|  |  |  | 			this._fileTimestamps.set(path, ts); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							| 
									
										
										
										
											2019-11-06 06:46:34 +08:00
										 |  |  | 	 * @param {Map<string, FileSystemInfoEntry | "ignore" | null>} map timestamps | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	addContextTimestamps(map) { | 
					
						
							|  |  |  | 		for (const [path, ts] of map) { | 
					
						
							|  |  |  | 			this._contextTimestamps.set(path, ts); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {string} path file path | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 	 * @param {function(WebpackError=, (FileSystemInfoEntry | "ignore" | null)=): void} callback callback function | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	getFileTimestamp(path, callback) { | 
					
						
							|  |  |  | 		const cache = this._fileTimestamps.get(path); | 
					
						
							| 
									
										
										
										
											2018-09-28 03:28:07 +08:00
										 |  |  | 		if (cache !== undefined) return callback(null, cache); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		this.fileTimestampQueue.add(path, callback); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {string} path context path | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 	 * @param {function(WebpackError=, (FileSystemInfoEntry | "ignore" | null)=): void} callback callback function | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	getContextTimestamp(path, callback) { | 
					
						
							|  |  |  | 		const cache = this._contextTimestamps.get(path); | 
					
						
							| 
									
										
										
										
											2018-09-28 03:28:07 +08:00
										 |  |  | 		if (cache !== undefined) return callback(null, cache); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		this.contextTimestampQueue.add(path, callback); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {string} path file path | 
					
						
							|  |  |  | 	 * @param {function(WebpackError=, string=): void} callback callback function | 
					
						
							|  |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	getFileHash(path, callback) { | 
					
						
							|  |  |  | 		const cache = this._fileHashes.get(path); | 
					
						
							|  |  |  | 		if (cache !== undefined) return callback(null, cache); | 
					
						
							|  |  |  | 		this.fileHashQueue.add(path, callback); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {string} path context path | 
					
						
							|  |  |  | 	 * @param {function(WebpackError=, string=): void} callback callback function | 
					
						
							|  |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	getContextHash(path, callback) { | 
					
						
							|  |  |  | 		const cache = this._contextHashes.get(path); | 
					
						
							|  |  |  | 		if (cache !== undefined) return callback(null, cache); | 
					
						
							|  |  |  | 		this.contextHashQueue.add(path, callback); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {string} context context directory | 
					
						
							|  |  |  | 	 * @param {Iterable<string>} deps dependencies | 
					
						
							|  |  |  | 	 * @param {function(Error=, ResolveBuildDependenciesResult=): void} callback callback function | 
					
						
							|  |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 	resolveBuildDependencies(context, deps, callback) { | 
					
						
							| 
									
										
										
										
											2019-10-20 17:35:36 +08:00
										 |  |  | 		/** @type {Set<string>} */ | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		const files = new Set(); | 
					
						
							| 
									
										
										
										
											2019-10-20 17:35:36 +08:00
										 |  |  | 		/** @type {Set<string>} */ | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		const directories = new Set(); | 
					
						
							| 
									
										
										
										
											2019-10-20 17:35:36 +08:00
										 |  |  | 		/** @type {Set<string>} */ | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		const missing = new Set(); | 
					
						
							| 
									
										
										
										
											2019-10-20 17:35:36 +08:00
										 |  |  | 		/** @type {Set<string>} */ | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | 		const resolveFiles = new Set(); | 
					
						
							| 
									
										
										
										
											2019-10-20 17:35:36 +08:00
										 |  |  | 		/** @type {Set<string>} */ | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | 		const resolveDirectories = new Set(); | 
					
						
							| 
									
										
										
										
											2019-10-20 17:35:36 +08:00
										 |  |  | 		/** @type {Set<string>} */ | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | 		const resolveMissing = new Set(); | 
					
						
							| 
									
										
										
										
											2019-10-20 17:35:36 +08:00
										 |  |  | 		/** @type {Map<string, string>} */ | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | 		const resolveResults = new Map(); | 
					
						
							| 
									
										
										
										
											2019-08-20 19:50:14 +08:00
										 |  |  | 		/** @type {asyncLib.QueueObject<{type: number, path: string, context?: string, expected?: string }, Error>} */ | 
					
						
							|  |  |  | 		const queue = asyncLib.queue( | 
					
						
							|  |  |  | 			({ type, context, path, expected }, callback) => { | 
					
						
							|  |  |  | 				const resolveDirectory = path => { | 
					
						
							|  |  |  | 					const key = `d\n${context}\n${path}`; | 
					
						
							|  |  |  | 					if (resolveResults.has(key)) { | 
					
						
							|  |  |  | 						return callback(); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					resolveContext( | 
					
						
							|  |  |  | 						context, | 
					
						
							|  |  |  | 						path, | 
					
						
							|  |  |  | 						{ | 
					
						
							|  |  |  | 							fileDependencies: resolveFiles, | 
					
						
							|  |  |  | 							contextDependencies: resolveDirectories, | 
					
						
							|  |  |  | 							missingDependencies: resolveMissing | 
					
						
							|  |  |  | 						}, | 
					
						
							|  |  |  | 						(err, result) => { | 
					
						
							|  |  |  | 							if (err) { | 
					
						
							|  |  |  | 								if ( | 
					
						
							|  |  |  | 									err.code === "ENOENT" || | 
					
						
							|  |  |  | 									err.code === "UNDECLARED_DEPENDENCY" | 
					
						
							|  |  |  | 								) | 
					
						
							|  |  |  | 									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": | 
					
						
							| 
									
										
										
										
											2019-11-11 22:25:03 +08:00
										 |  |  | 						resolveContext(context, path, {}, (err, result) => { | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | 							if (err) return callback(err); | 
					
						
							|  |  |  | 							if (result !== expectedResult) return callback(INVALID); | 
					
						
							|  |  |  | 							callback(); | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 					case "f": | 
					
						
							| 
									
										
										
										
											2019-11-11 22:25:03 +08:00
										 |  |  | 						resolve(context, path, {}, (err, result) => { | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | 							if (err) return callback(err); | 
					
						
							|  |  |  | 							if (result !== expectedResult) return callback(INVALID); | 
					
						
							|  |  |  | 							callback(); | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 					default: | 
					
						
							|  |  |  | 						callback(new Error("Unexpected type in resolve result key")); | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			/** | 
					
						
							|  |  |  | 			 * @param {Error | typeof INVALID=} err error or invalid flag | 
					
						
							|  |  |  | 			 * @returns {void} | 
					
						
							|  |  |  | 			 */ | 
					
						
							|  |  |  | 			err => { | 
					
						
							|  |  |  | 				if (err === INVALID) { | 
					
						
							|  |  |  | 					return callback(null, false); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if (err) { | 
					
						
							|  |  |  | 					return callback(err); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return callback(null, true); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * @param {number} startTime when processing the files has started | 
					
						
							|  |  |  | 	 * @param {Iterable<string>} files all files | 
					
						
							|  |  |  | 	 * @param {Iterable<string>} directories all directories | 
					
						
							|  |  |  | 	 * @param {Iterable<string>} missing all missing files or directories | 
					
						
							|  |  |  | 	 * @param {Object} options options object (for future extensions) | 
					
						
							| 
									
										
										
										
											2019-11-14 23:49:45 +08:00
										 |  |  | 	 * @param {boolean=} options.hash should use hash to snapshot | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 	 * @param {function(WebpackError=, Snapshot=): void} callback callback function | 
					
						
							|  |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 	createSnapshot(startTime, files, directories, missing, options, callback) { | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		/** @type {Map<string, FileSystemInfoEntry>} */ | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		const fileTimestamps = new Map(); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		/** @type {Map<string, string>} */ | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		const fileHashes = new Map(); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		/** @type {Map<string, FileSystemInfoEntry>} */ | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		const contextTimestamps = new Map(); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		/** @type {Map<string, string>} */ | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		const contextHashes = new Map(); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		/** @type {Map<string, boolean>} */ | 
					
						
							| 
									
										
										
										
											2019-11-14 23:49:45 +08:00
										 |  |  | 		const missingExistance = new Map(); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		/** @type {Map<string, string>} */ | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		const managedItemInfo = new Map(); | 
					
						
							| 
									
										
										
										
											2020-01-31 17:59:28 +08:00
										 |  |  | 		/** @type {Set<Snapshot>} */ | 
					
						
							|  |  |  | 		const children = new Set(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/** @type {Set<string>} */ | 
					
						
							|  |  |  | 		const unsetOptimizationEntries = new Set(); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		/** @type {Set<string>} */ | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		const managedItems = new Set(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							| 
									
										
										
										
											2019-11-14 23:49:45 +08:00
										 |  |  | 				if (missingExistance.size !== 0) | 
					
						
							|  |  |  | 					snapshot.missingExistance = missingExistance; | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 				if (managedItemInfo.size !== 0) | 
					
						
							|  |  |  | 					snapshot.managedItemInfo = managedItemInfo; | 
					
						
							| 
									
										
										
										
											2020-01-31 17:59:28 +08:00
										 |  |  | 				if (children.size !== 0) snapshot.children = children; | 
					
						
							| 
									
										
										
										
											2019-11-08 00:31:18 +08:00
										 |  |  | 				this._snapshotCache.set(snapshot, true); | 
					
						
							| 
									
										
										
										
											2020-01-31 17:59:28 +08:00
										 |  |  | 				const optimizationEntry = { | 
					
						
							|  |  |  | 					snapshot, | 
					
						
							|  |  |  | 					shared: 0, | 
					
						
							|  |  |  | 					snapshotContent: undefined, | 
					
						
							|  |  |  | 					children: undefined | 
					
						
							|  |  |  | 				}; | 
					
						
							|  |  |  | 				for (const path of unsetOptimizationEntries) { | 
					
						
							|  |  |  | 					this._snapshotOptimization.set(path, optimizationEntry); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 				callback(null, snapshot); | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		const jobError = () => { | 
					
						
							|  |  |  | 			if (jobs > 0) { | 
					
						
							|  |  |  | 				// large negative number instead of NaN or something else to keep jobs to stay a SMI (v8)
 | 
					
						
							|  |  |  | 				jobs = -100000000; | 
					
						
							|  |  |  | 				callback(null, null); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											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) { | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 								if (this.logger) { | 
					
						
							|  |  |  | 									this.logger.debug( | 
					
						
							|  |  |  | 										`Error snapshotting file hash of ${path}: ${err}` | 
					
						
							|  |  |  | 									); | 
					
						
							|  |  |  | 								} | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 								jobError(); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 							} else { | 
					
						
							|  |  |  | 								fileHashes.set(path, entry); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 								jobDone(); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 							} | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else { | 
					
						
							| 
									
										
										
										
											2020-01-31 17:59:28 +08:00
										 |  |  | 				const capturedFiles = new Set(); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 				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
										 |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2020-01-31 17:59:28 +08:00
										 |  |  | 					capturedFiles.add(path); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				const checkedOptimizationEntries = new Set(); | 
					
						
							|  |  |  | 				/** | 
					
						
							|  |  |  | 				 * @param {SnapshotOptimizationEntry} entry optimization entry | 
					
						
							|  |  |  | 				 * @returns {void} | 
					
						
							|  |  |  | 				 */ | 
					
						
							|  |  |  | 				const increaseSharedAndStoreOptimizationEntry = entry => { | 
					
						
							|  |  |  | 					if (entry.children !== undefined) { | 
					
						
							|  |  |  | 						entry.children.forEach(increaseSharedAndStoreOptimizationEntry); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					entry.shared++; | 
					
						
							|  |  |  | 					storeOptimizationEntry(entry); | 
					
						
							|  |  |  | 				}; | 
					
						
							|  |  |  | 				/** | 
					
						
							|  |  |  | 				 * @param {SnapshotOptimizationEntry} entry optimization entry | 
					
						
							|  |  |  | 				 * @returns {void} | 
					
						
							|  |  |  | 				 */ | 
					
						
							|  |  |  | 				const storeOptimizationEntry = entry => { | 
					
						
							|  |  |  | 					for (const path of entry.snapshotContent) { | 
					
						
							|  |  |  | 						const old = this._snapshotOptimization.get(path); | 
					
						
							|  |  |  | 						if (old.shared < entry.shared) { | 
					
						
							|  |  |  | 							this._snapshotOptimization.set(path, entry); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						capturedFiles.delete(path); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				}; | 
					
						
							|  |  |  | 				capturedFiles: for (const path of capturedFiles) { | 
					
						
							|  |  |  | 					const optimizationEntry = this._snapshotOptimization.get(path); | 
					
						
							|  |  |  | 					if (optimizationEntry === undefined) { | 
					
						
							|  |  |  | 						unsetOptimizationEntries.add(path); | 
					
						
							|  |  |  | 						continue; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					if (checkedOptimizationEntries.has(optimizationEntry)) continue; | 
					
						
							|  |  |  | 					const snapshot = optimizationEntry.snapshot; | 
					
						
							|  |  |  | 					if (optimizationEntry.shared > 0) { | 
					
						
							|  |  |  | 						// It's a shared snapshot
 | 
					
						
							|  |  |  | 						// We can't change it, so we can only use it when all files match
 | 
					
						
							|  |  |  | 						const nonSharedFiles = new Set(); | 
					
						
							|  |  |  | 						for (const path of optimizationEntry.snapshotContent) { | 
					
						
							|  |  |  | 							if (!capturedFiles.has(path)) { | 
					
						
							|  |  |  | 								if (!snapshot.fileTimestamps.has(path)) { | 
					
						
							|  |  |  | 									// File is not shared and can't be removed from the snapshot
 | 
					
						
							|  |  |  | 									// because it's in a child of the snapshot
 | 
					
						
							|  |  |  | 									checkedOptimizationEntries.add(optimizationEntry); | 
					
						
							|  |  |  | 									continue capturedFiles; | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 								nonSharedFiles.add(path); | 
					
						
							|  |  |  | 								continue; | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						if (nonSharedFiles.size === 0) { | 
					
						
							|  |  |  | 							// The complete snapshot is shared
 | 
					
						
							|  |  |  | 							// add it as child
 | 
					
						
							|  |  |  | 							children.add(snapshot); | 
					
						
							|  |  |  | 							increaseSharedAndStoreOptimizationEntry(optimizationEntry); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							// Only a part of the snapshot is shared
 | 
					
						
							|  |  |  | 							// Extract common timestamps from both snapshots
 | 
					
						
							|  |  |  | 							const commonMap = new Map(); | 
					
						
							|  |  |  | 							for (const [path, ts] of snapshot.fileTimestamps) { | 
					
						
							|  |  |  | 								if (nonSharedFiles.has(path)) continue; | 
					
						
							|  |  |  | 								commonMap.set(path, ts); | 
					
						
							|  |  |  | 								snapshot.fileTimestamps.delete(path); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 							// Create and attach snapshot
 | 
					
						
							|  |  |  | 							/** @type {Snapshot} */ | 
					
						
							|  |  |  | 							const commonSnapshot = { fileTimestamps: commonMap }; | 
					
						
							|  |  |  | 							children.add(commonSnapshot); | 
					
						
							|  |  |  | 							if (!snapshot.children) snapshot.children = new Set(); | 
					
						
							|  |  |  | 							snapshot.children.add(commonSnapshot); | 
					
						
							|  |  |  | 							// Create optimization entry
 | 
					
						
							|  |  |  | 							const newEntry = { | 
					
						
							|  |  |  | 								snapshot: commonSnapshot, | 
					
						
							|  |  |  | 								shared: optimizationEntry.shared + 1, | 
					
						
							|  |  |  | 								snapshotContent: new Set(commonMap.keys()), | 
					
						
							|  |  |  | 								children: undefined | 
					
						
							|  |  |  | 							}; | 
					
						
							|  |  |  | 							if (optimizationEntry.children === undefined) | 
					
						
							|  |  |  | 								optimizationEntry.children = new Set(); | 
					
						
							|  |  |  | 							optimizationEntry.children.add(newEntry); | 
					
						
							|  |  |  | 							storeOptimizationEntry(newEntry); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						// It's a unshared snapshot
 | 
					
						
							|  |  |  | 						// We can extract a common shared snapshot
 | 
					
						
							|  |  |  | 						// with all common files
 | 
					
						
							|  |  |  | 						const commonMap = new Map(); | 
					
						
							|  |  |  | 						for (const path of capturedFiles) { | 
					
						
							|  |  |  | 							const ts = snapshot.fileTimestamps.get(path); | 
					
						
							|  |  |  | 							if (ts === undefined) continue; | 
					
						
							|  |  |  | 							commonMap.set(path, ts); | 
					
						
							|  |  |  | 							snapshot.fileTimestamps.delete(path); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						if (commonMap.size < 2) { | 
					
						
							|  |  |  | 							// Common part it too small
 | 
					
						
							|  |  |  | 							continue capturedFiles; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						// Create and attach snapshot
 | 
					
						
							|  |  |  | 						/** @type {Snapshot} */ | 
					
						
							|  |  |  | 						const commonSnapshot = { fileTimestamps: commonMap }; | 
					
						
							|  |  |  | 						children.add(commonSnapshot); | 
					
						
							|  |  |  | 						if (!snapshot.children) snapshot.children = new Set(); | 
					
						
							|  |  |  | 						snapshot.children.add(commonSnapshot); | 
					
						
							|  |  |  | 						// Remove files from snaphot
 | 
					
						
							|  |  |  | 						for (const path of commonMap.keys()) | 
					
						
							|  |  |  | 							snapshot.fileTimestamps.delete(path); | 
					
						
							|  |  |  | 						// Create optimization entry
 | 
					
						
							|  |  |  | 						storeOptimizationEntry({ | 
					
						
							|  |  |  | 							snapshot: commonSnapshot, | 
					
						
							|  |  |  | 							shared: 2, | 
					
						
							|  |  |  | 							snapshotContent: new Set(commonMap.keys()), | 
					
						
							|  |  |  | 							children: undefined | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				for (const path of capturedFiles) { | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					const cache = this._fileTimestamps.get(path); | 
					
						
							|  |  |  | 					if (cache !== undefined) { | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 						if (cache !== "ignore") { | 
					
						
							|  |  |  | 							fileTimestamps.set(path, cache); | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					} else { | 
					
						
							|  |  |  | 						jobs++; | 
					
						
							|  |  |  | 						this.fileTimestampQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 							if (err) { | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 								if (this.logger) { | 
					
						
							|  |  |  | 									this.logger.debug( | 
					
						
							|  |  |  | 										`Error snapshotting file timestamp of ${path}: ${err}` | 
					
						
							|  |  |  | 									); | 
					
						
							|  |  |  | 								} | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 								jobError(); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 							} else { | 
					
						
							|  |  |  | 								fileTimestamps.set(path, entry); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 								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 (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) { | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 								if (this.logger) { | 
					
						
							|  |  |  | 									this.logger.debug( | 
					
						
							|  |  |  | 										`Error snapshotting context hash of ${path}: ${err}` | 
					
						
							|  |  |  | 									); | 
					
						
							|  |  |  | 								} | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 								jobError(); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 							} else { | 
					
						
							|  |  |  | 								contextHashes.set(path, entry); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 								jobDone(); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 							} | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} 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) { | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 						if (cache !== "ignore") { | 
					
						
							|  |  |  | 							contextTimestamps.set(path, cache); | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 					} else { | 
					
						
							|  |  |  | 						jobs++; | 
					
						
							|  |  |  | 						this.contextTimestampQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 							if (err) { | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 								if (this.logger) { | 
					
						
							|  |  |  | 									this.logger.debug( | 
					
						
							|  |  |  | 										`Error snapshotting context timestamp of ${path}: ${err}` | 
					
						
							|  |  |  | 									); | 
					
						
							|  |  |  | 								} | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 								jobError(); | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 							} else { | 
					
						
							|  |  |  | 								contextTimestamps.set(path, entry); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 								jobDone(); | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 							} | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											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) { | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 					if (cache !== "ignore") { | 
					
						
							|  |  |  | 						missingExistance.set(path, toExistance(cache)); | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 				} else { | 
					
						
							|  |  |  | 					jobs++; | 
					
						
							|  |  |  | 					this.fileTimestampQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 						if (err) { | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 							if (this.logger) { | 
					
						
							|  |  |  | 								this.logger.debug( | 
					
						
							|  |  |  | 									`Error snapshotting missing timestamp of ${path}: ${err}` | 
					
						
							|  |  |  | 								); | 
					
						
							|  |  |  | 							} | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 							jobError(); | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 						} else { | 
					
						
							| 
									
										
										
										
											2019-11-14 23:49:45 +08:00
										 |  |  | 							missingExistance.set(path, toExistance(entry)); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 							jobDone(); | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 						} | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		for (const path of managedItems) { | 
					
						
							|  |  |  | 			const cache = this._managedItems.get(path); | 
					
						
							|  |  |  | 			if (cache !== undefined) { | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 				managedItemInfo.set(path, cache); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			} else { | 
					
						
							|  |  |  | 				jobs++; | 
					
						
							|  |  |  | 				this.managedItemQueue.add(path, (err, entry) => { | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 					if (err) { | 
					
						
							|  |  |  | 						if (this.logger) { | 
					
						
							|  |  |  | 							this.logger.debug( | 
					
						
							|  |  |  | 								`Error snapshotting managed item ${path}: ${err}` | 
					
						
							|  |  |  | 							); | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 						jobError(); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					} else { | 
					
						
							|  |  |  | 						managedItemInfo.set(path, entry); | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 						jobDone(); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		jobDone(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {Snapshot} snapshot1 a snapshot | 
					
						
							|  |  |  | 	 * @param {Snapshot} snapshot2 a snapshot | 
					
						
							|  |  |  | 	 * @returns {Snapshot} merged snapshot | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	mergeSnapshots(snapshot1, snapshot2) { | 
					
						
							|  |  |  | 		/** @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 | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-11-14 23:49:45 +08:00
										 |  |  | 		if (snapshot1.missingExistance || snapshot2.missingExistance) { | 
					
						
							|  |  |  | 			snapshot.missingExistance = mergeMaps( | 
					
						
							|  |  |  | 				snapshot1.missingExistance, | 
					
						
							|  |  |  | 				snapshot2.missingExistance | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (snapshot1.managedItemInfo || snapshot2.managedItemInfo) { | 
					
						
							|  |  |  | 			snapshot.managedItemInfo = mergeMaps( | 
					
						
							|  |  |  | 				snapshot1.managedItemInfo, | 
					
						
							|  |  |  | 				snapshot2.managedItemInfo | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-11-08 00:31:18 +08:00
										 |  |  | 		if ( | 
					
						
							|  |  |  | 			this._snapshotCache.get(snapshot1) === true && | 
					
						
							|  |  |  | 			this._snapshotCache.get(snapshot2) === true | 
					
						
							|  |  |  | 		) { | 
					
						
							|  |  |  | 			this._snapshotCache.set(snapshot, true); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		return snapshot; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {Snapshot} snapshot the snapshot made | 
					
						
							|  |  |  | 	 * @param {function(WebpackError=, boolean=): void} callback callback function | 
					
						
							|  |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 	checkSnapshotValid(snapshot, callback) { | 
					
						
							| 
									
										
										
										
											2019-10-27 14:38:06 +08:00
										 |  |  | 		const cachedResult = this._snapshotCache.get(snapshot); | 
					
						
							|  |  |  | 		if (cachedResult !== undefined) { | 
					
						
							|  |  |  | 			if (typeof cachedResult === "boolean") { | 
					
						
							|  |  |  | 				callback(null, cachedResult); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				cachedResult.push(callback); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-01-31 17:59:28 +08:00
										 |  |  | 		this._checkSnapshotValidNoCache(snapshot, callback); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	_checkSnapshotValidNoCache(snapshot, callback) { | 
					
						
							| 
									
										
										
										
											2019-11-01 19:16:30 +08:00
										 |  |  | 		let 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-11-14 23:49:45 +08:00
										 |  |  | 			missingExistance, | 
					
						
							| 
									
										
										
										
											2020-01-31 17:59:28 +08:00
										 |  |  | 			managedItemInfo, | 
					
						
							|  |  |  | 			children | 
					
						
							| 
									
										
										
										
											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); | 
					
						
							| 
									
										
										
										
											2019-11-01 19:16:30 +08:00
										 |  |  | 				callback(null, true); | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 		const invalid = () => { | 
					
						
							|  |  |  | 			if (jobs > 0) { | 
					
						
							| 
									
										
										
										
											2019-11-01 19:16:30 +08:00
										 |  |  | 				// large negative number instead of NaN or something else to keep jobs to stay a SMI (v8)
 | 
					
						
							|  |  |  | 				jobs = -100000000; | 
					
						
							| 
									
										
										
										
											2019-10-27 14:38:06 +08:00
										 |  |  | 				this._snapshotCache.set(snapshot, false); | 
					
						
							| 
									
										
										
										
											2019-11-01 19:16:30 +08:00
										 |  |  | 				callback(null, false); | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 		const invalidWithError = (path, err) => { | 
					
						
							|  |  |  | 			if (this._remainingLogs > 0) { | 
					
						
							| 
									
										
										
										
											2019-11-15 00:28:10 +08:00
										 |  |  | 				this._log(path, `error occured: %s`, err); | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			invalid(); | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 		/** | 
					
						
							|  |  |  | 		 * @param {string} path file path | 
					
						
							|  |  |  | 		 * @param {string} current current hash | 
					
						
							|  |  |  | 		 * @param {string} snap snapshot hash | 
					
						
							|  |  |  | 		 * @returns {boolean} true, if ok | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		const checkHash = (path, current, snap) => { | 
					
						
							|  |  |  | 			if (current !== snap) { | 
					
						
							|  |  |  | 				// If hash differ it's invalid
 | 
					
						
							|  |  |  | 				if (this._remainingLogs > 0) { | 
					
						
							| 
									
										
										
										
											2019-11-15 00:28:10 +08:00
										 |  |  | 					this._log(path, `hashes differ (%s != %s)`, current, snap); | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				return false; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return true; | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 		/** | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 		 * @param {string} path file path | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		 * @param {boolean} current current entry | 
					
						
							|  |  |  | 		 * @param {boolean} snap entry from snapshot | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 		 * @returns {boolean} true, if ok | 
					
						
							|  |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 		const checkExistance = (path, current, snap) => { | 
					
						
							|  |  |  | 			if (!current !== !snap) { | 
					
						
							|  |  |  | 				// If existance of item differs
 | 
					
						
							|  |  |  | 				// it's invalid
 | 
					
						
							|  |  |  | 				if (this._remainingLogs > 0) { | 
					
						
							|  |  |  | 					this._log( | 
					
						
							|  |  |  | 						path, | 
					
						
							|  |  |  | 						current ? "it didn't exist before" : "it does no longer exist" | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return false; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return true; | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 		/** | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 		 * @param {string} path file path | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 		 * @param {FileSystemInfoEntry} current current entry | 
					
						
							|  |  |  | 		 * @param {FileSystemInfoEntry} snap entry from snapshot | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 		 * @returns {boolean} true, if ok | 
					
						
							|  |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 		const checkFile = (path, current, snap) => { | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 			if (current === snap) return true; | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 			if (!current !== !snap) { | 
					
						
							|  |  |  | 				// If existance of item differs
 | 
					
						
							|  |  |  | 				// it's invalid
 | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 				if (this._remainingLogs > 0) { | 
					
						
							|  |  |  | 					this._log( | 
					
						
							|  |  |  | 						path, | 
					
						
							|  |  |  | 						current ? "it didn't exist before" : "it does no longer exist" | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 				return false; | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 			if (current) { | 
					
						
							|  |  |  | 				// For existing items only
 | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 				if (current.safeTime > startTime) { | 
					
						
							|  |  |  | 					// If a change happened after starting reading the item
 | 
					
						
							|  |  |  | 					// this may no longer be valid
 | 
					
						
							|  |  |  | 					if (this._remainingLogs > 0) { | 
					
						
							|  |  |  | 						this._log( | 
					
						
							|  |  |  | 							path, | 
					
						
							|  |  |  | 							`it may have changed (%d) after the start time of the snapshot (%d)`, | 
					
						
							|  |  |  | 							current.safeTime, | 
					
						
							|  |  |  | 							startTime | 
					
						
							|  |  |  | 						); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					return false; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 				if ( | 
					
						
							|  |  |  | 					snap.timestamp !== undefined && | 
					
						
							|  |  |  | 					current.timestamp !== snap.timestamp | 
					
						
							|  |  |  | 				) { | 
					
						
							|  |  |  | 					// If we have a timestamp (it was a file or symlink) and it differs from current timestamp
 | 
					
						
							|  |  |  | 					// it's invalid
 | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 					if (this._remainingLogs > 0) { | 
					
						
							|  |  |  | 						this._log( | 
					
						
							|  |  |  | 							path, | 
					
						
							| 
									
										
										
										
											2019-11-15 00:28:10 +08:00
										 |  |  | 							`timestamps differ (%d != %d)`, | 
					
						
							|  |  |  | 							current.timestamp, | 
					
						
							|  |  |  | 							snap.timestamp | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 						); | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 					return false; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 				if ( | 
					
						
							|  |  |  | 					snap.timestampHash !== undefined && | 
					
						
							|  |  |  | 					current.timestampHash !== snap.timestampHash | 
					
						
							|  |  |  | 				) { | 
					
						
							|  |  |  | 					// If we have a timestampHash (it was a directory) and it differs from current timestampHash
 | 
					
						
							|  |  |  | 					// it's invalid
 | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 					if (this._remainingLogs > 0) { | 
					
						
							|  |  |  | 						this._log( | 
					
						
							|  |  |  | 							path, | 
					
						
							| 
									
										
										
										
											2019-11-15 00:28:10 +08:00
										 |  |  | 							`timestamps hashes differ (%s != %s)`, | 
					
						
							|  |  |  | 							current.timestampHash, | 
					
						
							|  |  |  | 							snap.timestampHash | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 						); | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 					return false; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			return true; | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2020-01-31 17:59:28 +08:00
										 |  |  | 		if (children) { | 
					
						
							|  |  |  | 			const childCallback = (err, result) => { | 
					
						
							|  |  |  | 				if (err || !result) return invalid(); | 
					
						
							|  |  |  | 				else jobDone(); | 
					
						
							|  |  |  | 			}; | 
					
						
							|  |  |  | 			for (const child of children) { | 
					
						
							|  |  |  | 				const cache = this._snapshotCache.get(child); | 
					
						
							|  |  |  | 				if (cache !== undefined) { | 
					
						
							|  |  |  | 					if (cache !== undefined) { | 
					
						
							|  |  |  | 						if (typeof cache === "boolean") { | 
					
						
							|  |  |  | 							if (cache === false) { | 
					
						
							|  |  |  | 								invalid(); | 
					
						
							|  |  |  | 								return; | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							jobs++; | 
					
						
							|  |  |  | 							cache.push(childCallback); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					jobs++; | 
					
						
							|  |  |  | 					this._checkSnapshotValidNoCache(child, childCallback); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											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) { | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 					if (cache !== "ignore" && !checkFile(path, cache, ts)) { | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 						invalid(); | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 						return; | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					jobs++; | 
					
						
							|  |  |  | 					this.fileTimestampQueue.add(path, (err, entry) => { | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 						if (err) return invalidWithError(path, err); | 
					
						
							|  |  |  | 						if (!checkFile(path, entry, ts)) { | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 							invalid(); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					}); | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (fileHashes) { | 
					
						
							|  |  |  | 			for (const [path, hash] of fileHashes) { | 
					
						
							|  |  |  | 				const cache = this._fileHashes.get(path); | 
					
						
							|  |  |  | 				if (cache !== undefined) { | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 					if (cache !== "ignore" && !checkHash(path, cache, hash)) { | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 						invalid(); | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 						return; | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 				} else { | 
					
						
							|  |  |  | 					jobs++; | 
					
						
							|  |  |  | 					this.fileHashQueue.add(path, (err, entry) => { | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 						if (err) return invalidWithError(path, err); | 
					
						
							|  |  |  | 						if (!checkHash(path, entry, hash)) { | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 							invalid(); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		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) { | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 					if (cache !== "ignore" && !checkFile(path, cache, ts)) { | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 						invalid(); | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 						return; | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					jobs++; | 
					
						
							|  |  |  | 					this.contextTimestampQueue.add(path, (err, entry) => { | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 						if (err) return invalidWithError(path, err); | 
					
						
							|  |  |  | 						if (!checkFile(path, entry, ts)) { | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 							invalid(); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											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) { | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 					if (cache !== "ignore" && !checkHash(path, cache, hash)) { | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 						invalid(); | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 						return; | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					jobs++; | 
					
						
							|  |  |  | 					this.contextHashQueue.add(path, (err, entry) => { | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 						if (err) return invalidWithError(path, err); | 
					
						
							|  |  |  | 						if (!checkHash(path, entry, hash)) { | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 							invalid(); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					}); | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-11-14 23:49:45 +08:00
										 |  |  | 		if (missingExistance) { | 
					
						
							|  |  |  | 			for (const [path, existance] of missingExistance) { | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 				const cache = this._fileTimestamps.get(path); | 
					
						
							|  |  |  | 				if (cache !== undefined) { | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 					if ( | 
					
						
							|  |  |  | 						cache !== "ignore" && | 
					
						
							|  |  |  | 						!checkExistance(path, toExistance(cache), existance) | 
					
						
							|  |  |  | 					) { | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 						invalid(); | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 						return; | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 				} else { | 
					
						
							|  |  |  | 					jobs++; | 
					
						
							|  |  |  | 					this.fileTimestampQueue.add(path, (err, entry) => { | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 						if (err) return invalidWithError(path, err); | 
					
						
							| 
									
										
										
										
											2019-11-14 23:49:45 +08:00
										 |  |  | 						if (!checkExistance(path, toExistance(entry), existance)) { | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 							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) { | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 					if (!checkHash(path, cache, info)) { | 
					
						
							| 
									
										
										
										
											2019-08-13 23:26:54 +08:00
										 |  |  | 						invalid(); | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 						return; | 
					
						
							| 
									
										
										
										
											2019-08-13 23:26:54 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					jobs++; | 
					
						
							|  |  |  | 					this.managedItemQueue.add(path, (err, entry) => { | 
					
						
							| 
									
										
										
										
											2019-11-04 17:06:53 +08:00
										 |  |  | 						if (err) return invalidWithError(path, err); | 
					
						
							|  |  |  | 						if (!checkHash(path, entry, info)) { | 
					
						
							| 
									
										
										
										
											2019-08-13 23:26:54 +08:00
										 |  |  | 							invalid(); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		jobDone(); | 
					
						
							| 
									
										
										
										
											2019-11-01 19:16:30 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// if there was an async action
 | 
					
						
							|  |  |  | 		// try to join multiple concurrent request for this snapshot
 | 
					
						
							|  |  |  | 		if (jobs > 0) { | 
					
						
							|  |  |  | 			callbacks = [callback]; | 
					
						
							|  |  |  | 			callback = (err, result) => { | 
					
						
							|  |  |  | 				for (const callback of callbacks) callback(err, result); | 
					
						
							|  |  |  | 			}; | 
					
						
							|  |  |  | 			this._snapshotCache.set(snapshot, callbacks); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	_readFileTimestamp(path, callback) { | 
					
						
							|  |  |  | 		this.fs.stat(path, (err, stat) => { | 
					
						
							|  |  |  | 			if (err) { | 
					
						
							|  |  |  | 				if (err.code === "ENOENT") { | 
					
						
							|  |  |  | 					this._fileTimestamps.set(path, null); | 
					
						
							|  |  |  | 					return callback(null, null); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return callback(err); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-14 23:49:14 +08:00
										 |  |  | 			let ts; | 
					
						
							|  |  |  | 			if (stat.isDirectory()) { | 
					
						
							|  |  |  | 				ts = { | 
					
						
							|  |  |  | 					safeTime: 0, | 
					
						
							|  |  |  | 					timestamp: undefined | 
					
						
							|  |  |  | 				}; | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				const mtime = +stat.mtime; | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-14 23:49:14 +08:00
										 |  |  | 				if (mtime) applyMtime(mtime); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-14 23:49:14 +08:00
										 |  |  | 				ts = { | 
					
						
							|  |  |  | 					safeTime: mtime ? mtime + FS_ACCURACY : Infinity, | 
					
						
							|  |  |  | 					timestamp: mtime | 
					
						
							|  |  |  | 				}; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			this._fileTimestamps.set(path, ts); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			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
 | 
					
						
							| 
									
										
										
										
											2019-11-06 07:27:47 +08:00
										 |  |  | 									return this.managedItemQueue.add(managedItem, (err, info) => { | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 										if (err) return callback(err); | 
					
						
							|  |  |  | 										return callback(null, { | 
					
						
							|  |  |  | 											safeTime: 0, | 
					
						
							|  |  |  | 											timestampHash: info | 
					
						
							|  |  |  | 										}); | 
					
						
							|  |  |  | 									}); | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 						if (stat.isFile()) { | 
					
						
							|  |  |  | 							return this.getFileTimestamp(child, callback); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						if (stat.isDirectory()) { | 
					
						
							|  |  |  | 							this.contextTimestampQueue.increaseParallelism(); | 
					
						
							|  |  |  | 							this.getContextTimestamp(child, (err, tsEntry) => { | 
					
						
							|  |  |  | 								this.contextTimestampQueue.decreaseParallelism(); | 
					
						
							|  |  |  | 								callback(err, tsEntry); | 
					
						
							|  |  |  | 							}); | 
					
						
							|  |  |  | 							return; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						callback(null, null); | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				(err, tsEntries) => { | 
					
						
							| 
									
										
										
										
											2019-09-08 21:53:12 +08:00
										 |  |  | 					if (err) return callback(err); | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 					const hash = createHash("md4"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					for (const file of files) hash.update(file); | 
					
						
							|  |  |  | 					let safeTime = 0; | 
					
						
							|  |  |  | 					for (const entry of tsEntries) { | 
					
						
							|  |  |  | 						if (!entry) { | 
					
						
							|  |  |  | 							hash.update("n"); | 
					
						
							|  |  |  | 							continue; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						if (entry.timestamp) { | 
					
						
							|  |  |  | 							hash.update("f"); | 
					
						
							|  |  |  | 							hash.update(`${entry.timestamp}`); | 
					
						
							|  |  |  | 						} else if (entry.timestampHash) { | 
					
						
							|  |  |  | 							hash.update("d"); | 
					
						
							|  |  |  | 							hash.update(`${entry.timestampHash}`); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						if (entry.safeTime) { | 
					
						
							|  |  |  | 							safeTime = Math.max(safeTime, entry.safeTime); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					const digest = /** @type {string} */ (hash.digest("hex")); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					const result = { | 
					
						
							|  |  |  | 						safeTime, | 
					
						
							|  |  |  | 						timestampHash: digest | 
					
						
							|  |  |  | 					}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					this._contextTimestamps.set(path, result); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					callback(null, result); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 	_readContextHash(path, callback) { | 
					
						
							|  |  |  | 		this.fs.readdir(path, (err, files) => { | 
					
						
							|  |  |  | 			if (err) { | 
					
						
							|  |  |  | 				if (err.code === "ENOENT") { | 
					
						
							|  |  |  | 					this._contextHashes.set(path, null); | 
					
						
							|  |  |  | 					return callback(null, null); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return callback(err); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			files = files | 
					
						
							|  |  |  | 				.map(file => file.normalize("NFC")) | 
					
						
							|  |  |  | 				.filter(file => !/^\./.test(file)) | 
					
						
							|  |  |  | 				.sort(); | 
					
						
							|  |  |  | 			asyncLib.map( | 
					
						
							|  |  |  | 				files, | 
					
						
							|  |  |  | 				(file, callback) => { | 
					
						
							|  |  |  | 					const child = join(this.fs, path, file); | 
					
						
							|  |  |  | 					this.fs.stat(child, (err, stat) => { | 
					
						
							|  |  |  | 						if (err) return callback(err); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 						for (const immutablePath of this.immutablePathsWithSlash) { | 
					
						
							|  |  |  | 							if (path.startsWith(immutablePath)) { | 
					
						
							|  |  |  | 								// ignore any immutable path for hashing
 | 
					
						
							|  |  |  | 								return callback(null, ""); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						for (const managedPath of this.managedPathsWithSlash) { | 
					
						
							|  |  |  | 							if (path.startsWith(managedPath)) { | 
					
						
							|  |  |  | 								const managedItem = getManagedItem(managedPath, child); | 
					
						
							|  |  |  | 								if (managedItem) { | 
					
						
							|  |  |  | 									// construct hash from managed info
 | 
					
						
							| 
									
										
										
										
											2019-11-06 07:27:47 +08:00
										 |  |  | 									return this.managedItemQueue.add(managedItem, (err, info) => { | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 										if (err) return callback(err); | 
					
						
							|  |  |  | 										callback(null, info || ""); | 
					
						
							|  |  |  | 									}); | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 						if (stat.isFile()) { | 
					
						
							| 
									
										
										
										
											2019-09-08 21:35:53 +08:00
										 |  |  | 							return this.getFileHash(child, (err, hash) => { | 
					
						
							|  |  |  | 								callback(err, hash || ""); | 
					
						
							|  |  |  | 							}); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 						} | 
					
						
							|  |  |  | 						if (stat.isDirectory()) { | 
					
						
							|  |  |  | 							this.contextHashQueue.increaseParallelism(); | 
					
						
							|  |  |  | 							this.getContextHash(child, (err, hash) => { | 
					
						
							|  |  |  | 								this.contextHashQueue.decreaseParallelism(); | 
					
						
							|  |  |  | 								callback(err, hash || ""); | 
					
						
							|  |  |  | 							}); | 
					
						
							|  |  |  | 							return; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						callback(null, ""); | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				(err, fileHashes) => { | 
					
						
							| 
									
										
										
										
											2019-09-08 21:53:12 +08:00
										 |  |  | 					if (err) return callback(err); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					const hash = createHash("md4"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					for (const file of files) hash.update(file); | 
					
						
							|  |  |  | 					for (const h of fileHashes) hash.update(h); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					const digest = /** @type {string} */ (hash.digest("hex")); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					this._contextHashes.set(path, digest); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					callback(null, digest); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 	_getManagedItemDirectoryInfo(path, callback) { | 
					
						
							|  |  |  | 		this.fs.readdir(path, (err, elements) => { | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 			if (err) { | 
					
						
							| 
									
										
										
										
											2019-09-09 03:30:27 +08:00
										 |  |  | 				if (err.code === "ENOENT" || err.code === "ENOTDIR") { | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 					return callback(null, EMPTY_SET); | 
					
						
							| 
									
										
										
										
											2019-09-04 19:34:34 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				return callback(err); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 			const set = new Set( | 
					
						
							|  |  |  | 				elements.map(element => join(this.fs, path, element)) | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 			callback(null, set); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	_getManagedItemInfo(path, callback) { | 
					
						
							|  |  |  | 		const dir = dirname(this.fs, path); | 
					
						
							|  |  |  | 		this.managedItemDirectoryQueue.add(dir, (err, elements) => { | 
					
						
							|  |  |  | 			if (err) { | 
					
						
							|  |  |  | 				return callback(err); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if (!elements.has(path)) { | 
					
						
							|  |  |  | 				// file or directory doesn't exist
 | 
					
						
							|  |  |  | 				this._managedItems.set(path, "missing"); | 
					
						
							|  |  |  | 				return callback(null, "missing"); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// something exists
 | 
					
						
							|  |  |  | 			// it may be a file or directory
 | 
					
						
							|  |  |  | 			if ( | 
					
						
							|  |  |  | 				path.endsWith("node_modules") && | 
					
						
							|  |  |  | 				(path.endsWith("/node_modules") || path.endsWith("\\node_modules")) | 
					
						
							|  |  |  | 			) { | 
					
						
							|  |  |  | 				// we are only interested in existance of this special directory
 | 
					
						
							|  |  |  | 				this._managedItems.set(path, "exists"); | 
					
						
							|  |  |  | 				return callback(null, "exists"); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// we assume it's a directory, as files shouldn't occur in managed paths
 | 
					
						
							|  |  |  | 			const packageJsonPath = join(this.fs, path, "package.json"); | 
					
						
							|  |  |  | 			this.fs.readFile(packageJsonPath, (err, content) => { | 
					
						
							|  |  |  | 				if (err) { | 
					
						
							|  |  |  | 					if (err.code === "ENOENT" || err.code === "ENOTDIR") { | 
					
						
							|  |  |  | 						// no package.json or path is not a directory
 | 
					
						
							| 
									
										
										
										
											2020-01-28 21:01:19 +08:00
										 |  |  | 						const problem = `Managed item ${path} isn't a directory or doesn't contain a package.json`; | 
					
						
							|  |  |  | 						this.logger.warn(problem); | 
					
						
							|  |  |  | 						return callback(new Error(problem)); | 
					
						
							| 
									
										
										
										
											2019-11-05 23:47:45 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 					return callback(err); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				let data; | 
					
						
							|  |  |  | 				try { | 
					
						
							|  |  |  | 					data = JSON.parse(content.toString("utf-8")); | 
					
						
							|  |  |  | 				} catch (e) { | 
					
						
							|  |  |  | 					return callback(e); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				const info = `${data.name || ""}@${data.version || ""}`; | 
					
						
							|  |  |  | 				this._managedItems.set(path, info); | 
					
						
							|  |  |  | 				callback(null, info); | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	getDeprecatedFileTimestamps() { | 
					
						
							|  |  |  | 		const map = new Map(); | 
					
						
							|  |  |  | 		for (const [path, info] of this._fileTimestamps) { | 
					
						
							| 
									
										
										
										
											2019-11-06 06:46:34 +08:00
										 |  |  | 			if (info) map.set(path, typeof info === "object" ? info.safeTime : null); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		return map; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	getDeprecatedContextTimestamps() { | 
					
						
							|  |  |  | 		const map = new Map(); | 
					
						
							|  |  |  | 		for (const [path, info] of this._contextTimestamps) { | 
					
						
							| 
									
										
										
										
											2019-11-06 06:46:34 +08:00
										 |  |  | 			if (info) map.set(path, typeof info === "object" ? info.safeTime : null); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		return map; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports = FileSystemInfo; |