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.
99 lines
2.8 KiB
JavaScript
99 lines
2.8 KiB
JavaScript
"use strict";
|
|
|
|
const Promise = require("bluebird");
|
|
const chokidar = require("chokidar");
|
|
const createFileSource = require("./file-source");
|
|
const fs = Promise.promisifyAll(require("fs"));
|
|
const path = require("path");
|
|
|
|
const splitPath = require("./split-path");
|
|
|
|
/* FIXME: Add deletion propagation support, based on correlating input files to output files */
|
|
module.exports = function watch(pattern, options = {}) {
|
|
let watcher;
|
|
let basePath;
|
|
|
|
if (options.basePath != null) {
|
|
/* FIXME: Verify that, if an explicit basePath is specified, it's not deeper in the path than the automatically-determined one (for any of the specified patterns); otherwise file-paths-relative-to-the-base-path no longer make any sense. */
|
|
basePath = options.basePath;
|
|
} else {
|
|
if (typeof pattern === "string") {
|
|
let pathSegments = splitPath(pattern);
|
|
|
|
let firstWildcardSegment = pathSegments.findIndex((segment) => {
|
|
return segment.includes("*");
|
|
});
|
|
|
|
if (firstWildcardSegment === -1) {
|
|
/* Assume an exact file path, and treat its folder as the base folder */
|
|
basePath = path.dirname(pattern);
|
|
} else {
|
|
/* Assume a path containing a wildcard, and treat the last non-wildcard segment as the base folder */
|
|
basePath = pathSegments.slice(0, firstWildcardSegment).join(path.sep);
|
|
}
|
|
} else {
|
|
throw new Error("When specifying multiple patterns to watch, you must explicitly specify a basePath in the options");
|
|
}
|
|
}
|
|
|
|
return createFileSource({
|
|
displayName: `Watch for changes (${pattern})`,
|
|
supportsTeardown: true,
|
|
basePath: basePath,
|
|
initialize: ({options, resolvePath, pushFile, reportError}) => {
|
|
function push(filePath, stats) {
|
|
return Promise.try(() => {
|
|
/* FIXME: Define more sensible resolvePath semantics */
|
|
let resolvedPath = resolvePath(filePath);
|
|
|
|
let metadata = {
|
|
isVirtual: false,
|
|
path: resolvedPath,
|
|
lastModified: stats.mtime
|
|
};
|
|
|
|
/* FIXME: File object format validation */
|
|
if (options.supportsStreams) {
|
|
pushFile({
|
|
metadata: {
|
|
isStream: true,
|
|
... metadata
|
|
},
|
|
stream: fs.createReadStream(resolvedPath)
|
|
});
|
|
} else {
|
|
return Promise.try(() => {
|
|
return fs.readFileAsync(resolvedPath);
|
|
}).then((contents) => {
|
|
pushFile({
|
|
metadata: {
|
|
isStream: false,
|
|
... metadata
|
|
},
|
|
contents: contents
|
|
});
|
|
});
|
|
}
|
|
}).catch((err) => {
|
|
reportError(err, false);
|
|
});
|
|
}
|
|
|
|
watcher = chokidar.watch(pattern, {alwaysStat: true})
|
|
.on("add", (path, stats) => {
|
|
push(path, stats);
|
|
})
|
|
.on("change", (path, stats) => {
|
|
push(path, stats);
|
|
})
|
|
.on("error", (err) => {
|
|
/* FIXME: Determine whether the error is fatal */
|
|
reportError(err, false);
|
|
});
|
|
},
|
|
teardown: function () {
|
|
watcher.close();
|
|
}
|
|
});
|
|
};
|