| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | /* | 
					
						
							|  |  |  | 	MIT License http://www.opensource.org/licenses/mit-license.php
 | 
					
						
							|  |  |  | 	Author Tobias Koppers @sokra | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | "use strict"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | const resolve = require("enhanced-resolve"); | 
					
						
							|  |  |  | const asyncLib = require("neo-async"); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | const AsyncQueue = require("./util/AsyncQueue"); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | const createHash = require("./util/createHash"); | 
					
						
							|  |  |  | const { join, dirname } = require("./util/fs"); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | /** @typedef {import("./WebpackError")} WebpackError */ | 
					
						
							|  |  |  | /** @typedef {import("./util/fs").InputFileSystem} InputFileSystem */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | const resolveContext = resolve.create({ | 
					
						
							|  |  |  | 	resolveToContext: true | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | let FS_ACCURACY = 2000; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-13 23:21:19 +08:00
										 |  |  | const RBDT_RESOLVE = 0; | 
					
						
							|  |  |  | const RBDT_RESOLVE_DIRECTORY = 1; | 
					
						
							|  |  |  | const RBDT_DIRECTORY = 2; | 
					
						
							|  |  |  | const RBDT_FILE = 3; | 
					
						
							|  |  |  | const RBDT_DIRECTORY_DEPENDENCIES = 4; | 
					
						
							|  |  |  | const RBDT_FILE_DEPENDENCIES = 5; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  |  * @property {number} timestamp | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @typedef {Object} Snapshot | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  |  * @property {number=} startTime | 
					
						
							|  |  |  |  * @property {Map<string, FileSystemInfoEntry | "error">=} fileTimestamps | 
					
						
							|  |  |  |  * @property {Map<string, string | "error">=} fileHashes | 
					
						
							|  |  |  |  * @property {Map<string, FileSystemInfoEntry | "error">=} contextTimestamps | 
					
						
							|  |  |  |  * @property {Map<string, string | "error">=} contextHashes | 
					
						
							|  |  |  |  * @property {Map<string, FileSystemInfoEntry | "error">=} missingTimestamps | 
					
						
							|  |  |  |  * @property {Map<string, string | "error">=} managedItemInfo | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | /* istanbul ignore next */ | 
					
						
							|  |  |  | 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-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; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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-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; | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			case 64: // @
 | 
					
						
							|  |  |  | 				slashes++; | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		i++; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-08-13 23:58:51 +08:00
										 |  |  | 	// if (path.slice(i + 1, i + 13) === "node_modules")
 | 
					
						
							|  |  |  | 	if ( | 
					
						
							|  |  |  | 		path.charCodeAt(i + 1) === 110 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 2) === 111 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 3) === 100 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 4) === 101 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 5) === 95 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 6) === 109 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 7) === 111 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 8) === 100 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 9) === 117 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 10) === 108 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 11) === 101 && | 
					
						
							|  |  |  | 		path.charCodeAt(i + 12) === 115 | 
					
						
							|  |  |  | 	) { | 
					
						
							|  |  |  | 		const c = path.charCodeAt(i + 13); | 
					
						
							|  |  |  | 		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
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | class FileSystemInfo { | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {InputFileSystem} fs file system | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 	 * @param {Object} options options | 
					
						
							|  |  |  | 	 * @param {Iterable<string>=} options.managedPaths paths that are only managed by a package manager | 
					
						
							| 
									
										
										
										
											2019-08-16 17:55:10 +08:00
										 |  |  | 	 * @param {Iterable<string>=} options.immutablePaths paths that are immutable | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2019-08-16 17:55:10 +08:00
										 |  |  | 	constructor(fs, { managedPaths = [], immutablePaths = [] } = {}) { | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		this.fs = fs; | 
					
						
							| 
									
										
										
										
											2019-01-05 02:17:37 +08:00
										 |  |  | 		/** @type {Map<string, FileSystemInfoEntry | null>} */ | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		this._fileTimestamps = new Map(); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		/** @type {Map<string, string>} */ | 
					
						
							|  |  |  | 		this._fileHashes = new Map(); | 
					
						
							| 
									
										
										
										
											2019-01-05 02:17:37 +08:00
										 |  |  | 		/** @type {Map<string, FileSystemInfoEntry | null>} */ | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		this._contextTimestamps = new Map(); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		/** @type {Map<string, string>} */ | 
					
						
							|  |  |  | 		this._contextHashes = new Map(); | 
					
						
							|  |  |  | 		/** @type {Map<string, string>} */ | 
					
						
							|  |  |  | 		this._managedItems = new Map(); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		this.fileTimestampQueue = new AsyncQueue({ | 
					
						
							|  |  |  | 			name: "file timestamp", | 
					
						
							|  |  |  | 			parallelism: 30, | 
					
						
							|  |  |  | 			processor: this._readFileTimestamp.bind(this) | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		this.fileHashQueue = new AsyncQueue({ | 
					
						
							|  |  |  | 			name: "file hash", | 
					
						
							|  |  |  | 			parallelism: 10, | 
					
						
							|  |  |  | 			processor: this._readFileHash.bind(this) | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		this.contextTimestampQueue = new AsyncQueue({ | 
					
						
							|  |  |  | 			name: "context timestamp", | 
					
						
							|  |  |  | 			parallelism: 2, | 
					
						
							|  |  |  | 			processor: this._readContextTimestamp.bind(this) | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		this.contextHashQueue = new AsyncQueue({ | 
					
						
							|  |  |  | 			name: "context hash", | 
					
						
							|  |  |  | 			parallelism: 2, | 
					
						
							|  |  |  | 			processor: this._readContextHash.bind(this) | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 		this.managedItemQueue = new AsyncQueue({ | 
					
						
							|  |  |  | 			name: "managed item info", | 
					
						
							|  |  |  | 			parallelism: 10, | 
					
						
							|  |  |  | 			processor: this._getManagedItemInfo.bind(this) | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 		this.managedPaths = Array.from(managedPaths); | 
					
						
							|  |  |  | 		this.managedPathsWithSlash = this.managedPaths.map(p => | 
					
						
							|  |  |  | 			join(fs, p, "_").slice(0, -1) | 
					
						
							|  |  |  | 		); | 
					
						
							| 
									
										
										
										
											2019-08-16 17:55:10 +08:00
										 |  |  | 		this.immutablePaths = Array.from(immutablePaths); | 
					
						
							|  |  |  | 		this.immutablePathsWithSlash = this.immutablePaths.map(p => | 
					
						
							|  |  |  | 			join(fs, p, "_").slice(0, -1) | 
					
						
							|  |  |  | 		); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							| 
									
										
										
										
											2019-01-05 02:17:37 +08:00
										 |  |  | 	 * @param {Map<string, FileSystemInfoEntry | null>} map timestamps | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	addFileTimestamps(map) { | 
					
						
							|  |  |  | 		for (const [path, ts] of map) { | 
					
						
							|  |  |  | 			this._fileTimestamps.set(path, ts); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							| 
									
										
										
										
											2019-01-05 02:17:37 +08:00
										 |  |  | 	 * @param {Map<string, FileSystemInfoEntry | null>} map timestamps | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	addContextTimestamps(map) { | 
					
						
							|  |  |  | 		for (const [path, ts] of map) { | 
					
						
							|  |  |  | 			this._contextTimestamps.set(path, ts); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {string} path file path | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 	 * @param {function(WebpackError=, FileSystemInfoEntry=): void} callback callback function | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	getFileTimestamp(path, callback) { | 
					
						
							|  |  |  | 		const cache = this._fileTimestamps.get(path); | 
					
						
							| 
									
										
										
										
											2018-09-28 03:28:07 +08:00
										 |  |  | 		if (cache !== undefined) return callback(null, cache); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		this.fileTimestampQueue.add(path, callback); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {string} path context path | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 	 * @param {function(WebpackError=, FileSystemInfoEntry=): void} callback callback function | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	getContextTimestamp(path, callback) { | 
					
						
							|  |  |  | 		const cache = this._contextTimestamps.get(path); | 
					
						
							| 
									
										
										
										
											2018-09-28 03:28:07 +08:00
										 |  |  | 		if (cache !== undefined) return callback(null, cache); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 		this.contextTimestampQueue.add(path, callback); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {string} path file path | 
					
						
							|  |  |  | 	 * @param {function(WebpackError=, string=): void} callback callback function | 
					
						
							|  |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	getFileHash(path, callback) { | 
					
						
							|  |  |  | 		const cache = this._fileHashes.get(path); | 
					
						
							|  |  |  | 		if (cache !== undefined) return callback(null, cache); | 
					
						
							|  |  |  | 		this.fileHashQueue.add(path, callback); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {string} path context path | 
					
						
							|  |  |  | 	 * @param {function(WebpackError=, string=): void} callback callback function | 
					
						
							|  |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	getContextHash(path, callback) { | 
					
						
							|  |  |  | 		const cache = this._contextHashes.get(path); | 
					
						
							|  |  |  | 		if (cache !== undefined) return callback(null, cache); | 
					
						
							|  |  |  | 		this.contextHashQueue.add(path, callback); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	resolveBuildDependencies(context, deps, callback) { | 
					
						
							|  |  |  | 		const files = new Set(); | 
					
						
							|  |  |  | 		const directories = new Set(); | 
					
						
							|  |  |  | 		const missing = new Set(); | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | 		const resolveFiles = new Set(); | 
					
						
							|  |  |  | 		const resolveDirectories = new Set(); | 
					
						
							|  |  |  | 		const resolveMissing = new Set(); | 
					
						
							|  |  |  | 		const resolveResults = new Map(); | 
					
						
							| 
									
										
										
										
											2019-08-13 23:21:19 +08:00
										 |  |  | 		/** @type {asyncLib.QueueObject<{type: number, path: string, context?: string }, Error>} */ | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		const queue = asyncLib.queue(({ type, context, path }, callback) => { | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | 			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(); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				); | 
					
						
							|  |  |  | 			}; | 
					
						
							|  |  |  | 			const resolveFile = path => { | 
					
						
							|  |  |  | 				const key = `f\n${context}\n${path}`; | 
					
						
							|  |  |  | 				if (resolveResults.has(key)) { | 
					
						
							|  |  |  | 					return callback(); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				resolve( | 
					
						
							|  |  |  | 					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_FILE, | 
					
						
							|  |  |  | 							path: result | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 						callback(); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				); | 
					
						
							|  |  |  | 			}; | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			switch (type) { | 
					
						
							| 
									
										
										
										
											2019-08-13 23:21:19 +08:00
										 |  |  | 				case RBDT_RESOLVE: { | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					const isDirectory = /[\\/]$/.test(path); | 
					
						
							|  |  |  | 					if (isDirectory) { | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | 						resolveDirectory(path.slice(0, path.length - 1)); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					} else { | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | 						resolveFile(path); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-08-13 23:21:19 +08:00
										 |  |  | 				case RBDT_RESOLVE_DIRECTORY: { | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | 					resolveDirectory(path); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					break; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-08-13 23:21:19 +08:00
										 |  |  | 				case RBDT_FILE: { | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					if (files.has(path)) { | 
					
						
							|  |  |  | 						callback(); | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					this.fs.realpath(path, (err, realPath) => { | 
					
						
							|  |  |  | 						if (err) return callback(err); | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | 						if (realPath !== path) { | 
					
						
							|  |  |  | 							resolveFiles.add(path); | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 						if (!files.has(realPath)) { | 
					
						
							|  |  |  | 							files.add(realPath); | 
					
						
							|  |  |  | 							queue.push({ | 
					
						
							| 
									
										
										
										
											2019-08-13 23:21:19 +08:00
										 |  |  | 								type: RBDT_FILE_DEPENDENCIES, | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 								path: realPath | 
					
						
							|  |  |  | 							}); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						callback(); | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-08-13 23:21:19 +08:00
										 |  |  | 				case RBDT_DIRECTORY: { | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					if (directories.has(path)) { | 
					
						
							|  |  |  | 						callback(); | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					this.fs.realpath(path, (err, realPath) => { | 
					
						
							|  |  |  | 						if (err) return callback(err); | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | 						if (realPath !== path) { | 
					
						
							|  |  |  | 							resolveFiles.add(path); | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 						if (!directories.has(realPath)) { | 
					
						
							|  |  |  | 							directories.add(realPath); | 
					
						
							|  |  |  | 							queue.push({ | 
					
						
							| 
									
										
										
										
											2019-08-13 23:21:19 +08:00
										 |  |  | 								type: RBDT_DIRECTORY_DEPENDENCIES, | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 								path: realPath | 
					
						
							|  |  |  | 							}); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						callback(); | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-08-13 23:21:19 +08:00
										 |  |  | 				case RBDT_FILE_DEPENDENCIES: { | 
					
						
							|  |  |  | 					/** @type {NodeModule} */ | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					const module = require.cache[path]; | 
					
						
							|  |  |  | 					if (module && Array.isArray(module.children)) { | 
					
						
							|  |  |  | 						for (const child of module.children) { | 
					
						
							|  |  |  | 							if (child.id) { | 
					
						
							|  |  |  | 								queue.push({ | 
					
						
							| 
									
										
										
										
											2019-08-13 23:21:19 +08:00
										 |  |  | 									type: RBDT_FILE, | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 									path: child.id | 
					
						
							|  |  |  | 								}); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} 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({ | 
					
						
							| 
									
										
										
										
											2019-08-13 23:21:19 +08:00
										 |  |  | 							type: RBDT_DIRECTORY, | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 							path: directory | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					callback(); | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-08-13 23:21:19 +08:00
										 |  |  | 				case RBDT_DIRECTORY_DEPENDENCIES: { | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					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") { | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | 								resolveMissing.add(packageJson); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 								const parent = dirname(this.fs, packagePath); | 
					
						
							|  |  |  | 								if (parent !== packagePath) { | 
					
						
							|  |  |  | 									queue.push({ | 
					
						
							| 
									
										
										
										
											2019-08-13 23:21:19 +08:00
										 |  |  | 										type: RBDT_DIRECTORY_DEPENDENCIES, | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 										path: parent | 
					
						
							|  |  |  | 									}); | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 								callback(); | 
					
						
							|  |  |  | 								return; | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 							return callback(err); | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:49 +08:00
										 |  |  | 						resolveFiles.add(packageJson); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 						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({ | 
					
						
							| 
									
										
										
										
											2019-08-13 23:21:19 +08:00
										 |  |  | 									type: RBDT_RESOLVE_DIRECTORY, | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 									context: packagePath, | 
					
						
							|  |  |  | 									path: dep | 
					
						
							|  |  |  | 								}); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						callback(); | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}, 50); | 
					
						
							|  |  |  | 		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 = () => {}; | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 		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-08-20 03:57:49 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {Map<string, string>} resolveResults results from resolving | 
					
						
							|  |  |  | 	 * @param {function(Error=, boolean=): void} callback callback with true when resolveResults resolve the same way | 
					
						
							|  |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	checkResolveResultsValid(resolveResults, callback) { | 
					
						
							|  |  |  | 		asyncLib.eachLimit( | 
					
						
							|  |  |  | 			resolveResults, | 
					
						
							|  |  |  | 			20, | 
					
						
							|  |  |  | 			([key, expectedResult], callback) => { | 
					
						
							|  |  |  | 				const [type, context, path] = key.split("\n"); | 
					
						
							|  |  |  | 				switch (type) { | 
					
						
							|  |  |  | 					case "d": | 
					
						
							|  |  |  | 						resolveContext(context, path, (err, result) => { | 
					
						
							|  |  |  | 							if (err) return callback(err); | 
					
						
							|  |  |  | 							if (result !== expectedResult) return callback(INVALID); | 
					
						
							|  |  |  | 							callback(); | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 					case "f": | 
					
						
							|  |  |  | 						resolve(context, path, (err, result) => { | 
					
						
							|  |  |  | 							if (err) return callback(err); | 
					
						
							|  |  |  | 							if (result !== expectedResult) return callback(INVALID); | 
					
						
							|  |  |  | 							callback(); | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 					default: | 
					
						
							|  |  |  | 						callback(new Error("Unexpected type in resolve result key")); | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			/** | 
					
						
							|  |  |  | 			 * @param {Error | typeof INVALID=} err error or invalid flag | 
					
						
							|  |  |  | 			 * @returns {void} | 
					
						
							|  |  |  | 			 */ | 
					
						
							|  |  |  | 			err => { | 
					
						
							|  |  |  | 				if (err === INVALID) { | 
					
						
							|  |  |  | 					return callback(null, false); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if (err) { | 
					
						
							|  |  |  | 					return callback(err); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return callback(null, true); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * @param {number} startTime when processing the files has started | 
					
						
							|  |  |  | 	 * @param {Iterable<string>} files all files | 
					
						
							|  |  |  | 	 * @param {Iterable<string>} directories all directories | 
					
						
							|  |  |  | 	 * @param {Iterable<string>} missing all missing files or directories | 
					
						
							|  |  |  | 	 * @param {Object} options options object (for future extensions) | 
					
						
							|  |  |  | 	 * @param {function(WebpackError=, Snapshot=): void} callback callback function | 
					
						
							|  |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 	createSnapshot(startTime, files, directories, missing, options, callback) { | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 		/** @type {Map<string, FileSystemInfoEntry | "error">} */ | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		const fileTimestamps = new Map(); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		/** @type {Map<string, string | "error">} */ | 
					
						
							|  |  |  | 		const fileHashes = new Map(); | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 		/** @type {Map<string, FileSystemInfoEntry | "error">} */ | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		const contextTimestamps = new Map(); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		/** @type {Map<string, string | "error">} */ | 
					
						
							|  |  |  | 		const contextHashes = new Map(); | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 		/** @type {Map<string, FileSystemInfoEntry | "error">} */ | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		const missingTimestamps = new Map(); | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		/** @type {Map<string, string | "error">} */ | 
					
						
							|  |  |  | 		const managedItemInfo = new Map(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		const managedItems = new Set(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		let jobs = 1; | 
					
						
							|  |  |  | 		const jobDone = () => { | 
					
						
							|  |  |  | 			if (--jobs === 0) { | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 				const snapshot = {}; | 
					
						
							|  |  |  | 				if (startTime) snapshot.startTime = startTime; | 
					
						
							|  |  |  | 				if (fileTimestamps.size !== 0) snapshot.fileTimestamps = fileTimestamps; | 
					
						
							|  |  |  | 				if (fileHashes.size !== 0) snapshot.fileHashes = fileHashes; | 
					
						
							|  |  |  | 				if (contextTimestamps.size !== 0) | 
					
						
							|  |  |  | 					snapshot.contextTimestamps = contextTimestamps; | 
					
						
							|  |  |  | 				if (contextHashes.size !== 0) snapshot.contextHashes = contextHashes; | 
					
						
							|  |  |  | 				if (missingTimestamps.size !== 0) | 
					
						
							|  |  |  | 					snapshot.missingTimestamps = missingTimestamps; | 
					
						
							|  |  |  | 				if (managedItemInfo.size !== 0) | 
					
						
							|  |  |  | 					snapshot.managedItemInfo = managedItemInfo; | 
					
						
							|  |  |  | 				callback(null, snapshot); | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 		if (files) { | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			if (options && options.hash) { | 
					
						
							|  |  |  | 				files: for (const path of files) { | 
					
						
							| 
									
										
										
										
											2019-08-16 17:55:10 +08:00
										 |  |  | 					for (const immutablePath of this.immutablePathsWithSlash) { | 
					
						
							|  |  |  | 						if (path.startsWith(immutablePath)) { | 
					
						
							|  |  |  | 							continue files; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					for (const managedPath of this.managedPathsWithSlash) { | 
					
						
							|  |  |  | 						if (path.startsWith(managedPath)) { | 
					
						
							|  |  |  | 							managedItems.add(getManagedItem(managedPath, path)); | 
					
						
							|  |  |  | 							continue files; | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 					const cache = this._fileHashes.get(path); | 
					
						
							|  |  |  | 					if (cache !== undefined) { | 
					
						
							|  |  |  | 						fileHashes.set(path, cache); | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						jobs++; | 
					
						
							|  |  |  | 						this.fileHashQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 							if (err) { | 
					
						
							|  |  |  | 								fileHashes.set(path, "error"); | 
					
						
							|  |  |  | 							} else { | 
					
						
							|  |  |  | 								fileHashes.set(path, entry); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				files: for (const path of files) { | 
					
						
							| 
									
										
										
										
											2019-08-16 17:55:10 +08:00
										 |  |  | 					for (const immutablePath of this.immutablePathsWithSlash) { | 
					
						
							|  |  |  | 						if (path.startsWith(immutablePath)) { | 
					
						
							|  |  |  | 							continue files; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					for (const managedPath of this.managedPathsWithSlash) { | 
					
						
							|  |  |  | 						if (path.startsWith(managedPath)) { | 
					
						
							|  |  |  | 							managedItems.add(getManagedItem(managedPath, path)); | 
					
						
							|  |  |  | 							continue files; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					const cache = this._fileTimestamps.get(path); | 
					
						
							|  |  |  | 					if (cache !== undefined) { | 
					
						
							|  |  |  | 						fileTimestamps.set(path, cache); | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						jobs++; | 
					
						
							|  |  |  | 						this.fileTimestampQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 							if (err) { | 
					
						
							|  |  |  | 								fileTimestamps.set(path, "error"); | 
					
						
							|  |  |  | 							} else { | 
					
						
							|  |  |  | 								fileTimestamps.set(path, entry); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 		if (directories) { | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			if (options && options.hash) { | 
					
						
							|  |  |  | 				directories: for (const path of directories) { | 
					
						
							| 
									
										
										
										
											2019-08-16 17:55:10 +08:00
										 |  |  | 					for (const immutablePath of this.immutablePathsWithSlash) { | 
					
						
							|  |  |  | 						if (path.startsWith(immutablePath)) { | 
					
						
							|  |  |  | 							continue directories; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					for (const managedPath of this.managedPathsWithSlash) { | 
					
						
							|  |  |  | 						if (path.startsWith(managedPath)) { | 
					
						
							|  |  |  | 							managedItems.add(getManagedItem(managedPath, path)); | 
					
						
							|  |  |  | 							continue directories; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					const cache = this._contextHashes.get(path); | 
					
						
							|  |  |  | 					if (cache !== undefined) { | 
					
						
							|  |  |  | 						contextHashes.set(path, cache); | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						jobs++; | 
					
						
							|  |  |  | 						this.contextHashQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 							if (err) { | 
					
						
							|  |  |  | 								contextHashes.set(path, "error"); | 
					
						
							|  |  |  | 							} else { | 
					
						
							|  |  |  | 								contextHashes.set(path, entry); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				directories: for (const path of directories) { | 
					
						
							| 
									
										
										
										
											2019-08-16 17:55:10 +08:00
										 |  |  | 					for (const immutablePath of this.immutablePathsWithSlash) { | 
					
						
							|  |  |  | 						if (path.startsWith(immutablePath)) { | 
					
						
							|  |  |  | 							continue directories; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 					for (const managedPath of this.managedPathsWithSlash) { | 
					
						
							|  |  |  | 						if (path.startsWith(managedPath)) { | 
					
						
							|  |  |  | 							managedItems.add(getManagedItem(managedPath, path)); | 
					
						
							|  |  |  | 							continue directories; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					contextTimestamps.set(path, "error"); | 
					
						
							|  |  |  | 					// TODO: getContextTimestamp is not implemented yet
 | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											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)) { | 
					
						
							|  |  |  | 						managedItems.add(getManagedItem(managedPath, path)); | 
					
						
							|  |  |  | 						continue missing; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 				const cache = this._fileTimestamps.get(path); | 
					
						
							|  |  |  | 				if (cache !== undefined) { | 
					
						
							|  |  |  | 					missingTimestamps.set(path, cache); | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					jobs++; | 
					
						
							|  |  |  | 					this.fileTimestampQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 						if (err) { | 
					
						
							|  |  |  | 							missingTimestamps.set(path, "error"); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							missingTimestamps.set(path, entry); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						jobDone(); | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		for (const path of managedItems) { | 
					
						
							|  |  |  | 			const cache = this._managedItems.get(path); | 
					
						
							|  |  |  | 			if (cache !== undefined) { | 
					
						
							|  |  |  | 				managedItemInfo.set(path, cache); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				jobs++; | 
					
						
							|  |  |  | 				this.managedItemQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 					if (err) { | 
					
						
							|  |  |  | 						managedItemInfo.set(path, "error"); | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						managedItemInfo.set(path, entry); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					jobDone(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		jobDone(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {Snapshot} snapshot1 a snapshot | 
					
						
							|  |  |  | 	 * @param {Snapshot} snapshot2 a snapshot | 
					
						
							|  |  |  | 	 * @returns {Snapshot} merged snapshot | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	mergeSnapshots(snapshot1, snapshot2) { | 
					
						
							|  |  |  | 		/** @type {Snapshot} */ | 
					
						
							|  |  |  | 		const snapshot = {}; | 
					
						
							|  |  |  | 		if (snapshot1.startTime && snapshot2.startTime) | 
					
						
							|  |  |  | 			snapshot.startTime = Math.min(snapshot1.startTime, snapshot2.startTime); | 
					
						
							|  |  |  | 		else if (snapshot2.startTime) snapshot.startTime = snapshot2.startTime; | 
					
						
							|  |  |  | 		else if (snapshot1.startTime) snapshot.startTime = snapshot1.startTime; | 
					
						
							|  |  |  | 		if (snapshot1.fileTimestamps || snapshot2.fileTimestamps) { | 
					
						
							|  |  |  | 			snapshot.fileTimestamps = mergeMaps( | 
					
						
							|  |  |  | 				snapshot1.fileTimestamps, | 
					
						
							|  |  |  | 				snapshot2.fileTimestamps | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (snapshot1.fileHashes || snapshot2.fileHashes) { | 
					
						
							|  |  |  | 			snapshot.fileHashes = mergeMaps( | 
					
						
							|  |  |  | 				snapshot1.fileHashes, | 
					
						
							|  |  |  | 				snapshot2.fileHashes | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (snapshot1.contextTimestamps || snapshot2.contextTimestamps) { | 
					
						
							|  |  |  | 			snapshot.contextTimestamps = mergeMaps( | 
					
						
							|  |  |  | 				snapshot1.contextTimestamps, | 
					
						
							|  |  |  | 				snapshot2.contextTimestamps | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (snapshot1.contextHashes || snapshot2.contextHashes) { | 
					
						
							|  |  |  | 			snapshot.contextHashes = mergeMaps( | 
					
						
							|  |  |  | 				snapshot1.contextHashes, | 
					
						
							|  |  |  | 				snapshot2.contextHashes | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (snapshot1.missingTimestamps || snapshot2.missingTimestamps) { | 
					
						
							|  |  |  | 			snapshot.missingTimestamps = mergeMaps( | 
					
						
							|  |  |  | 				snapshot1.missingTimestamps, | 
					
						
							|  |  |  | 				snapshot2.missingTimestamps | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (snapshot1.managedItemInfo || snapshot2.managedItemInfo) { | 
					
						
							|  |  |  | 			snapshot.managedItemInfo = mergeMaps( | 
					
						
							|  |  |  | 				snapshot1.managedItemInfo, | 
					
						
							|  |  |  | 				snapshot2.managedItemInfo | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return snapshot; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {Snapshot} snapshot the snapshot made | 
					
						
							|  |  |  | 	 * @param {function(WebpackError=, boolean=): void} callback callback function | 
					
						
							|  |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 	checkSnapshotValid(snapshot, callback) { | 
					
						
							|  |  |  | 		const { | 
					
						
							|  |  |  | 			startTime, | 
					
						
							|  |  |  | 			fileTimestamps, | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			fileHashes, | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			contextTimestamps, | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			contextHashes, | 
					
						
							| 
									
										
										
										
											2019-08-13 23:26:54 +08:00
										 |  |  | 			missingTimestamps, | 
					
						
							|  |  |  | 			managedItemInfo | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		} = snapshot; | 
					
						
							|  |  |  | 		let jobs = 1; | 
					
						
							|  |  |  | 		const jobDone = () => { | 
					
						
							|  |  |  | 			if (--jobs === 0) { | 
					
						
							|  |  |  | 				callback(null, true); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 		const invalid = () => { | 
					
						
							|  |  |  | 			if (jobs > 0) { | 
					
						
							|  |  |  | 				jobs = NaN; | 
					
						
							|  |  |  | 				callback(null, false); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		const checkHash = (current, snap) => { | 
					
						
							|  |  |  | 			if (snap === "error") { | 
					
						
							|  |  |  | 				// If there was an error while snapshotting (i. e. EBUSY)
 | 
					
						
							|  |  |  | 				// we can't compare further data and assume it's invalid
 | 
					
						
							|  |  |  | 				return false; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return current === snap; | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 		/** | 
					
						
							|  |  |  | 		 * @param {FileSystemInfoEntry} current current entry | 
					
						
							|  |  |  | 		 * @param {FileSystemInfoEntry | "error"} snap entry from snapshot | 
					
						
							|  |  |  | 		 * @returns {boolean} true, if ok | 
					
						
							|  |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		const checkExistance = (current, snap) => { | 
					
						
							|  |  |  | 			if (snap === "error") { | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 				// If there was an error while snapshotting (i. e. EBUSY)
 | 
					
						
							|  |  |  | 				// we can't compare further data and assume it's invalid
 | 
					
						
							|  |  |  | 				return false; | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			return !current === !snap; | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2019-07-18 05:35:05 +08:00
										 |  |  | 		/** | 
					
						
							|  |  |  | 		 * @param {FileSystemInfoEntry} current current entry | 
					
						
							|  |  |  | 		 * @param {FileSystemInfoEntry | "error"} snap entry from snapshot | 
					
						
							|  |  |  | 		 * @returns {boolean} true, if ok | 
					
						
							|  |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		const checkFile = (current, snap) => { | 
					
						
							|  |  |  | 			if (snap === "error") { | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 				// If there was an error while snapshotting (i. e. EBUSY)
 | 
					
						
							|  |  |  | 				// we can't compare further data and assume it's invalid
 | 
					
						
							|  |  |  | 				return false; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if (current && current.safeTime > startTime) { | 
					
						
							|  |  |  | 				// If a change happened after starting reading the item
 | 
					
						
							|  |  |  | 				// this may no longer be valid
 | 
					
						
							|  |  |  | 				return false; | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 			if (!current !== !snap) { | 
					
						
							|  |  |  | 				// If existance of item differs
 | 
					
						
							|  |  |  | 				// it's invalid
 | 
					
						
							|  |  |  | 				return false; | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 			if (current) { | 
					
						
							|  |  |  | 				// For existing items only
 | 
					
						
							|  |  |  | 				if ( | 
					
						
							|  |  |  | 					snap.timestamp !== undefined && | 
					
						
							|  |  |  | 					current.timestamp !== snap.timestamp | 
					
						
							|  |  |  | 				) { | 
					
						
							|  |  |  | 					// If we have a timestamp (it was a file or symlink) and it differs from current timestamp
 | 
					
						
							|  |  |  | 					// it's invalid
 | 
					
						
							|  |  |  | 					return false; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return true; | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		if (fileTimestamps) { | 
					
						
							|  |  |  | 			for (const [path, ts] of fileTimestamps) { | 
					
						
							|  |  |  | 				const cache = this._fileTimestamps.get(path); | 
					
						
							|  |  |  | 				if (cache !== undefined) { | 
					
						
							|  |  |  | 					if (!checkFile(cache, ts)) { | 
					
						
							|  |  |  | 						invalid(); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					jobs++; | 
					
						
							|  |  |  | 					this.fileTimestampQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 						if (err) return invalid(); | 
					
						
							|  |  |  | 						if (!checkFile(entry, ts)) { | 
					
						
							|  |  |  | 							invalid(); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					}); | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (fileHashes) { | 
					
						
							|  |  |  | 			for (const [path, hash] of fileHashes) { | 
					
						
							|  |  |  | 				const cache = this._fileHashes.get(path); | 
					
						
							|  |  |  | 				if (cache !== undefined) { | 
					
						
							|  |  |  | 					if (!checkHash(cache, hash)) { | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 						invalid(); | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 				} else { | 
					
						
							|  |  |  | 					jobs++; | 
					
						
							|  |  |  | 					this.fileHashQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 						if (err) return invalid(); | 
					
						
							|  |  |  | 						if (!checkHash(entry, hash)) { | 
					
						
							|  |  |  | 							invalid(); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		if (contextTimestamps && contextTimestamps.size > 0) { | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			// TODO: getContextTimestamp is not implemented yet
 | 
					
						
							|  |  |  | 			invalid(); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 		if (contextHashes) { | 
					
						
							|  |  |  | 			for (const [path, hash] of contextHashes) { | 
					
						
							|  |  |  | 				const cache = this._contextHashes.get(path); | 
					
						
							|  |  |  | 				if (cache !== undefined) { | 
					
						
							|  |  |  | 					if (!checkHash(cache, hash)) { | 
					
						
							|  |  |  | 						invalid(); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					jobs++; | 
					
						
							|  |  |  | 					this.contextHashQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 						if (err) return invalid(); | 
					
						
							|  |  |  | 						if (!checkHash(entry, hash)) { | 
					
						
							|  |  |  | 							invalid(); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					}); | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (missingTimestamps) { | 
					
						
							|  |  |  | 			for (const [path, ts] of missingTimestamps) { | 
					
						
							|  |  |  | 				const cache = this._fileTimestamps.get(path); | 
					
						
							|  |  |  | 				if (cache !== undefined) { | 
					
						
							|  |  |  | 					if (!checkExistance(cache, ts)) { | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 						invalid(); | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 				} else { | 
					
						
							|  |  |  | 					jobs++; | 
					
						
							|  |  |  | 					this.fileTimestampQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 						if (err) return invalid(); | 
					
						
							|  |  |  | 						if (!checkExistance(entry, ts)) { | 
					
						
							|  |  |  | 							invalid(); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-13 23:26:54 +08:00
										 |  |  | 		if (managedItemInfo) { | 
					
						
							|  |  |  | 			for (const [path, info] of managedItemInfo) { | 
					
						
							|  |  |  | 				const cache = this._managedItems.get(path); | 
					
						
							|  |  |  | 				if (cache !== undefined) { | 
					
						
							|  |  |  | 					if (!checkHash(cache, info)) { | 
					
						
							|  |  |  | 						invalid(); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					jobs++; | 
					
						
							|  |  |  | 					this.managedItemQueue.add(path, (err, entry) => { | 
					
						
							|  |  |  | 						if (err) return invalid(); | 
					
						
							|  |  |  | 						if (!checkHash(entry, info)) { | 
					
						
							|  |  |  | 							invalid(); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							jobDone(); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 		jobDone(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	_readFileTimestamp(path, callback) { | 
					
						
							|  |  |  | 		this.fs.stat(path, (err, stat) => { | 
					
						
							|  |  |  | 			if (err) { | 
					
						
							|  |  |  | 				if (err.code === "ENOENT") { | 
					
						
							|  |  |  | 					this._fileTimestamps.set(path, null); | 
					
						
							|  |  |  | 					return callback(null, null); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return callback(err); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 			const mtime = +stat.mtime; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (mtime) applyMtime(mtime); | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			const ts = { | 
					
						
							| 
									
										
										
										
											2019-01-09 20:23:26 +08:00
										 |  |  | 				safeTime: mtime ? mtime + FS_ACCURACY : Infinity, | 
					
						
							|  |  |  | 				timestamp: stat.isDirectory() ? undefined : mtime | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 			}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			this._fileTimestamps.set(path, ts); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			callback(null, ts); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-13 04:59:09 +08:00
										 |  |  | 	_readFileHash(path, callback) { | 
					
						
							|  |  |  | 		this.fs.readFile(path, (err, content) => { | 
					
						
							|  |  |  | 			if (err) { | 
					
						
							|  |  |  | 				if (err.code === "ENOENT") { | 
					
						
							|  |  |  | 					this._fileHashes.set(path, null); | 
					
						
							|  |  |  | 					return callback(null, null); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return callback(err); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			const hash = createHash("md4"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			hash.update(content); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			const digest = /** @type {string} */ (hash.digest("hex")); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			this._fileHashes.set(path, digest); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			callback(null, digest); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	_readContextTimestamp(path, callback) { | 
					
						
							|  |  |  | 		// TODO read whole folder
 | 
					
						
							|  |  |  | 		this._contextTimestamps.set(path, null); | 
					
						
							|  |  |  | 		callback(null, null); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 						if (stat.isFile()) { | 
					
						
							|  |  |  | 							return this.getFileHash(child, callback); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						if (stat.isDirectory()) { | 
					
						
							|  |  |  | 							this.contextHashQueue.increaseParallelism(); | 
					
						
							|  |  |  | 							this.getContextHash(child, (err, hash) => { | 
					
						
							|  |  |  | 								this.contextHashQueue.decreaseParallelism(); | 
					
						
							|  |  |  | 								callback(err, hash || ""); | 
					
						
							|  |  |  | 							}); | 
					
						
							|  |  |  | 							return; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						callback(null, ""); | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				(err, fileHashes) => { | 
					
						
							|  |  |  | 					const hash = createHash("md4"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					for (const file of files) hash.update(file); | 
					
						
							|  |  |  | 					for (const h of fileHashes) hash.update(h); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					const digest = /** @type {string} */ (hash.digest("hex")); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					this._contextHashes.set(path, digest); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					callback(null, digest); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	_getManagedItemInfo(path, callback) { | 
					
						
							|  |  |  | 		const packageJsonPath = join(this.fs, path, "package.json"); | 
					
						
							|  |  |  | 		this.fs.readFile(packageJsonPath, (err, content) => { | 
					
						
							|  |  |  | 			if (err) return callback(err); | 
					
						
							|  |  |  | 			let data; | 
					
						
							|  |  |  | 			try { | 
					
						
							|  |  |  | 				data = JSON.parse(content.toString("utf-8")); | 
					
						
							|  |  |  | 			} catch (e) { | 
					
						
							|  |  |  | 				return callback(e); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			const info = `${data.name || ""}@${data.version || ""}`; | 
					
						
							|  |  |  | 			callback(null, info); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-27 13:22:19 +08:00
										 |  |  | 	getDeprecatedFileTimestamps() { | 
					
						
							|  |  |  | 		const map = new Map(); | 
					
						
							|  |  |  | 		for (const [path, info] of this._fileTimestamps) { | 
					
						
							|  |  |  | 			if (info) map.set(path, info.safeTime); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return map; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	getDeprecatedContextTimestamps() { | 
					
						
							|  |  |  | 		const map = new Map(); | 
					
						
							|  |  |  | 		for (const [path, info] of this._contextTimestamps) { | 
					
						
							|  |  |  | 			if (info) map.set(path, info.safeTime); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return map; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports = FileSystemInfo; |