lib/watcher.js
function DirWatcher(inputPath, persistent) {
    assert.ok(this instanceof DirWatcher);

    var self = this;
    var absPath = path.resolve(inputPath);

    if (!fs.statSync(absPath).isDirectory()) {
        throw new Error(inputPath + "is not a directory!");
    }

    EventEmitter.call(self);

    self.ready = false;
    self.on("ready", function(){
        self.ready = true;
    });

    Object.defineProperties(self, {
        // Map of absDirPaths to fs.FSWatcher objects from fs.watch().
        watchers: { value: {} },
        dirContents: { value: {} },
        rootPath: { value: absPath },
        persistent: { value: !!persistent }
    });

    process.nextTick(function() {
        self.add(absPath);
        self.emit("ready");
    });
}
function DirWatcher(inputPath, persistent) {
    assert.ok(this instanceof DirWatcher);

    var self = this;
    var absPath = path.resolve(inputPath);

    if (!fs.statSync(absPath).isDirectory()) {
        throw new Error(inputPath + "is not a directory!");
    }

    EventEmitter.call(self);

    self.ready = false;
    self.on("ready", function(){
        self.ready = true;
    });

    Object.defineProperties(self, {
        // Map of absDirPaths to fs.FSWatcher objects from fs.watch().
        watchers: { value: {} },
        dirContents: { value: {} },
        rootPath: { value: absPath },
        persistent: { value: !!persistent }
    });

    process.nextTick(function() {
        self.add(absPath);
        self.emit("ready");
    });
}
DirWatcher code adapted from Jeffrey Lin's original implementation:
https://github.com/jeffreylin/jsx_transformer_fun/blob/master/dirWatcher.js

Invariant: this only watches the dir inode, not the actual path.
That means the dir can't be renamed and swapped with another dir.