"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(); } }); };