The rest is a watch module, which is deeper. First, we will discuss the whole content and then explain it separately.
The flow chart is as follows:
NodeWatchFileSystem
const Watchpack = require("watchpack"); class NodeWatchFileSystem { constructor(inputFileSystem) { this.inputFileSystem = inputFileSystem; this.watcherOptions = { aggregateTimeout: 0 }; this.watcher = new Watchpack(this.watcherOptions); } watch( files, /*Array*/ dirs, /*Array*/ missing, /*Array*/ startTime, /*number*/ options, /*object*/ callback, /*function*/ callbackUndelayed /*function*/ ) { // params validate... const oldWatcher = this.watcher; // generate Watchpack object this.watcher = new Watchpack(options); if (callbackUndelayed) this.watcher.once("change", callbackUndelayed); this.watcher.once("aggregated", (changes, removals) => { /**/ }); // Call the watch method this.watcher.watch(files.concat(missing), dirs.concat(missing), startTime); if (oldWatcher) { oldWatcher.close(); } return { close: () => { /**/ }, pause: () => { /**/ } }; } } module.exports = NodeWatchFileSystem;
Out and in-house implementation, the module is roughly as follows;
1. Introducing Watchpack Module
2. Accept an inputFileSystem as a constructor parameter
3. Instantiate a Watchpack class according to configuration options, and call the watch method of the class according to the incoming parameters
4. Bind two one-time events to bind and return an object
The method at the core of the module calls on the Watchpack entity class, so it needs to be further explored.
This module refers to the event module of nodejs. The content is very simple. It is not introduced here. For details, you can see the official API:https://nodejs.org/dist/latest-v8.x/docs/api/events.html.
Watchpack
var watcherManager = require("./watcherManager"); var EventEmitter = require("events").EventEmitter; Watchpack.prototype = Object.create(EventEmitter.prototype); class Watchpack { constructor(options) { EventEmitter.call(this); if (!options) options = {}; if (!options.aggregateTimeout) options.aggregateTimeout = 200; this.options = options; this.watcherOptions = { ignored: options.ignored, poll: options.poll }; this.fileWatchers = []; this.dirWatchers = []; this.mtimes = Object.create(null); this.paused = false; this.aggregatedChanges = []; this.aggregatedRemovals = []; this.aggregateTimeout = 0; this._onTimeout = this._onTimeout.bind(this); } watch(files, directories, startTime) { this.paused = false; var oldFileWatchers = this.fileWatchers; var oldDirWatchers = this.dirWatchers; this.fileWatchers = files.map(function(file) { return this._fileWatcher(file, watcherManager.watchFile(file, this.watcherOptions, startTime)); }, this); this.dirWatchers = directories.map(function(dir) { return this._dirWatcher(dir, watcherManager.watchDirectory(dir, this.watcherOptions, startTime)); }, this); oldFileWatchers.forEach(function(w) { w.close(); }, this); oldDirWatchers.forEach(function(w) { w.close(); }, this); }; pause() { /**/ }; getTimes() { /**/ }; _fileWatcher(file, watcher) { /**/ }; _dirWatcher(item, watcher) { /**/ }; _onChange(item, mtime, file) { /**/ }; _onRemove(item, file) { /**/ }; _onTimeout() { /**/ }; close() { /**/ }; } module.exports = Watchpack; function addWatchersToArray(watchers, array) { /**/ }
This module introduces and inherits Event Emitter of nodejs, and introduces a new module watcher Manager. The main contents are listed as follows:
1. The constructor accepts an object with the keys aggregateTimeout, ignored, poll. In this case, only the first one is passed in and set to 0.
2. The core method is watch, which depends on the watchManager module introduced.
3. The other methods are all tool methods.
WatcherManager
var path = require("path"); class WatcherManager { constructor() { this.directoryWatchers = {}; }; // Factory function getDirectoryWatcher(directory, options) { // Import module var DirectoryWatcher = require("./DirectoryWatcher"); options = options || {}; var key = directory + " " + JSON.stringify(options); if (!this.directoryWatchers[key]) { this.directoryWatchers[key] = new DirectoryWatcher(directory, options); // Delete files from containers at the end of file monitoring this.directoryWatchers[key].on("closed", function() { delete this.directoryWatchers[key]; }.bind(this)); } return this.directoryWatchers[key]; }; // Monitoring file watchFile(p, options, startTime) { var directory = path.dirname(p); return this.getDirectoryWatcher(directory, options).watch(p, startTime); }; // Monitoring directory watchDirectory(directory, options, startTime) { return this.getDirectoryWatcher(directory, options).watch(directory, startTime); }; } module.exports = new WatcherManager();
As you can see, this is an intermediate processing function in which the constructor generates a container whose key is a string generated by the directory + parameter, which is deleted immediately after the monitoring is closed.
This module is similar to tapable and is a monitor object manager.
Finally, the monitoring core implementation module, which contains a lot of modules, here we only briefly look at the constructor and watch method:
var EventEmitter = require("events").EventEmitter; var async = require("async"); var chokidar = require("chokidar"); var fs = require("graceful-fs"); class Watcher { constructor(directoryWatcher, filePath, startTime) { EventEmitter.call(this); this.directoryWatcher = directoryWatcher; this.path = filePath; this.startTime = startTime && +startTime; this.data = 0; }; checkStartTime(mtime, initial) { /**/ }; close() { /**/ }; } function DirectoryWatcher(directoryPath, options) { EventEmitter.call(this); this.options = options; this.path = directoryPath; this.files = Object.create(null); this.directories = Object.create(null); this.watcher = chokidar.watch(directoryPath, { ignoreInitial: true, persistent: true, followSymlinks: false, depth: 0, atomic: false, alwaysStat: true, ignorePermissionErrors: true, ignored: options.ignored, usePolling: options.poll ? true : undefined, interval: typeof options.poll === "number" ? options.poll : undefined, disableGlobbing: true }); this.watcher.on("add", this.onFileAdded.bind(this)); this.watcher.on("addDir", this.onDirectoryAdded.bind(this)); this.watcher.on("change", this.onChange.bind(this)); this.watcher.on("unlink", this.onFileUnlinked.bind(this)); this.watcher.on("unlinkDir", this.onDirectoryUnlinked.bind(this)); this.watcher.on("error", this.onWatcherError.bind(this)); // ... } DirectoryWatcher.prototype.watch = function watch(filePath, startTime) { this.watchers[withoutCase(filePath)] = this.watchers[withoutCase(filePath)] || []; this.refs++; var watcher = new Watcher(this, filePath, startTime); watcher.on("closed", function() { /**/ }.bind(this)); // ... return watcher; }; // ... module.exports = DirectoryWatcher;
A lot of information can be obtained from introducing constructors and modules, as follows:
1. Introducing graceful-js module, we can see that the bottom layer still uses fs module of nodejs to monitor.
2. All monitoring events are operated by EventEmitter module based on nodejs
3. There is also an auxiliary class Watcher inside.
4. According to the code of the constructor, the operations monitored include (possibly not limited to) adding new files, adding new folders, changing contents, deleting files, deleting folders, etc.
All modules are organized as above, and the next few sections will analyze each piece of content.