pax_global_header00006660000000000000000000000064135172174420014520gustar00rootroot0000000000000052 comment=1e31c1250b743b42d3ae49f9611fd6fee81935e8 node-watchpack-1.6.0/000077500000000000000000000000001351721744200144145ustar00rootroot00000000000000node-watchpack-1.6.0/.editorconfig000066400000000000000000000004341351721744200170720ustar00rootroot00000000000000root = true [*] indent_style = tab indent_size = 4 charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true max_line_length = 233 [*.json] indent_style = space indent_size = 2 [*.yml] indent_style = space indent_size = 2 [*.md] trim_trailing_whitespace = false node-watchpack-1.6.0/.eslintrc000066400000000000000000000001511351721744200162350ustar00rootroot00000000000000{ "env": { "node": true }, "rules": { "strict": 0, "curly": 0, "no-underscore-dangle": 0 } } node-watchpack-1.6.0/.gitattributes000066400000000000000000000000521351721744200173040ustar00rootroot00000000000000* text=auto examples/* eol=lf bin/* eol=lfnode-watchpack-1.6.0/.gitignore000066400000000000000000000001031351721744200163760ustar00rootroot00000000000000/node_modules /test/fixtures /playground/folder /coverage .DS_Storenode-watchpack-1.6.0/.travis.yml000066400000000000000000000004451351721744200165300ustar00rootroot00000000000000sudo: false language: node_js os: - linux - osx node_js: - "8" - "6" - "4" script: npm run travis after_success: - cat ./coverage/lcov.info | node_modules/.bin/coveralls --verbose - cat ./coverage/coverage.json | node_modules/codecov.io/bin/codecov.io.js - rm -rf ./coverage node-watchpack-1.6.0/LICENSE000066400000000000000000000020571351721744200154250ustar00rootroot00000000000000Copyright JS Foundation and other contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. node-watchpack-1.6.0/README.md000066400000000000000000000057201351721744200156770ustar00rootroot00000000000000# watchpack Wrapper library for directory and file watching. [![Build Status](https://travis-ci.org/webpack/watchpack.svg?branch=master)](https://travis-ci.org/webpack/watchpack) [![Build status](https://ci.appveyor.com/api/projects/status/e5u2qvmugtv0r647/branch/master?svg=true)](https://ci.appveyor.com/project/sokra/watchpack/branch/master) [![Test coverage][coveralls-image]][coveralls-url] ## Concept watchpack high level API doesn't map directly to watchers. Instead a three level architecture ensures that for each directory only a single watcher exists. * The high level API requests `DirectoryWatchers` from a `WatcherManager`, which ensures that only a single `DirectoryWatcher` per directory is created. * A user-faced `Watcher` can be obtained from a `DirectoryWatcher` and provides a filtered view on the `DirectoryWatcher`. * Reference-counting is used on the `DirectoryWatcher` and `Watcher` to decide when to close them. * The real watchers (currently chokidar) are created by the `DirectoryWatcher`. * Files are never watched directly. This should keep the watcher count low. * Watching can be started in the past. This way watching can start after file reading. * Symlinks are not followed, instead the symlink is watched. ## API ``` javascript var Watchpack = require("watchpack"); var wp = new Watchpack({ // options: aggregateTimeout: 1000 // fire "aggregated" event when after a change for 1000ms no additional change occurred // aggregated defaults to undefined, which doesn't fire an "aggregated" event poll: true // poll: true - use polling with the default interval // poll: 10000 - use polling with an interval of 10s // poll defaults to undefined, which prefer native watching methods // Note: enable polling when watching on a network path ignored: /node_modules/, // anymatch-compatible definition of files/paths to be ignored // see https://github.com/paulmillr/chokidar#path-filtering }); // Watchpack.prototype.watch(string[] files, string[] directories, [number startTime]) wp.watch(listOfFiles, listOfDirectories, Date.now() - 10000); // starts watching these files and directories // calling this again will override the files and directories wp.on("change", function(filePath, mtime) { // filePath: the changed file // mtime: last modified time for the changed file }); wp.on("aggregated", function(changes) { // changes: an array of all changed files }); // Watchpack.prototype.pause() wp.pause(); // stops emitting events, but keeps watchers open // next "watch" call can reuse the watchers // Watchpack.prototype.close() wp.close(); // stops emitting events and closes all watchers // Watchpack.prototype.getTimes() var fileTimes = wp.getTimes(); // returns an object with all know change times for files // this include timestamps from files not directly watched // key: absolute path, value: timestamp as number ``` [coveralls-url]: https://coveralls.io/r/webpack/watchpack/ [coveralls-image]: https://img.shields.io/coveralls/webpack/watchpack.svg node-watchpack-1.6.0/appveyor.yml000066400000000000000000000006131351721744200170040ustar00rootroot00000000000000# appveyor file # http://www.appveyor.com/docs/appveyor-yml init: - git config --global core.autocrlf input # what combinations to test environment: matrix: - nodejs_version: 8 - nodejs_version: 6 - nodejs_version: 4 install: - ps: Install-Product node $env:nodejs_version x64 - npm install build: off test_script: - node --version - npm --version - cmd: npm test node-watchpack-1.6.0/lib/000077500000000000000000000000001351721744200151625ustar00rootroot00000000000000node-watchpack-1.6.0/lib/DirectoryWatcher.js000066400000000000000000000265441351721744200210150ustar00rootroot00000000000000/* MIT License http://www.opensource.org/licenses/mit-license.php Author Tobias Koppers @sokra */ "use strict"; var EventEmitter = require("events").EventEmitter; var async = require("neo-async"); var chokidar = require("chokidar"); var fs = require("graceful-fs"); var path = require("path"); var watcherManager = require("./watcherManager"); var FS_ACCURACY = 1000; function withoutCase(str) { return str.toLowerCase(); } function Watcher(directoryWatcher, filePath, startTime) { EventEmitter.call(this); this.directoryWatcher = directoryWatcher; this.path = filePath; this.startTime = startTime && +startTime; // TODO this.data seem to be only read, weird this.data = 0; } Watcher.prototype = Object.create(EventEmitter.prototype); Watcher.prototype.constructor = Watcher; Watcher.prototype.checkStartTime = function checkStartTime(mtime, initial) { if(typeof this.startTime !== "number") return !initial; var startTime = this.startTime; return startTime <= mtime; }; Watcher.prototype.close = function close() { this.emit("closed"); }; function DirectoryWatcher(directoryPath, options) { EventEmitter.call(this); this.options = options; this.path = directoryPath; this.files = Object.create(null); this.directories = Object.create(null); var interval = typeof options.poll === "number" ? options.poll : undefined; 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: interval, binaryInterval: interval, 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)); this.initialScan = true; this.nestedWatching = false; this.initialScanRemoved = []; this.doInitialScan(); this.watchers = Object.create(null); this.parentWatcher = null; this.refs = 0; } module.exports = DirectoryWatcher; DirectoryWatcher.prototype = Object.create(EventEmitter.prototype); DirectoryWatcher.prototype.constructor = DirectoryWatcher; DirectoryWatcher.prototype.setFileTime = function setFileTime(filePath, mtime, initial, type) { var now = Date.now(); var old = this.files[filePath]; this.files[filePath] = [initial ? Math.min(now, mtime) : now, mtime]; // we add the fs accuracy to reach the maximum possible mtime if(mtime) mtime = mtime + FS_ACCURACY; if(!old) { if(mtime) { if(this.watchers[withoutCase(filePath)]) { this.watchers[withoutCase(filePath)].forEach(function(w) { if(!initial || w.checkStartTime(mtime, initial)) { w.emit("change", mtime, initial ? "initial" : type); } }); } } } else if(!initial && mtime) { if(this.watchers[withoutCase(filePath)]) { this.watchers[withoutCase(filePath)].forEach(function(w) { w.emit("change", mtime, type); }); } } else if(!initial && !mtime) { if(this.watchers[withoutCase(filePath)]) { this.watchers[withoutCase(filePath)].forEach(function(w) { w.emit("remove", type); }); } } if(this.watchers[withoutCase(this.path)]) { this.watchers[withoutCase(this.path)].forEach(function(w) { if(!initial || w.checkStartTime(mtime, initial)) { w.emit("change", filePath, mtime, initial ? "initial" : type); } }); } }; DirectoryWatcher.prototype.setDirectory = function setDirectory(directoryPath, exist, initial, type) { if(directoryPath === this.path) { if(!initial && this.watchers[withoutCase(this.path)]) { this.watchers[withoutCase(this.path)].forEach(function(w) { w.emit("change", directoryPath, w.data, initial ? "initial" : type); }); } } else { var old = this.directories[directoryPath]; if(!old) { if(exist) { if(this.nestedWatching) { this.createNestedWatcher(directoryPath); } else { this.directories[directoryPath] = true; } if(!initial && this.watchers[withoutCase(this.path)]) { this.watchers[withoutCase(this.path)].forEach(function(w) { w.emit("change", directoryPath, w.data, initial ? "initial" : type); }); } if(this.watchers[withoutCase(directoryPath) + "#directory"]) { this.watchers[withoutCase(directoryPath) + "#directory"].forEach(function(w) { w.emit("change", w.data, initial ? "initial" : type); }); } } } else { if(!exist) { if(this.nestedWatching) this.directories[directoryPath].close(); delete this.directories[directoryPath]; if(!initial && this.watchers[withoutCase(this.path)]) { this.watchers[withoutCase(this.path)].forEach(function(w) { w.emit("change", directoryPath, w.data, initial ? "initial" : type); }); } if(this.watchers[withoutCase(directoryPath) + "#directory"]) { this.watchers[withoutCase(directoryPath) + "#directory"].forEach(function(w) { w.emit("change", directoryPath, w.data, initial ? "initial" : type); }); } } } } }; DirectoryWatcher.prototype.createNestedWatcher = function(directoryPath) { this.directories[directoryPath] = watcherManager.watchDirectory(directoryPath, this.options, 1); this.directories[directoryPath].on("change", function(filePath, mtime, type) { if(this.watchers[withoutCase(this.path)]) { this.watchers[withoutCase(this.path)].forEach(function(w) { if(w.checkStartTime(mtime, false)) { w.emit("change", filePath, mtime, type); } }); } }.bind(this)); }; DirectoryWatcher.prototype.setNestedWatching = function(flag) { if(this.nestedWatching !== !!flag) { this.nestedWatching = !!flag; if(this.nestedWatching) { Object.keys(this.directories).forEach(function(directory) { this.createNestedWatcher(directory); }, this); } else { Object.keys(this.directories).forEach(function(directory) { this.directories[directory].close(); this.directories[directory] = true; }, 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() { var idx = this.watchers[withoutCase(filePath)].indexOf(watcher); this.watchers[withoutCase(filePath)].splice(idx, 1); if(this.watchers[withoutCase(filePath)].length === 0) { delete this.watchers[withoutCase(filePath)]; if(this.path === filePath) this.setNestedWatching(false); } if(--this.refs <= 0) this.close(); }.bind(this)); this.watchers[withoutCase(filePath)].push(watcher); var data; if(filePath === this.path) { this.setNestedWatching(true); data = false; Object.keys(this.files).forEach(function(file) { var d = this.files[file]; if(!data) data = d; else data = [Math.max(data[0], d[0]), Math.max(data[1], d[1])]; }, this); } else { data = this.files[filePath]; } process.nextTick(function() { if(data) { var ts = data[0] === data[1] ? data[0] + FS_ACCURACY : data[0]; if(ts >= startTime) watcher.emit("change", data[1]); } else if(this.initialScan && this.initialScanRemoved.indexOf(filePath) >= 0) { watcher.emit("remove"); } }.bind(this)); return watcher; }; DirectoryWatcher.prototype.onFileAdded = function onFileAdded(filePath, stat) { if(filePath.indexOf(this.path) !== 0) return; if(/[\\\/]/.test(filePath.substr(this.path.length + 1))) return; this.setFileTime(filePath, +stat.mtime || +stat.ctime || 1, false, "add"); }; DirectoryWatcher.prototype.onDirectoryAdded = function onDirectoryAdded(directoryPath /*, stat */) { if(directoryPath.indexOf(this.path) !== 0) return; if(/[\\\/]/.test(directoryPath.substr(this.path.length + 1))) return; this.setDirectory(directoryPath, true, false, "add"); }; DirectoryWatcher.prototype.onChange = function onChange(filePath, stat) { if(filePath.indexOf(this.path) !== 0) return; if(/[\\\/]/.test(filePath.substr(this.path.length + 1))) return; var mtime = +stat.mtime || +stat.ctime || 1; ensureFsAccuracy(mtime); this.setFileTime(filePath, mtime, false, "change"); }; DirectoryWatcher.prototype.onFileUnlinked = function onFileUnlinked(filePath) { if(filePath.indexOf(this.path) !== 0) return; if(/[\\\/]/.test(filePath.substr(this.path.length + 1))) return; this.setFileTime(filePath, null, false, "unlink"); if(this.initialScan) { this.initialScanRemoved.push(filePath); } }; DirectoryWatcher.prototype.onDirectoryUnlinked = function onDirectoryUnlinked(directoryPath) { if(directoryPath.indexOf(this.path) !== 0) return; if(/[\\\/]/.test(directoryPath.substr(this.path.length + 1))) return; this.setDirectory(directoryPath, false, false, "unlink"); if(this.initialScan) { this.initialScanRemoved.push(directoryPath); } }; DirectoryWatcher.prototype.onWatcherError = function onWatcherError(/* err */) { }; DirectoryWatcher.prototype.doInitialScan = function doInitialScan() { fs.readdir(this.path, function(err, items) { if(err) { this.parentWatcher = watcherManager.watchFile(this.path + "#directory", this.options, 1); this.parentWatcher.on("change", function(mtime, type) { if(this.watchers[withoutCase(this.path)]) { this.watchers[withoutCase(this.path)].forEach(function(w) { w.emit("change", this.path, mtime, type); }, this); } }.bind(this)); this.initialScan = false; return; } async.forEach(items, function(item, callback) { var itemPath = path.join(this.path, item); fs.stat(itemPath, function(err2, stat) { if(!this.initialScan) return; if(err2) { callback(); return; } if(stat.isFile()) { if(!this.files[itemPath]) this.setFileTime(itemPath, +stat.mtime || +stat.ctime || 1, true); } else if(stat.isDirectory()) { if(!this.directories[itemPath]) this.setDirectory(itemPath, true, true); } callback(); }.bind(this)); }.bind(this), function() { this.initialScan = false; this.initialScanRemoved = null; }.bind(this)); }.bind(this)); }; DirectoryWatcher.prototype.getTimes = function() { var obj = Object.create(null); var selfTime = 0; Object.keys(this.files).forEach(function(file) { var data = this.files[file]; var time; if(data[1]) { time = Math.max(data[0], data[1] + FS_ACCURACY); } else { time = data[0]; } obj[file] = time; if(time > selfTime) selfTime = time; }, this); if(this.nestedWatching) { Object.keys(this.directories).forEach(function(dir) { var w = this.directories[dir]; var times = w.directoryWatcher.getTimes(); Object.keys(times).forEach(function(file) { var time = times[file]; obj[file] = time; if(time > selfTime) selfTime = time; }); }, this); obj[this.path] = selfTime; } return obj; }; DirectoryWatcher.prototype.close = function() { this.initialScan = false; this.watcher.close(); if(this.nestedWatching) { Object.keys(this.directories).forEach(function(dir) { this.directories[dir].close(); }, this); } if(this.parentWatcher) this.parentWatcher.close(); this.emit("closed"); }; function ensureFsAccuracy(mtime) { if(!mtime) return; if(FS_ACCURACY > 1 && mtime % 1 !== 0) FS_ACCURACY = 1; else if(FS_ACCURACY > 10 && mtime % 10 !== 0) FS_ACCURACY = 10; else if(FS_ACCURACY > 100 && mtime % 100 !== 0) FS_ACCURACY = 100; } node-watchpack-1.6.0/lib/watcherManager.js000066400000000000000000000021151351721744200204470ustar00rootroot00000000000000/* MIT License http://www.opensource.org/licenses/mit-license.php Author Tobias Koppers @sokra */ "use strict"; var path = require("path"); function WatcherManager() { this.directoryWatchers = {}; } WatcherManager.prototype.getDirectoryWatcher = function(directory, options) { var DirectoryWatcher = require("./DirectoryWatcher"); options = options || {}; var key = directory + " " + JSON.stringify(options); if(!this.directoryWatchers[key]) { this.directoryWatchers[key] = new DirectoryWatcher(directory, options); this.directoryWatchers[key].on("closed", function() { delete this.directoryWatchers[key]; }.bind(this)); } return this.directoryWatchers[key]; }; WatcherManager.prototype.watchFile = function watchFile(p, options, startTime) { var directory = path.dirname(p); return this.getDirectoryWatcher(directory, options).watch(p, startTime); }; WatcherManager.prototype.watchDirectory = function watchDirectory(directory, options, startTime) { return this.getDirectoryWatcher(directory, options).watch(directory, startTime); }; module.exports = new WatcherManager(); node-watchpack-1.6.0/lib/watchpack.js000066400000000000000000000102401351721744200174620ustar00rootroot00000000000000/* MIT License http://www.opensource.org/licenses/mit-license.php Author Tobias Koppers @sokra */ "use strict"; var watcherManager = require("./watcherManager"); var EventEmitter = require("events").EventEmitter; function Watchpack(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); } module.exports = Watchpack; Watchpack.prototype = Object.create(EventEmitter.prototype); Watchpack.prototype.watch = function 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); }; Watchpack.prototype.close = function resume() { this.paused = true; if(this.aggregateTimeout) clearTimeout(this.aggregateTimeout); this.fileWatchers.forEach(function(w) { w.close(); }, this); this.dirWatchers.forEach(function(w) { w.close(); }, this); this.fileWatchers.length = 0; this.dirWatchers.length = 0; }; Watchpack.prototype.pause = function pause() { this.paused = true; if(this.aggregateTimeout) clearTimeout(this.aggregateTimeout); }; function addWatchersToArray(watchers, array) { watchers.forEach(function(w) { if(array.indexOf(w.directoryWatcher) < 0) { array.push(w.directoryWatcher); addWatchersToArray(Object.keys(w.directoryWatcher.directories).reduce(function(a, dir) { if(w.directoryWatcher.directories[dir] !== true) a.push(w.directoryWatcher.directories[dir]); return a; }, []), array); } }); } Watchpack.prototype.getTimes = function() { var directoryWatchers = []; addWatchersToArray(this.fileWatchers.concat(this.dirWatchers), directoryWatchers); var obj = Object.create(null); directoryWatchers.forEach(function(w) { var times = w.getTimes(); Object.keys(times).forEach(function(file) { obj[file] = times[file]; }); }); return obj; }; Watchpack.prototype._fileWatcher = function _fileWatcher(file, watcher) { watcher.on("change", function(mtime, type) { this._onChange(file, mtime, file, type); }.bind(this)); watcher.on("remove", function(type) { this._onRemove(file, file, type); }.bind(this)); return watcher; }; Watchpack.prototype._dirWatcher = function _dirWatcher(item, watcher) { watcher.on("change", function(file, mtime, type) { this._onChange(item, mtime, file, type); }.bind(this)); return watcher; }; Watchpack.prototype._onChange = function _onChange(item, mtime, file) { file = file || item; this.mtimes[file] = mtime; if(this.paused) return; this.emit("change", file, mtime); if(this.aggregateTimeout) clearTimeout(this.aggregateTimeout); if(this.aggregatedChanges.indexOf(item) < 0) this.aggregatedChanges.push(item); this.aggregateTimeout = setTimeout(this._onTimeout, this.options.aggregateTimeout); }; Watchpack.prototype._onRemove = function _onRemove(item, file) { file = file || item; delete this.mtimes[item]; if(this.paused) return; this.emit("remove", item); if(this.aggregateTimeout) clearTimeout(this.aggregateTimeout); if(this.aggregatedRemovals.indexOf(item) < 0) this.aggregatedRemovals.push(item); this.aggregateTimeout = setTimeout(this._onTimeout, this.options.aggregateTimeout); }; Watchpack.prototype._onTimeout = function _onTimeout() { this.aggregateTimeout = 0; var changes = this.aggregatedChanges; var removals = this.aggregatedRemovals; this.aggregatedChanges = []; this.aggregatedRemovals = []; this.emit("aggregated", changes, removals); }; node-watchpack-1.6.0/package.json000066400000000000000000000017561351721744200167130ustar00rootroot00000000000000{ "name": "watchpack", "version": "1.6.0", "description": "", "main": "./lib/watchpack.js", "directories": { "test": "test" }, "files": [ "lib/" ], "scripts": { "pretest": "npm run lint", "test": "mocha", "travis": "npm run cover -- --report lcovonly", "lint": "eslint lib", "precover": "npm run lint", "cover": "istanbul cover node_modules/mocha/bin/_mocha" }, "repository": { "type": "git", "url": "https://github.com/webpack/watchpack.git" }, "author": "Tobias Koppers @sokra", "license": "MIT", "bugs": { "url": "https://github.com/webpack/watchpack/issues" }, "homepage": "https://github.com/webpack/watchpack", "devDependencies": { "codecov.io": "^0.1.6", "coveralls": "^3.0.0", "eslint": "^4.18.1", "istanbul": "^0.4.3", "mocha": "^5.0.1", "rimraf": "^2.6.2", "should": "^8.3.1" }, "dependencies": { "chokidar": "^2.0.2", "graceful-fs": "^4.1.2", "neo-async": "^2.5.0" } } node-watchpack-1.6.0/playground/000077500000000000000000000000001351721744200166005ustar00rootroot00000000000000node-watchpack-1.6.0/playground/watch-folder.js000066400000000000000000000017161351721744200215220ustar00rootroot00000000000000"use strict"; var path = require("path"); var Watchpack = require("../"); var folder = path.join(__dirname, "folder"); function startWatcher(name, files, folders) { var w = new Watchpack({ aggregateTimeout: 3000 }); w.on("change", function(file, mtime) { console.log(name, "change", path.relative(folder, file), mtime); }); w.on("aggregated", function(changes) { var times = w.getTimes(); console.log(name, "aggregated", changes.map(function(file) { return path.relative(folder, file); }), Object.keys(times).reduce(function(obj, file) { obj[path.relative(folder, file)] = times[file]; return obj }, {})); }); var startTime = Date.now() - 10000; console.log(name, startTime); w.watch(files, folders, startTime); } startWatcher("folder", [], [folder]); startWatcher("sub+files", [ path.join(folder, "a.txt"), path.join(folder, "b.txt"), path.join(folder, "c.txt"), path.join(folder, "d.txt"), ], [ path.join(folder, "subfolder") ]); node-watchpack-1.6.0/test/000077500000000000000000000000001351721744200153735ustar00rootroot00000000000000node-watchpack-1.6.0/test/Assumption.js000066400000000000000000000131161351721744200200750ustar00rootroot00000000000000/*globals describe it beforeEach afterEach */ "use strict"; require("should"); var path = require("path"); var fs = require("fs"); var chokidar = require("chokidar"); var TestHelper = require("./helpers/TestHelper"); var Watchpack = require("../lib/watchpack"); var fixtures = path.join(__dirname, "fixtures"); var testHelper = new TestHelper(fixtures); describe("Assumption", function() { this.timeout(10000); var watcherToClose = null; beforeEach(testHelper.before); afterEach(function(done) { if(watcherToClose) { watcherToClose.close(); watcherToClose = null; } testHelper.after(done); }); it("should have a file system with correct mtime behavior (stats)", function(done) { this.timeout(20000); var i = 60; var count = 60; var minDiffBefore = +Infinity; var maxDiffBefore = -Infinity; var sumDiffBefore = 0; var minDiffAfter = +Infinity; var maxDiffAfter = -Infinity; var sumDiffAfter = 0; testHelper.tick(100, function checkMtime() { var before = Date.now(); testHelper.file("a"); var after = Date.now(); var s = fs.statSync(path.join(fixtures, "a")); var diffBefore = +s.mtime - before; if(diffBefore < minDiffBefore) minDiffBefore = diffBefore; if(diffBefore > maxDiffBefore) maxDiffBefore = diffBefore; sumDiffBefore += diffBefore; var diffAfter = +s.mtime - after; if(diffAfter < minDiffAfter) minDiffAfter = diffAfter; if(diffAfter > maxDiffAfter) maxDiffAfter = diffAfter; sumDiffAfter += diffAfter; if(i-- === 0) { afterMeasure(); } else { testHelper.tick(100, checkMtime); } }); function afterMeasure() { console.log("mtime stats accuracy (before): [" + minDiffBefore + " ; " + maxDiffBefore + "] avg " + Math.round(sumDiffBefore / count)); console.log("mtime stats accuracy (after): [" + minDiffAfter + " ; " + maxDiffAfter + "] avg " + Math.round(sumDiffAfter / count)); minDiffBefore.should.be.aboveOrEqual(-2000); maxDiffBefore.should.be.below(2000); minDiffAfter.should.be.aboveOrEqual(-2000); maxDiffAfter.should.be.below(2000); done(); } }); it("should have a file system with correct mtime behavior (chokidar)", function(done) { this.timeout(20000); testHelper.file("a"); var i = 60; var count = 60; var before; var after; var minDiffBefore = +Infinity; var maxDiffBefore = -Infinity; var sumDiffBefore = 0; var minDiffAfter = +Infinity; var maxDiffAfter = -Infinity; var sumDiffAfter = 0; var watcher = watcherToClose = chokidar.watch(fixtures, { ignoreInitial: true, persistent: true, followSymlinks: false, depth: 0, atomic: false, alwaysStat: true, ignorePermissionErrors: true }); testHelper.tick(100, function() { watcher.on("change", function(path, s) { if(before && after) { var diffBefore = +s.mtime - before; if(diffBefore < minDiffBefore) minDiffBefore = diffBefore; if(diffBefore > maxDiffBefore) maxDiffBefore = diffBefore; sumDiffBefore += diffBefore; var diffAfter = +s.mtime - after; if(diffAfter < minDiffAfter) minDiffAfter = diffAfter; if(diffAfter > maxDiffAfter) maxDiffAfter = diffAfter; sumDiffAfter += diffAfter; before = after = undefined; if(i-- === 0) { afterMeasure(); } else { testHelper.tick(100, checkMtime); } } }); testHelper.tick(100, checkMtime); }); function checkMtime() { before = Date.now(); testHelper.file("a"); after = Date.now(); } function afterMeasure() { console.log("mtime chokidar accuracy (before): [" + minDiffBefore + " ; " + maxDiffBefore + "] avg " + Math.round(sumDiffBefore / count)); console.log("mtime chokidar accuracy (after): [" + minDiffAfter + " ; " + maxDiffAfter + "] avg " + Math.round(sumDiffAfter / count)); minDiffBefore.should.be.aboveOrEqual(-2000); maxDiffBefore.should.be.below(2000); minDiffAfter.should.be.aboveOrEqual(-2000); maxDiffAfter.should.be.below(2000); done(); } }); it("should not fire events in subdirectories", function(done) { testHelper.dir("watch-test-directory"); var watcher = watcherToClose = chokidar.watch(fixtures, { ignoreInitial: true, persistent: true, followSymlinks: false, depth: 0, atomic: false, alwaysStat: true, ignorePermissionErrors: true }); watcher.on("add", function(arg) { done(new Error("should not be emitted " + arg)); done = function() {}; }); watcher.on("change", function(arg) { done(new Error("should not be emitted " + arg)); done = function() {}; }); watcher.on("error", function(err) { done(err); done = function() {}; }); testHelper.tick(500, function() { testHelper.file("watch-test-directory/watch-test-file"); testHelper.tick(500, function() { done(); }); }); }); [100, 200, 300, 500, 700, 1000].reverse().forEach(function(delay) { it("should fire events not after start and " + delay + "ms delay", function(done) { testHelper.file("watch-test-file-" + delay); testHelper.tick(delay, function() { var watcher = watcherToClose = chokidar.watch(fixtures, { ignoreInitial: true, persistent: true, followSymlinks: false, depth: 0, atomic: false, alwaysStat: true, ignorePermissionErrors: true }); watcher.on("add", function(arg) { done(new Error("should not be emitted " + arg)); done = function() {}; }); watcher.on("change", function(arg) { done(new Error("should not be emitted " + arg)); done = function() {}; }); watcher.on("error", function(err) { done(err); done = function() {}; }); testHelper.tick(500, function() { done(); }); }); }); }); }); node-watchpack-1.6.0/test/Casing.js000066400000000000000000000022201351721744200171310ustar00rootroot00000000000000/*globals describe it beforeEach afterEach */ "use strict"; require("should"); var path = require("path"); var TestHelper = require("./helpers/TestHelper"); var Watchpack = require("../lib/watchpack"); var fixtures = path.join(__dirname, "fixtures"); var testHelper = new TestHelper(fixtures); var fsIsCaseInsensitive; try { fsIsCaseInsensitive = require("fs").existsSync(path.join(__dirname, "..", "PACKAGE.JSON")); } catch(e) { fsIsCaseInsensitive = false; } if(fsIsCaseInsensitive) { describe("Casing", function() { this.timeout(10000); beforeEach(testHelper.before); afterEach(testHelper.after); it("should watch a file with the wrong casing", function(done) { var w = new Watchpack({ aggregateTimeout: 1000 }); var changeEvents = 0; w.on("change", function(file) { file.should.be.eql(path.join(fixtures, "a")); changeEvents++; }); w.on("aggregated", function(changes) { changes.should.be.eql([path.join(fixtures, "a")]); changeEvents.should.be.eql(1); w.close(); done(); }); w.watch([path.join(fixtures, "a")], []); testHelper.tick(function() { testHelper.file("A"); }); }); }); } node-watchpack-1.6.0/test/DirectoryWatcher.test.js000066400000000000000000000100611351721744200221670ustar00rootroot00000000000000/*globals describe it beforeEach afterEach */ "use strict"; require("should"); var path = require("path"); var TestHelper = require("./helpers/TestHelper"); var OrgDirectoryWatcher = require("../lib/DirectoryWatcher"); var fixtures = path.join(__dirname, "fixtures"); var testHelper = new TestHelper(fixtures); var openWatchers = []; var DirectoryWatcher = function(p, options) { var d = new OrgDirectoryWatcher(p, options); openWatchers.push(d); var orgClose = d.close; d.close = function() { orgClose.call(this); var idx = openWatchers.indexOf(d); if(idx < 0) throw new Error("DirectoryWatcher was already closed"); openWatchers.splice(idx, 1); }; return d; }; describe("DirectoryWatcher", function() { this.timeout(10000); beforeEach(testHelper.before); afterEach(testHelper.after); afterEach(function() { openWatchers.forEach(function(d) { console.log("DirectoryWatcher (" + d.path + ") was not closed."); d.close(); }); }); it("should detect a file creation", function(done) { var d = new DirectoryWatcher(fixtures, {}); var a = d.watch(path.join(fixtures, "a")); a.on("change", function(mtime) { mtime.should.be.type("number"); Object.keys(d.getTimes()).sort().should.be.eql([ path.join(fixtures, "a") ]); a.close(); done(); }); testHelper.tick(function() { testHelper.file("a"); }); }); it("should detect a file change", function(done) { var d = new DirectoryWatcher(fixtures, {}); testHelper.file("a"); var a = d.watch(path.join(fixtures, "a")); a.on("change", function(mtime) { mtime.should.be.type("number"); a.close(); done(); }); testHelper.tick(function() { testHelper.file("a"); }); }); it("should not detect a file change in initial scan", function(done) { testHelper.file("a"); testHelper.tick(function() { var d = new DirectoryWatcher(fixtures, {}); var a = d.watch(path.join(fixtures, "a")); a.on("change", function() { throw new Error("should not be detected"); }); testHelper.tick(function() { a.close(); done(); }); }); }); it("should detect a file change in initial scan with start date", function(done) { var start = new Date(); testHelper.tick(1000, function() { testHelper.file("a"); testHelper.tick(1000, function() { var d = new DirectoryWatcher(fixtures, {}); var a = d.watch(path.join(fixtures, "a"), start); a.on("change", function() { a.close(); done(); }); }); }); }); it("should not detect a file change in initial scan without start date", function(done) { testHelper.file("a"); testHelper.tick(200, function() { var d = new DirectoryWatcher(fixtures, {}); var a = d.watch(path.join(fixtures, "a")); a.on("change", function(mtime, type) { throw new Error("should not be detected (" + type + " mtime=" + mtime + " now=" + Date.now() + ")"); }); testHelper.tick(function() { a.close(); done(); }); }); }); var timings = { slow: 300, fast: 50 }; Object.keys(timings).forEach(function(name) { var time = timings[name]; it("should detect multiple file changes (" + name + ")", function(done) { var d = new DirectoryWatcher(fixtures, {}); testHelper.file("a"); testHelper.tick(function() { var a = d.watch(path.join(fixtures, "a")); var count = 20; var wasChanged = false; a.on("change", function(mtime) { mtime.should.be.type("number"); if(!wasChanged) return; wasChanged = false; if(count-- <= 0) { a.close(); done(); } else { testHelper.tick(time, function() { wasChanged = true; testHelper.file("a"); }); } }); testHelper.tick(function() { wasChanged = true; testHelper.file("a"); }); }); }); }); it("should detect a file removal", function(done) { testHelper.file("a"); var d = new DirectoryWatcher(fixtures, {}); var a = d.watch(path.join(fixtures, "a")); a.on("remove", function(mtime) { (typeof mtime === 'undefined').should.be.true; a.close(); done(); }); testHelper.tick(function() { testHelper.remove("a"); }); }); }); node-watchpack-1.6.0/test/Watchpack.js000066400000000000000000000412441351721744200176430ustar00rootroot00000000000000/*globals describe it beforeEach afterEach */ "use strict"; require("should"); var path = require("path"); var TestHelper = require("./helpers/TestHelper"); var Watchpack = require("../lib/watchpack"); var fixtures = path.join(__dirname, "fixtures"); var testHelper = new TestHelper(fixtures); describe("Watchpack", function() { this.timeout(10000); beforeEach(testHelper.before); afterEach(testHelper.after); it("should watch a single file", function(done) { var w = new Watchpack({ aggregateTimeout: 1000 }); var changeEvents = 0; w.on("change", function(file) { file.should.be.eql(path.join(fixtures, "a")); changeEvents++; }); w.on("aggregated", function(changes) { changes.should.be.eql([path.join(fixtures, "a")]); changeEvents.should.be.eql(1); w.close(); done(); }); w.watch([path.join(fixtures, "a")], []); testHelper.tick(function() { testHelper.file("a"); }); }); it("should watch multiple files", function(done) { var w = new Watchpack({ aggregateTimeout: 1000 }); var changeEvents = []; w.on("change", function(file) { if(changeEvents[changeEvents.length - 1] === file) return; changeEvents.push(file); }); w.on("aggregated", function(changes) { changes.sort().should.be.eql([path.join(fixtures, "a"), path.join(fixtures, "b")]); changeEvents.should.be.eql([ path.join(fixtures, "a"), path.join(fixtures, "b"), path.join(fixtures, "a"), path.join(fixtures, "b"), path.join(fixtures, "a") ]); Object.keys(w.getTimes()).sort().should.be.eql([ path.join(fixtures, "a"), path.join(fixtures, "b") ]); w.close(); done(); }); w.watch([path.join(fixtures, "a"), path.join(fixtures, "b")], []); testHelper.tick(400, function() { testHelper.file("a"); testHelper.tick(400, function() { testHelper.file("b"); testHelper.tick(400, function() { testHelper.file("a"); testHelper.tick(400, function() { testHelper.file("b"); testHelper.tick(400, function() { testHelper.file("a"); }); }); }); }); }); }); it("should watch a directory", function(done) { var w = new Watchpack({ aggregateTimeout: 1000 }); var changeEvents = []; w.on("change", function(file) { if(changeEvents[changeEvents.length - 1] === file) return; changeEvents.push(file); }); w.on("aggregated", function(changes) { changes.should.be.eql([path.join(fixtures, "dir")]); changeEvents.should.be.eql([path.join(fixtures, "dir", "a")]); w.close(); done(); }); testHelper.dir("dir"); testHelper.tick(200, function() { w.watch([], [path.join(fixtures, "dir")]); testHelper.tick(200, function() { testHelper.file(path.join("dir", "a")); }); }); }); it("should watch a file then a directory", function(done) { var w = new Watchpack({ aggregateTimeout: 1000 }); var changeEvents = []; w.on("change", function(file) { if(changeEvents[changeEvents.length - 1] === file) return; changeEvents.push(file); }); w.on("aggregated", function(changes) { changes.should.be.eql([path.join(fixtures, "dir")]); changeEvents.should.be.eql([path.join(fixtures, "dir", "a")]); w.close(); done(); }); testHelper.dir("dir"); testHelper.dir(path.join("dir", "subdir")); testHelper.file(path.join("dir", "a")); testHelper.tick(400, function() { w.watch([path.join(fixtures, "dir", "a")], []); testHelper.tick(function() { w.watch([path.join(fixtures, "dir")], [path.join(fixtures, "dir")]); testHelper.tick(function() { testHelper.file(path.join("dir", "a")); }); }); }); }); it("should watch a directory (delete file)", function(done) { var w = new Watchpack({ aggregateTimeout: 1000 }); var changeEvents = []; w.on("change", function(file) { if(changeEvents[changeEvents.length - 1] === file) return; changeEvents.push(file); }); w.on("aggregated", function(changes) { changes.should.be.eql([path.join(fixtures, "dir")]); changeEvents.should.be.eql([path.join(fixtures, "dir", "a")]); w.close(); done(); }); testHelper.dir("dir"); testHelper.file(path.join("dir", "a")); testHelper.tick(function() { w.watch([], [path.join(fixtures, "dir")]); testHelper.tick(function() { testHelper.remove(path.join("dir", "a")); }); }); }); it("should watch a directory (delete and recreate file)", function(done) { var w = new Watchpack({ aggregateTimeout: 1000 }); var changeEvents = []; w.on("change", function(file) { if(changeEvents[changeEvents.length - 1] === file) return; changeEvents.push(file); }); w.on("aggregated", function(changes) { changes.should.be.eql([path.join(fixtures, "dir")]); changeEvents.should.be.eql([ path.join(fixtures, "dir", "a"), path.join(fixtures, "dir", "b"), path.join(fixtures, "dir", "a") ]); w.close(); done(); }); testHelper.dir("dir"); testHelper.file(path.join("dir", "a")); testHelper.tick(function() { w.watch([], [path.join(fixtures, "dir")]); testHelper.tick(function() { testHelper.remove(path.join("dir", "a")); testHelper.tick(function() { testHelper.file(path.join("dir", "b")); testHelper.tick(function() { testHelper.file(path.join("dir", "a")); }); }); }); }); }); it("should watch a missing directory", function(done) { var w = new Watchpack({ aggregateTimeout: 1000 }); var changeEvents = []; w.on("change", function(file) { if(changeEvents[changeEvents.length - 1] === file) return; changeEvents.push(file); }); w.on("aggregated", function(changes) { changes.should.be.eql([path.join(fixtures, "dir", "sub")]); changeEvents.should.be.eql([path.join(fixtures, "dir", "sub")]); w.close(); done(); }); testHelper.dir("dir"); testHelper.tick(function() { w.watch([], [path.join(fixtures, "dir", "sub")]); testHelper.tick(function() { testHelper.dir(path.join("dir", "sub")); }); }); }); it("should watch a directory (add directory)", function(done) { var w = new Watchpack({ aggregateTimeout: 1000 }); var changeEvents = []; w.on("change", function(file) { if(changeEvents[changeEvents.length - 1] === file) return; changeEvents.push(file); }); w.on("aggregated", function(changes) { changes.should.be.eql([path.join(fixtures, "dir")]); changeEvents.should.be.eql([path.join(fixtures, "dir", "sub")]); w.close(); done(); }); testHelper.dir("dir"); testHelper.tick(function() { w.watch([], [path.join(fixtures, "dir")]); testHelper.tick(function() { testHelper.dir(path.join("dir", "sub")); }); }); }); it("should watch a directory (delete directory)", function(done) { var w = new Watchpack({ aggregateTimeout: 1000 }); var changeEvents = []; w.on("change", function(file) { if(changeEvents[changeEvents.length - 1] === file) return; changeEvents.push(file); }); w.on("aggregated", function(changes) { changes.should.be.eql([path.join(fixtures, "dir")]); w.close(); done(); }); testHelper.dir("dir"); testHelper.dir(path.join("dir", "sub")); testHelper.file(path.join("dir", "sub", "a")); testHelper.tick(function() { w.watch([], [path.join(fixtures, "dir")]); testHelper.tick(function() { testHelper.remove(path.join("dir", "sub")); }); }); }); it("should watch a directory (delete directory2)", function(done) { var w = new Watchpack({ aggregateTimeout: 1000 }); var changeEvents = []; w.on("change", function(file) { if(changeEvents[changeEvents.length - 1] === file) return; changeEvents.push(file); }); w.on("aggregated", function(changes) { changes.should.be.eql([path.join(fixtures, "dir")]); changeEvents.should.be.eql([path.join(fixtures, "dir", "sub")]); w.close(); done(); }); testHelper.dir("dir"); testHelper.dir(path.join("dir", "sub")); testHelper.tick(function() { w.watch([], [path.join(fixtures, "dir")]); testHelper.tick(function() { testHelper.remove(path.join("dir", "sub")); }); }); }); it("should watch already watched directory", function(done) { var w = new Watchpack({ aggregateTimeout: 1000 }); var changeEvents = []; w.on("change", function(file) { if(changeEvents[changeEvents.length - 1] === file) return; changeEvents.push(file); }); w.on("aggregated", function(changes) { changes.should.be.eql([path.join(fixtures, "dir")]); changeEvents.should.be.eql([path.join(fixtures, "dir", "a")]); w.close(); done(); }); testHelper.dir("dir"); testHelper.file(path.join("dir", "a")); testHelper.tick(400, function() { w.watch([path.join(fixtures, "dir", "a")], []); testHelper.tick(1000, function() { w.watch([], [path.join(fixtures, "dir")]); testHelper.tick(400, function() { testHelper.remove(path.join("dir", "a")); }); }); }); }); it("should watch file in a sub directory", function(done) { var w = new Watchpack({ aggregateTimeout: 1000 }); var changeEvents = []; w.on("change", function(file) { if(changeEvents[changeEvents.length - 1] === file) return; changeEvents.push(file); }); w.on("aggregated", function(changes) { changes.should.be.eql([path.join(fixtures, "dir")]); changeEvents.should.be.eql([path.join(fixtures, "dir", "sub", "a")]); var times = w.getTimes(); times[path.join(fixtures, "dir")].should.be.type("number"); times[path.join(fixtures, "dir")].should.be.eql(times[path.join(fixtures, "dir", "sub", "a")]); times[path.join(fixtures, "dir", "sub")].should.be.eql(times[path.join(fixtures, "dir", "sub", "a")]); w.close(); done(); }); testHelper.dir("dir"); testHelper.dir(path.join("dir", "sub")); testHelper.tick(function() { w.watch([], [path.join(fixtures, "dir")]); testHelper.tick(function() { testHelper.file(path.join("dir", "sub", "a")); }); }); }); it("should watch file in a sub sub directory", function(done) { var w = new Watchpack({ aggregateTimeout: 1000 }); var changeEvents = []; w.on("change", function(file) { if(changeEvents[changeEvents.length - 1] === file) return; changeEvents.push(file); }); w.on("aggregated", function(changes) { changes.should.be.eql([path.join(fixtures, "dir")]); changeEvents.should.be.eql([path.join(fixtures, "dir", "sub", "sub", "a")]); Object.keys(w.getTimes()).sort().should.be.eql([ path.join(fixtures, "dir"), path.join(fixtures, "dir", "sub"), path.join(fixtures, "dir", "sub", "sub"), path.join(fixtures, "dir", "sub", "sub", "a") ]); w.close(); done(); }); testHelper.dir("dir"); testHelper.dir(path.join("dir", "sub")); testHelper.dir(path.join("dir", "sub", "sub")); testHelper.tick(function() { w.watch([], [path.join(fixtures, "dir")]); testHelper.tick(function() { testHelper.file(path.join("dir", "sub", "sub", "a")); }); }); }); it("should watch file in a directory that contains special glob characters", function(done) { var w = new Watchpack({ aggregateTimeout: 1000 }); var changeEvents = []; w.on("change", function(file) { if(changeEvents[changeEvents.length - 1] === file) return; changeEvents.push(file); }); w.on("aggregated", function(changes) { changes.should.be.eql([path.join(fixtures, "dir")]); changeEvents.should.be.eql([path.join(fixtures, "dir", "sub()", "a")]); var times = w.getTimes(); times[path.join(fixtures, "dir")].should.be.type("number"); times[path.join(fixtures, "dir")].should.be.eql(times[path.join(fixtures, "dir", "sub()", "a")]); times[path.join(fixtures, "dir", "sub()")].should.be.eql(times[path.join(fixtures, "dir", "sub()", "a")]); w.close(); done(); }); testHelper.dir("dir"); testHelper.dir(path.join("dir", "sub()")); testHelper.tick(function() { w.watch([], [path.join(fixtures, "dir")]); testHelper.tick(function() { testHelper.file(path.join("dir", "sub()", "a")); }); }); }); it("should detect a single change to future timestamps", function(done) { var w = new Watchpack({ aggregateTimeout: 1000 }); var w2 = new Watchpack({ aggregateTimeout: 1000 }); w.on("change", function() { throw new Error("should not report change event"); }); w.on("aggregated", function() { throw new Error("should not report aggregated event"); }); testHelper.file("a"); testHelper.tick(400, function() { w2.watch([path.join(fixtures, "a")], []); testHelper.tick(1000, function() { // wait for initial scan testHelper.mtime("a", Date.now() + 1000000); testHelper.tick(400, function() { w.watch([path.join(fixtures, "a")], []); testHelper.tick(1000, function() { w2.close(); w.close(); done(); }); }); }); }); }); it("should detect a past change to a file (timestamp)", function(done) { var w = new Watchpack({ aggregateTimeout: 1000 }); var changeEvents = 0; w.on("change", function(file) { file.should.be.eql(path.join(fixtures, "a")); changeEvents++; }); w.on("aggregated", function(changes) { changes.should.be.eql([path.join(fixtures, "a")]); changeEvents.should.be.greaterThan(0); w.close(); done(); }); var startTime = Date.now(); testHelper.tick(function() { testHelper.file("a"); testHelper.tick(function() { w.watch([path.join(fixtures, "a")], [], startTime); }); }); }); it("should not detect a past change to a file (watched)", function(done) { var w2 = new Watchpack(); var w = new Watchpack(); w.on("change", function() { throw new Error("Should not be detected"); }); testHelper.tick(function() { testHelper.file("b"); w2.watch([path.join(fixtures, "b")], []); testHelper.tick(1000, function() { // wait for stable state testHelper.file("a"); testHelper.tick(1000, function() { var startTime = Date.now(); testHelper.tick(400, function() { w.watch([path.join(fixtures, "a")], [], startTime); testHelper.tick(1000, function() { w.close(); w2.close(); done(); }); }); }); }); }); }); it("should detect a past change to a file (watched)", function(done) { var w2 = new Watchpack(); var w = new Watchpack(); var changeEvents = 0; w.on("change", function(file) { file.should.be.eql(path.join(fixtures, "a")); changeEvents++; }); w.on("aggregated", function(changes) { changes.should.be.eql([path.join(fixtures, "a")]); changeEvents.should.be.eql(1); w.close(); w2.close(); done(); }); testHelper.tick(function() { testHelper.file("b"); w2.watch([path.join(fixtures, "b")], []); testHelper.tick(function() { var startTime = Date.now(); testHelper.tick(function() { testHelper.file("a"); testHelper.tick(400, function() { w.watch([path.join(fixtures, "a")], [], startTime); }); }); }); }); }); it("should watch a single file removal", function(done) { testHelper.file("a"); var w = new Watchpack({ aggregateTimeout: 1000 }); var removeEvents = 0; w.on("remove", function(file) { file.should.be.eql(path.join(fixtures, "a")); removeEvents++; }); w.on("aggregated", function(changes, removals) { removals.should.be.eql([path.join(fixtures, "a")]); removeEvents.should.be.eql(1); w.close(); done(); }); testHelper.tick(400, function() { w.watch([path.join(fixtures, "a")], []); testHelper.tick(function() { testHelper.remove("a"); }); }); }); it("should watch multiple file removals", function(done) { var step = 0; testHelper.file("a"); testHelper.file("b"); var w = new Watchpack({ aggregateTimeout: 1500 }); var removeEvents = []; w.on("remove", function(file) { if(removeEvents[removeEvents.length - 1] === file) return; removeEvents.push(file); }); w.on("aggregated", function(changes, removals) { step.should.be.eql(6); removals.sort().should.be.eql([path.join(fixtures, "a"), path.join(fixtures, "b")]); removeEvents.should.be.eql([ path.join(fixtures, "a"), path.join(fixtures, "b"), path.join(fixtures, "a"), path.join(fixtures, "b"), ]); Object.keys(w.getTimes()).sort().should.be.eql([ path.join(fixtures, "a"), path.join(fixtures, "b") ]); w.close(); done(); }); testHelper.tick(400, function() { w.watch([path.join(fixtures, "a"), path.join(fixtures, "b")], []); step = 1; testHelper.tick(1000, function() { testHelper.remove("a"); step = 2; testHelper.tick(function() { testHelper.remove("b"); step = 3; testHelper.tick(1000, function() { testHelper.file("a"); testHelper.file("b"); step = 4; testHelper.tick(1000, function() { testHelper.remove("a"); step = 5; testHelper.tick(function() { testHelper.remove("b"); step = 6; }); }); }); }); }); }); }); }); node-watchpack-1.6.0/test/helpers/000077500000000000000000000000001351721744200170355ustar00rootroot00000000000000node-watchpack-1.6.0/test/helpers/TestHelper.js000066400000000000000000000031561351721744200214570ustar00rootroot00000000000000"use strict"; var fs = require("fs"); var path = require("path"); var rimraf = require("rimraf"); var watcherManager = require("../../lib/watcherManager"); function TestHelper(testdir) { this.testdir = testdir; var self = this; this.before = function(done) { self._before(done); }; this.after = function(done) { self._after(done); }; } module.exports = TestHelper; TestHelper.prototype._before = function before(done) { Object.keys(watcherManager.directoryWatchers).should.be.eql([]); this.tick(400, function() { rimraf.sync(this.testdir); fs.mkdirSync(this.testdir); done(); }.bind(this)); }; TestHelper.prototype._after = function after(done) { var i = 0; this.tick(300, function del() { try { rimraf.sync(this.testdir); } catch(e) { if(i++ > 20) throw e; this.tick(100, del.bind(this)); return; } Object.keys(watcherManager.directoryWatchers).should.be.eql([]); this.tick(300, done); }.bind(this)); }; TestHelper.prototype.dir = function dir(name) { fs.mkdirSync(path.join(this.testdir, name)); }; TestHelper.prototype.file = function file(name) { fs.writeFileSync(path.join(this.testdir, name), Math.random() + "", "utf-8"); }; TestHelper.prototype.mtime = function mtime(name, mtime) { var stats = fs.statSync(path.join(this.testdir, name)); fs.utimesSync(path.join(this.testdir, name), stats.atime, new Date(mtime)); }; TestHelper.prototype.remove = function remove(name) { rimraf.sync(path.join(this.testdir, name)); }; TestHelper.prototype.tick = function tick(arg, fn) { if(typeof arg === "function") { fn = arg; arg = 100; } setTimeout(function() { fn(); }, arg); }; node-watchpack-1.6.0/test/mocha.opts000066400000000000000000000000351351721744200173670ustar00rootroot00000000000000--full-trace --reporter spec