| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383 | 
							- /*
 
- 	MIT License http://www.opensource.org/licenses/mit-license.php
 
- 	Author Tobias Koppers @sokra
 
- */
 
- "use strict";
 
- const getWatcherManager = require("./getWatcherManager");
 
- const LinkResolver = require("./LinkResolver");
 
- const EventEmitter = require("events").EventEmitter;
 
- const globToRegExp = require("glob-to-regexp");
 
- const watchEventSource = require("./watchEventSource");
 
- const EMPTY_ARRAY = [];
 
- const EMPTY_OPTIONS = {};
 
- function addWatchersToSet(watchers, set) {
 
- 	for (const ww of watchers) {
 
- 		const w = ww.watcher;
 
- 		if (!set.has(w.directoryWatcher)) {
 
- 			set.add(w.directoryWatcher);
 
- 		}
 
- 	}
 
- }
 
- const stringToRegexp = ignored => {
 
- 	const source = globToRegExp(ignored, { globstar: true, extended: true })
 
- 		.source;
 
- 	const matchingStart = source.slice(0, source.length - 1) + "(?:$|\\/)";
 
- 	return matchingStart;
 
- };
 
- const ignoredToFunction = ignored => {
 
- 	if (Array.isArray(ignored)) {
 
- 		const regexp = new RegExp(ignored.map(i => stringToRegexp(i)).join("|"));
 
- 		return x => regexp.test(x.replace(/\\/g, "/"));
 
- 	} else if (typeof ignored === "string") {
 
- 		const regexp = new RegExp(stringToRegexp(ignored));
 
- 		return x => regexp.test(x.replace(/\\/g, "/"));
 
- 	} else if (ignored instanceof RegExp) {
 
- 		return x => ignored.test(x.replace(/\\/g, "/"));
 
- 	} else if (ignored instanceof Function) {
 
- 		return ignored;
 
- 	} else if (ignored) {
 
- 		throw new Error(`Invalid option for 'ignored': ${ignored}`);
 
- 	} else {
 
- 		return () => false;
 
- 	}
 
- };
 
- const normalizeOptions = options => {
 
- 	return {
 
- 		followSymlinks: !!options.followSymlinks,
 
- 		ignored: ignoredToFunction(options.ignored),
 
- 		poll: options.poll
 
- 	};
 
- };
 
- const normalizeCache = new WeakMap();
 
- const cachedNormalizeOptions = options => {
 
- 	const cacheEntry = normalizeCache.get(options);
 
- 	if (cacheEntry !== undefined) return cacheEntry;
 
- 	const normalized = normalizeOptions(options);
 
- 	normalizeCache.set(options, normalized);
 
- 	return normalized;
 
- };
 
- class WatchpackFileWatcher {
 
- 	constructor(watchpack, watcher, files) {
 
- 		this.files = Array.isArray(files) ? files : [files];
 
- 		this.watcher = watcher;
 
- 		watcher.on("initial-missing", type => {
 
- 			for (const file of this.files) {
 
- 				if (!watchpack._missing.has(file))
 
- 					watchpack._onRemove(file, file, type);
 
- 			}
 
- 		});
 
- 		watcher.on("change", (mtime, type) => {
 
- 			for (const file of this.files) {
 
- 				watchpack._onChange(file, mtime, file, type);
 
- 			}
 
- 		});
 
- 		watcher.on("remove", type => {
 
- 			for (const file of this.files) {
 
- 				watchpack._onRemove(file, file, type);
 
- 			}
 
- 		});
 
- 	}
 
- 	update(files) {
 
- 		if (!Array.isArray(files)) {
 
- 			if (this.files.length !== 1) {
 
- 				this.files = [files];
 
- 			} else if (this.files[0] !== files) {
 
- 				this.files[0] = files;
 
- 			}
 
- 		} else {
 
- 			this.files = files;
 
- 		}
 
- 	}
 
- 	close() {
 
- 		this.watcher.close();
 
- 	}
 
- }
 
- class WatchpackDirectoryWatcher {
 
- 	constructor(watchpack, watcher, directories) {
 
- 		this.directories = Array.isArray(directories) ? directories : [directories];
 
- 		this.watcher = watcher;
 
- 		watcher.on("initial-missing", type => {
 
- 			for (const item of this.directories) {
 
- 				watchpack._onRemove(item, item, type);
 
- 			}
 
- 		});
 
- 		watcher.on("change", (file, mtime, type) => {
 
- 			for (const item of this.directories) {
 
- 				watchpack._onChange(item, mtime, file, type);
 
- 			}
 
- 		});
 
- 		watcher.on("remove", type => {
 
- 			for (const item of this.directories) {
 
- 				watchpack._onRemove(item, item, type);
 
- 			}
 
- 		});
 
- 	}
 
- 	update(directories) {
 
- 		if (!Array.isArray(directories)) {
 
- 			if (this.directories.length !== 1) {
 
- 				this.directories = [directories];
 
- 			} else if (this.directories[0] !== directories) {
 
- 				this.directories[0] = directories;
 
- 			}
 
- 		} else {
 
- 			this.directories = directories;
 
- 		}
 
- 	}
 
- 	close() {
 
- 		this.watcher.close();
 
- 	}
 
- }
 
- class Watchpack extends EventEmitter {
 
- 	constructor(options) {
 
- 		super();
 
- 		if (!options) options = EMPTY_OPTIONS;
 
- 		this.options = options;
 
- 		this.aggregateTimeout =
 
- 			typeof options.aggregateTimeout === "number"
 
- 				? options.aggregateTimeout
 
- 				: 200;
 
- 		this.watcherOptions = cachedNormalizeOptions(options);
 
- 		this.watcherManager = getWatcherManager(this.watcherOptions);
 
- 		this.fileWatchers = new Map();
 
- 		this.directoryWatchers = new Map();
 
- 		this._missing = new Set();
 
- 		this.startTime = undefined;
 
- 		this.paused = false;
 
- 		this.aggregatedChanges = new Set();
 
- 		this.aggregatedRemovals = new Set();
 
- 		this.aggregateTimer = undefined;
 
- 		this._onTimeout = this._onTimeout.bind(this);
 
- 	}
 
- 	watch(arg1, arg2, arg3) {
 
- 		let files, directories, missing, startTime;
 
- 		if (!arg2) {
 
- 			({
 
- 				files = EMPTY_ARRAY,
 
- 				directories = EMPTY_ARRAY,
 
- 				missing = EMPTY_ARRAY,
 
- 				startTime
 
- 			} = arg1);
 
- 		} else {
 
- 			files = arg1;
 
- 			directories = arg2;
 
- 			missing = EMPTY_ARRAY;
 
- 			startTime = arg3;
 
- 		}
 
- 		this.paused = false;
 
- 		const fileWatchers = this.fileWatchers;
 
- 		const directoryWatchers = this.directoryWatchers;
 
- 		const ignored = this.watcherOptions.ignored;
 
- 		const filter = path => !ignored(path);
 
- 		const addToMap = (map, key, item) => {
 
- 			const list = map.get(key);
 
- 			if (list === undefined) {
 
- 				map.set(key, item);
 
- 			} else if (Array.isArray(list)) {
 
- 				list.push(item);
 
- 			} else {
 
- 				map.set(key, [list, item]);
 
- 			}
 
- 		};
 
- 		const fileWatchersNeeded = new Map();
 
- 		const directoryWatchersNeeded = new Map();
 
- 		const missingFiles = new Set();
 
- 		if (this.watcherOptions.followSymlinks) {
 
- 			const resolver = new LinkResolver();
 
- 			for (const file of files) {
 
- 				if (filter(file)) {
 
- 					for (const innerFile of resolver.resolve(file)) {
 
- 						if (file === innerFile || filter(innerFile)) {
 
- 							addToMap(fileWatchersNeeded, innerFile, file);
 
- 						}
 
- 					}
 
- 				}
 
- 			}
 
- 			for (const file of missing) {
 
- 				if (filter(file)) {
 
- 					for (const innerFile of resolver.resolve(file)) {
 
- 						if (file === innerFile || filter(innerFile)) {
 
- 							missingFiles.add(file);
 
- 							addToMap(fileWatchersNeeded, innerFile, file);
 
- 						}
 
- 					}
 
- 				}
 
- 			}
 
- 			for (const dir of directories) {
 
- 				if (filter(dir)) {
 
- 					let first = true;
 
- 					for (const innerItem of resolver.resolve(dir)) {
 
- 						if (filter(innerItem)) {
 
- 							addToMap(
 
- 								first ? directoryWatchersNeeded : fileWatchersNeeded,
 
- 								innerItem,
 
- 								dir
 
- 							);
 
- 						}
 
- 						first = false;
 
- 					}
 
- 				}
 
- 			}
 
- 		} else {
 
- 			for (const file of files) {
 
- 				if (filter(file)) {
 
- 					addToMap(fileWatchersNeeded, file, file);
 
- 				}
 
- 			}
 
- 			for (const file of missing) {
 
- 				if (filter(file)) {
 
- 					missingFiles.add(file);
 
- 					addToMap(fileWatchersNeeded, file, file);
 
- 				}
 
- 			}
 
- 			for (const dir of directories) {
 
- 				if (filter(dir)) {
 
- 					addToMap(directoryWatchersNeeded, dir, dir);
 
- 				}
 
- 			}
 
- 		}
 
- 		// Close unneeded old watchers
 
- 		// and update existing watchers
 
- 		for (const [key, w] of fileWatchers) {
 
- 			const needed = fileWatchersNeeded.get(key);
 
- 			if (needed === undefined) {
 
- 				w.close();
 
- 				fileWatchers.delete(key);
 
- 			} else {
 
- 				w.update(needed);
 
- 				fileWatchersNeeded.delete(key);
 
- 			}
 
- 		}
 
- 		for (const [key, w] of directoryWatchers) {
 
- 			const needed = directoryWatchersNeeded.get(key);
 
- 			if (needed === undefined) {
 
- 				w.close();
 
- 				directoryWatchers.delete(key);
 
- 			} else {
 
- 				w.update(needed);
 
- 				directoryWatchersNeeded.delete(key);
 
- 			}
 
- 		}
 
- 		// Create new watchers and install handlers on these watchers
 
- 		watchEventSource.batch(() => {
 
- 			for (const [key, files] of fileWatchersNeeded) {
 
- 				const watcher = this.watcherManager.watchFile(key, startTime);
 
- 				if (watcher) {
 
- 					fileWatchers.set(key, new WatchpackFileWatcher(this, watcher, files));
 
- 				}
 
- 			}
 
- 			for (const [key, directories] of directoryWatchersNeeded) {
 
- 				const watcher = this.watcherManager.watchDirectory(key, startTime);
 
- 				if (watcher) {
 
- 					directoryWatchers.set(
 
- 						key,
 
- 						new WatchpackDirectoryWatcher(this, watcher, directories)
 
- 					);
 
- 				}
 
- 			}
 
- 		});
 
- 		this._missing = missingFiles;
 
- 		this.startTime = startTime;
 
- 	}
 
- 	close() {
 
- 		this.paused = true;
 
- 		if (this.aggregateTimer) clearTimeout(this.aggregateTimer);
 
- 		for (const w of this.fileWatchers.values()) w.close();
 
- 		for (const w of this.directoryWatchers.values()) w.close();
 
- 		this.fileWatchers.clear();
 
- 		this.directoryWatchers.clear();
 
- 	}
 
- 	pause() {
 
- 		this.paused = true;
 
- 		if (this.aggregateTimer) clearTimeout(this.aggregateTimer);
 
- 	}
 
- 	getTimes() {
 
- 		const directoryWatchers = new Set();
 
- 		addWatchersToSet(this.fileWatchers.values(), directoryWatchers);
 
- 		addWatchersToSet(this.directoryWatchers.values(), directoryWatchers);
 
- 		const obj = Object.create(null);
 
- 		for (const w of directoryWatchers) {
 
- 			const times = w.getTimes();
 
- 			for (const file of Object.keys(times)) obj[file] = times[file];
 
- 		}
 
- 		return obj;
 
- 	}
 
- 	getTimeInfoEntries() {
 
- 		const map = new Map();
 
- 		this.collectTimeInfoEntries(map, map);
 
- 		return map;
 
- 	}
 
- 	collectTimeInfoEntries(fileTimestamps, directoryTimestamps) {
 
- 		const allWatchers = new Set();
 
- 		addWatchersToSet(this.fileWatchers.values(), allWatchers);
 
- 		addWatchersToSet(this.directoryWatchers.values(), allWatchers);
 
- 		const safeTime = { value: 0 };
 
- 		for (const w of allWatchers) {
 
- 			w.collectTimeInfoEntries(fileTimestamps, directoryTimestamps, safeTime);
 
- 		}
 
- 	}
 
- 	getAggregated() {
 
- 		if (this.aggregateTimer) {
 
- 			clearTimeout(this.aggregateTimer);
 
- 			this.aggregateTimer = undefined;
 
- 		}
 
- 		const changes = this.aggregatedChanges;
 
- 		const removals = this.aggregatedRemovals;
 
- 		this.aggregatedChanges = new Set();
 
- 		this.aggregatedRemovals = new Set();
 
- 		return { changes, removals };
 
- 	}
 
- 	_onChange(item, mtime, file, type) {
 
- 		file = file || item;
 
- 		if (!this.paused) {
 
- 			this.emit("change", file, mtime, type);
 
- 			if (this.aggregateTimer) clearTimeout(this.aggregateTimer);
 
- 			this.aggregateTimer = setTimeout(this._onTimeout, this.aggregateTimeout);
 
- 		}
 
- 		this.aggregatedRemovals.delete(item);
 
- 		this.aggregatedChanges.add(item);
 
- 	}
 
- 	_onRemove(item, file, type) {
 
- 		file = file || item;
 
- 		if (!this.paused) {
 
- 			this.emit("remove", file, type);
 
- 			if (this.aggregateTimer) clearTimeout(this.aggregateTimer);
 
- 			this.aggregateTimer = setTimeout(this._onTimeout, this.aggregateTimeout);
 
- 		}
 
- 		this.aggregatedChanges.delete(item);
 
- 		this.aggregatedRemovals.add(item);
 
- 	}
 
- 	_onTimeout() {
 
- 		this.aggregateTimer = undefined;
 
- 		const changes = this.aggregatedChanges;
 
- 		const removals = this.aggregatedRemovals;
 
- 		this.aggregatedChanges = new Set();
 
- 		this.aggregatedRemovals = new Set();
 
- 		this.emit("aggregated", changes, removals);
 
- 	}
 
- }
 
- module.exports = Watchpack;
 
 
  |