You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
188 lines
5.7 KiB
JavaScript
188 lines
5.7 KiB
JavaScript
7 years ago
|
var path = require('path');
|
||
|
var fs = require('fs');
|
||
|
var readdirp = require('readdirp');
|
||
|
var use_fs_watch = process.platform === 'win32' || process.env.USE_FS_WATCH;
|
||
|
|
||
|
module.exports = function() {
|
||
|
var watched_files = {};
|
||
|
var watched_directories = {};
|
||
|
var check_dir_pause = 1000;
|
||
|
var checkInterval = undefined;
|
||
|
|
||
|
// @api public
|
||
|
// Watches the directory passed and its contained files
|
||
|
// accepts args as an object.
|
||
|
|
||
|
// @param root(string): the root directory to watch
|
||
|
// @param fileFilter(array): ignore these files
|
||
|
// @param directoryFilter(array): ignore these files
|
||
|
// @param listener(fn(file)): on file change event this will be called
|
||
|
// @param complete(fn): on complete of file watching setup
|
||
|
function watchDirectory(args) {
|
||
|
readdirp({ root: args.root, fileFilter: args.fileFilter, directoryFilter: args.directoryFilter }, function(err, res) {
|
||
|
res.files.forEach(function(file) {
|
||
|
watchFile(file, args.listener, args.partial);
|
||
|
});
|
||
|
typeof args.complete == "function" && args.complete();
|
||
|
});
|
||
|
|
||
|
!args.partial && (checkInterval = setInterval(function() {checkDirectory(args)}, check_dir_pause));
|
||
|
}
|
||
|
|
||
|
// @api public
|
||
|
// Watches the files passed
|
||
|
// accepts args as an object.
|
||
|
// @param files(array): a list of files to watch
|
||
|
// @param listener(fn(file)): on file change event this will be called
|
||
|
// @param complete(fn): on complete of file watching setup
|
||
|
function watchFiles(args) {
|
||
|
args.files.forEach(function(file) {
|
||
|
var o = {
|
||
|
fullPath: fs.realpathSync(file),
|
||
|
name: fs.realpathSync(file).split('/').pop()
|
||
|
};
|
||
|
o.fullParentDir = o.fullPath.split('/').slice(0, o.fullPath.split('/').length - 1).join('/')
|
||
|
|
||
|
watchFile(o, args.listener);
|
||
|
});
|
||
|
|
||
|
typeof args.complete == "function" && args.complete();
|
||
|
}
|
||
|
|
||
|
function unwatchAll() {
|
||
|
if (use_fs_watch) {
|
||
|
Object.keys(watched_files).forEach(function(key) {
|
||
|
watched_files[key].close();
|
||
|
});
|
||
|
} else {
|
||
|
Object.keys(watched_files).forEach(function(key) {
|
||
|
fs.unwatchFile(key);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
clearInterval(checkInterval);
|
||
|
watched_files = {};
|
||
|
watched_directories = {};
|
||
|
}
|
||
|
|
||
|
// Checks to see if something in the directory has changed
|
||
|
function checkDirectory(args) {
|
||
|
Object.keys(watched_directories).forEach(function(path) {
|
||
|
var lastModified = watched_directories[path];
|
||
|
fs.stat(path, function(err, stats) {
|
||
|
var stats_stamp = lastModified;
|
||
|
if (!err) {
|
||
|
stats_stamp = (new Date(stats.mtime)).getTime();
|
||
|
}
|
||
|
if (stats_stamp != lastModified) {
|
||
|
watched_directories[path] = stats_stamp;
|
||
|
watchDirectory({
|
||
|
root: path,
|
||
|
listener: args.listener,
|
||
|
fileFilter: args.fileFilter,
|
||
|
directoryFilter: args.directoryFilter,
|
||
|
partial: true
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// sets the absolute path to the file from the current working dir
|
||
|
function setAbsolutePath(file) {
|
||
|
file.absolutePath = path.resolve(process.cwd(), file.fullPath);
|
||
|
return file.absolutePath;
|
||
|
}
|
||
|
|
||
|
// Watches the file passed and its containing directory
|
||
|
// on change calls given listener with file object
|
||
|
function watchFile(file, cb, partial) {
|
||
|
setAbsolutePath(file);
|
||
|
storeDirectory(file);
|
||
|
if (!watched_files[file.fullPath]) {
|
||
|
if (use_fs_watch) {
|
||
|
(function() {
|
||
|
watched_files[file.fullPath] = fs.watch(file.fullPath, function() {
|
||
|
typeof cb === "function" && cb(file);
|
||
|
});
|
||
|
partial && cb(file);
|
||
|
})(file, cb);
|
||
|
} else {
|
||
|
(function(file, cb) {
|
||
|
watched_files[file.fullPath] = true;
|
||
|
fs.watchFile(file.fullPath, {interval: 150}, function() {
|
||
|
typeof cb === "function" && cb(file);
|
||
|
});
|
||
|
partial && cb(file);
|
||
|
})(file, cb);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Sets up a store of the folders being watched
|
||
|
// and saves the last modification timestamp for it
|
||
|
function storeDirectory(file) {
|
||
|
var directory = file.fullParentDir;
|
||
|
if (!watched_directories[directory]) {
|
||
|
fs.stat(directory, function(err, stats) {
|
||
|
if (err) {
|
||
|
watched_directories[directory] = (new Date).getTime();
|
||
|
} else {
|
||
|
watched_directories[directory] = (new Date(stats.mtime)).getTime();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// distinguish between files and directories
|
||
|
// @returns {Object} contains directories and files array
|
||
|
|
||
|
function distinguishPaths(paths) {
|
||
|
paths = Array.isArray(paths) ? paths : [paths];
|
||
|
var result = {
|
||
|
directories: [],
|
||
|
files: []
|
||
|
};
|
||
|
paths.forEach(function(name) {
|
||
|
if (fs.statSync(name).isDirectory()) {
|
||
|
result.directories.push(name);
|
||
|
} else {
|
||
|
result.files.push(name);
|
||
|
}
|
||
|
});
|
||
|
return result;
|
||
|
};
|
||
|
|
||
|
// for functions accepts an object as paramter
|
||
|
// copy the object and modify with attributes
|
||
|
function extend(prototype, attributes) {
|
||
|
var object = {};
|
||
|
Object.keys(prototype).forEach(function(key) {
|
||
|
object[key] = prototype[key];
|
||
|
});
|
||
|
Object.keys(attributes).forEach(function(key) {
|
||
|
object[key] = attributes[key];
|
||
|
});
|
||
|
return object;
|
||
|
};
|
||
|
|
||
|
// watch files if the paths refer to files, or directories
|
||
|
function watchPaths(args) {
|
||
|
var result = distinguishPaths(args.path)
|
||
|
if (result.directories.length) {
|
||
|
result.directories.forEach(function(directory) {
|
||
|
watchDirectory(extend(args, {root: directory}));
|
||
|
});
|
||
|
}
|
||
|
if (result.files.length)
|
||
|
watchFiles(extend(args, {files: result.files}));
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
watchDirectory: watchDirectory,
|
||
|
watchFiles: watchFiles,
|
||
|
watchPaths: watchPaths,
|
||
|
unwatchAll: unwatchAll
|
||
|
};
|
||
|
}
|