turbowatch
Version:
Extremely fast file change detector and task orchestrator for Node.js.
169 lines • 6.61 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.watch = void 0;
const TurboWatcher_1 = require("./backends/TurboWatcher");
const generateShortId_1 = require("./generateShortId");
const Logger_1 = require("./Logger");
const subscribe_1 = require("./subscribe");
const testExpression_1 = require("./testExpression");
const serialize_error_1 = require("serialize-error");
const throttle_debounce_1 = require("throttle-debounce");
const log = Logger_1.Logger.child({
namespace: 'watch',
});
const watch = (configurationInput) => {
var _a, _b, _c, _d, _e;
const { cwd, project, triggers, debounce: userDebounce, Watcher, } = {
// as far as I can tell, this is a bug in unicorn/no-unused-properties
// https://github.com/sindresorhus/eslint-plugin-unicorn/issues/2051
// eslint-disable-next-line unicorn/no-unused-properties
debounce: {
wait: 1000,
},
// eslint-disable-next-line unicorn/no-unused-properties
Watcher: TurboWatcher_1.TurboWatcher,
...configurationInput,
};
const abortController = new AbortController();
const abortSignal = abortController.signal;
let discoveredFileCount = 0;
const indexingIntervalId = setInterval(() => {
log.trace('indexed %d %s...', discoveredFileCount, discoveredFileCount === 1 ? 'file' : 'files');
}, 5000);
const subscriptions = [];
const watcher = new Watcher(project);
let shuttingDown = false;
const shutdown = async () => {
if (shuttingDown) {
return;
}
shuttingDown = true;
// eslint-disable-next-line promise/prefer-await-to-then
await watcher.close();
clearInterval(indexingIntervalId);
abortController.abort();
for (const subscription of subscriptions) {
const { activeTask } = subscription;
if (activeTask === null || activeTask === void 0 ? void 0 : activeTask.promise) {
await (activeTask === null || activeTask === void 0 ? void 0 : activeTask.promise);
}
}
for (const subscription of subscriptions) {
const { teardown } = subscription;
if (teardown) {
await teardown();
}
}
};
if (abortSignal) {
abortSignal.addEventListener('abort', () => {
shutdown();
}, {
once: true,
});
}
for (const trigger of triggers) {
subscriptions.push((0, subscribe_1.subscribe)({
abortSignal,
cwd,
expression: trigger.expression,
id: (0, generateShortId_1.generateShortId)(),
initialRun: (_a = trigger.initialRun) !== null && _a !== void 0 ? _a : true,
interruptible: (_b = trigger.interruptible) !== null && _b !== void 0 ? _b : true,
name: trigger.name,
onChange: trigger.onChange,
onTeardown: trigger.onTeardown,
persistent: (_c = trigger.persistent) !== null && _c !== void 0 ? _c : false,
retry: (_d = trigger.retry) !== null && _d !== void 0 ? _d : {
retries: 0,
},
throttleOutput: (_e = trigger.throttleOutput) !== null && _e !== void 0 ? _e : { delay: 1000 },
}));
}
let queuedFileChangeEvents = [];
const evaluateSubscribers = (0, throttle_debounce_1.debounce)(userDebounce.wait, () => {
const currentFileChangeEvents = queuedFileChangeEvents;
queuedFileChangeEvents = [];
for (const subscription of subscriptions) {
const relevantEvents = currentFileChangeEvents.filter((fileChangeEvent) => {
return (0, testExpression_1.testExpression)(subscription.expression, fileChangeEvent.filename);
});
if (relevantEvents.length) {
if (abortSignal === null || abortSignal === void 0 ? void 0 : abortSignal.aborted) {
return;
}
void subscription.trigger(relevantEvents);
}
}
}, {
noLeading: true,
});
let ready = false;
const discoveredFiles = [];
watcher.on('change', ({ filename }) => {
if (ready) {
queuedFileChangeEvents.push({
filename,
});
evaluateSubscribers();
}
else {
if (discoveredFiles.length < 10) {
discoveredFiles.push(filename);
}
discoveredFileCount++;
}
});
return new Promise((resolve, reject) => {
watcher.on('error', (error) => {
log.error({
error: (0, serialize_error_1.serializeError)(error),
}, 'could not watch project');
if (ready) {
shutdown();
}
else {
reject(error);
}
});
watcher.on('ready', () => {
ready = true;
clearInterval(indexingIntervalId);
if (discoveredFiles.length > 10) {
log.trace({
files: discoveredFiles.slice(0, 10).map((file) => {
return file;
}),
}, 'discovered %d files in %s; showing first 10', discoveredFileCount, project);
}
else if (discoveredFiles.length > 0) {
log.trace({
files: discoveredFiles.map((file) => {
return file;
}),
}, 'discovered %d %s in %s', discoveredFileCount, discoveredFiles.length === 1 ? 'file' : 'files', project);
}
log.info('triggering initial runs');
const initialRuns = [];
for (const subscription of subscriptions) {
if (subscription.initialRun && !subscription.persistent) {
initialRuns.push(subscription.trigger([]));
}
}
// eslint-disable-next-line promise/prefer-await-to-then
void Promise.allSettled(initialRuns).then(() => {
for (const subscription of subscriptions) {
if (subscription.initialRun && subscription.persistent) {
void subscription.trigger([]);
}
}
log.info('ready for file changes');
resolve({
shutdown,
});
});
});
});
};
exports.watch = watch;
//# sourceMappingURL=watch.js.map