UNPKG

turbowatch

Version:

Extremely fast file change detector and task orchestrator for Node.js.

182 lines 6.72 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.subscribe = void 0; const createSpawn_1 = require("./createSpawn"); const generateShortId_1 = require("./generateShortId"); const Logger_1 = require("./Logger"); const p_retry_1 = __importDefault(require("p-retry")); const log = Logger_1.Logger.child({ namespace: 'subscribe', }); const subscribe = (trigger) => { let activeTask = null; let first = true; let fileChangeEventQueue = []; const handleSubscriptionEvent = async () => { let currentFirst = first; if (first) { currentFirst = true; first = false; } let abortController = null; if (trigger.interruptible) { abortController = new AbortController(); } let abortSignal = abortController === null || abortController === void 0 ? void 0 : abortController.signal; if (abortSignal && trigger.abortSignal) { trigger.abortSignal.addEventListener('abort', () => { abortController === null || abortController === void 0 ? void 0 : abortController.abort(); }); } else if (trigger.abortSignal) { abortSignal = trigger.abortSignal; } if (activeTask) { if (trigger.interruptible) { log.warn('%s (%s): aborted task', trigger.name, activeTask.id); if (!activeTask.abortController) { throw new Error('Expected abort controller to be set'); } activeTask.abortController.abort(); activeTask = null; } else { if (trigger.persistent) { log.warn('%s (%s): ignoring event because the trigger is persistent', trigger.name, activeTask.id); return undefined; } log.warn('%s (%s): waiting for task to complete', trigger.name, activeTask.id); if (activeTask.queued) { return undefined; } activeTask.queued = true; try { await activeTask.promise; } catch (_a) { // nothing to do } } } const affectedPaths = []; const event = { files: fileChangeEventQueue .filter(({ filename }) => { if (affectedPaths.includes(filename)) { return false; } affectedPaths.push(filename); return true; }) .map(({ filename }) => { return { name: filename, }; }), }; fileChangeEventQueue = []; const taskId = (0, generateShortId_1.generateShortId)(); if (trigger.initialRun && currentFirst) { log.debug('%s (%s): initial run...', trigger.name, taskId); } else if (event.files.length > 10) { log.debug({ files: event.files.slice(0, 10).map((file) => { return file.name; }), }, '%s (%s): %d files changed; showing first 10', trigger.name, taskId, event.files.length); } else { log.debug({ files: event.files.map((file) => { return file.name; }), }, '%s (%s): %d %s changed', trigger.name, taskId, event.files.length, event.files.length === 1 ? 'file' : 'files'); } const taskPromise = (0, p_retry_1.default)((attempt) => { return trigger.onChange({ abortSignal, attempt, files: event.files.map((file) => { return { name: file.name, }; }), first: currentFirst, log, spawn: (0, createSpawn_1.createSpawn)(taskId, { abortSignal, cwd: trigger.cwd, throttleOutput: trigger.throttleOutput, }), taskId, }); }, { ...trigger.retry, onFailedAttempt: ({ retriesLeft }) => { if (retriesLeft > 0) { log.warn('%s (%s): retrying task %d/%d...', trigger.name, taskId, trigger.retry.retries - retriesLeft, trigger.retry.retries); } }, }) // eslint-disable-next-line promise/prefer-await-to-then .then(() => { if (taskId === (activeTask === null || activeTask === void 0 ? void 0 : activeTask.id)) { log.debug('%s (%s): completed task', trigger.name, taskId); activeTask = null; } }) // eslint-disable-next-line promise/prefer-await-to-then .catch(() => { log.warn('%s (%s): task failed', trigger.name, taskId); }); // eslint-disable-next-line require-atomic-updates activeTask = { abortController, id: taskId, promise: taskPromise, queued: false, }; log.debug('%s (%s): started task', trigger.name, taskId); return taskPromise; }; return { activeTask, expression: trigger.expression, initialRun: trigger.initialRun, persistent: trigger.persistent, teardown: async () => { if (trigger.onTeardown) { const taskId = (0, generateShortId_1.generateShortId)(); try { await trigger.onTeardown({ spawn: (0, createSpawn_1.createSpawn)(taskId, { throttleOutput: trigger.throttleOutput, }), }); } catch (error) { log.error({ error, }, 'teardown produced an error'); } } }, trigger: async (events) => { fileChangeEventQueue.push(...events); try { await handleSubscriptionEvent(); } catch (error) { log.error({ error, }, 'trigger produced an error'); } }, }; }; exports.subscribe = subscribe; //# sourceMappingURL=subscribe.js.map