Files
meteor/tools/safe-pathwatcher.js
David Glasser 5d17598584 Update pathwatcher, which took our PR
Our PR landed in 4.1.0, so we no longer need a fork.

Hopefully, the runas-related bug which causes dev bundle builds to
sometimes fail (which is why we moved the install to its own step) is
fixed with the newer version of runas used by pathwatcher now.

Note that we were previous using a fork with an early version of our PR,
which put the numeric errno on 'code'. The accepted version of the PR
puts the numeric errno on 'errno' (and in Node 0.12, puts a string on
'code').
2015-02-16 14:34:54 -08:00

105 lines
3.9 KiB
JavaScript

var files = require("./files.js");
// Set METEOR_WATCH_FORCE_POLLING environment variable to a truthy value to
// force the use of files.watchFile instead of pathwatcher.watch.
// Enabled on Mac and Linux and disabled on Windows by default.
var PATHWATCHER_ENABLED = !process.env.METEOR_WATCH_FORCE_POLLING;
if (process.platform === "win32") {
PATHWATCHER_ENABLED = false;
}
var DEFAULT_POLLING_INTERVAL =
~~process.env.METEOR_WATCH_POLLING_INTERVAL_MS || 5000;
var NO_PATHWATCHER_POLLING_INTERVAL =
~~process.env.METEOR_WATCH_POLLING_INTERVAL_MS || 500;
var suggestedRaisingWatchLimit = false;
exports.watch = function watch(absPath, callback) {
var lastPathwatcherEventTime = 0;
function pathwatcherWrapper() {
// It's tempting to call files.unwatchFile(absPath, watchFileWrapper)
// here, but previous pathwatcher success is no guarantee of future
// pathwatcher reliability. For example, pathwatcher works just fine
// when file changes originate from within a Vagrant VM, but changes
// to shared files made outside the VM are invisible to pathwatcher,
// so our only hope of catching them is to continue polling.
lastPathwatcherEventTime = +new Date;
callback.apply(this, arguments);
}
var watcher = null;
if (PATHWATCHER_ENABLED) {
try {
watcher = files.pathwatcherWatch(absPath, pathwatcherWrapper);
} catch (e) {
// If it isn't an actual pathwatcher failure, rethrow.
if (e.message !== 'Unable to watch path')
throw e;
var constants = require('constants');
var archinfo = require('./archinfo.js');
if (! suggestedRaisingWatchLimit &&
// Note: the not-super-documented require('constants') maps from
// strings to SYSTEM errno values. System errno values aren't the same
// as the numbers used internally by libuv! Once we're upgraded
// to Node 0.12, we'll have the system errno as a string (on 'code'),
// but the support for that wasn't in Node 0.10's uv.
// See our PR https://github.com/atom/node-pathwatcher/pull/53
// (and make sure to read the final commit message, not the original
// proposed PR, which had a slightly different interface).
e.errno === constants.ENOSPC &&
// The only suggestion we currently have is for Linux.
archinfo.matches(archinfo.host(), 'os.linux')) {
suggestedRaisingWatchLimit = true;
var Console = require('./console.js').Console;
Console.arrowWarn(
"It looks like a simple tweak to your system's configuration will " +
"make many tools (including this Meteor command) more efficient. " +
"To learn more, see " +
Console.url("https://github.com/meteor/meteor/wiki/File-Change-Watcher-Efficiency"));
}
// ... ignore the error. We'll still have watchFile, which is good
// enough.
}
};
var pollingInterval = watcher
? DEFAULT_POLLING_INTERVAL : NO_PATHWATCHER_POLLING_INTERVAL;
function watchFileWrapper() {
// If a pathwatcher event fired in the last polling interval, ignore
// this event.
if (new Date - lastPathwatcherEventTime > pollingInterval) {
callback.apply(this, arguments);
}
}
// We use files.watchFile in addition to pathwatcher.watch as a fail-safe to
// detect file changes even on network file systems. However (unless the user
// disabled pathwatcher or this pathwatcher call failed), we use a relatively
// long default polling interval of 5000ms to save CPU cycles.
files.watchFile(absPath, {
persistent: false,
interval: pollingInterval
}, watchFileWrapper);
var polling = true;
return {
close: function close() {
if (watcher) {
watcher.close();
watcher = null;
}
if (polling) {
polling = false;
files.unwatchFile(absPath, watchFileWrapper);
}
}
};
};