turbowatch
Version:
Extremely fast file change detector and task orchestrator for Node.js.
109 lines • 4.51 kB
JavaScript
;
// cspell:words nothrow
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createSpawn = void 0;
const Logger_1 = require("./Logger");
const chalk_1 = __importDefault(require("chalk"));
const randomcolor_1 = __importDefault(require("randomcolor"));
const throttle_debounce_1 = require("throttle-debounce");
const zx_1 = require("zx");
const log = Logger_1.Logger.child({
namespace: 'createSpawn',
});
const prefixLines = (subject, prefix) => {
const response = [];
for (const fragment of subject.split('\n')) {
response.push(prefix + fragment);
}
return response.join('\n');
};
const createSpawn = (taskId, { cwd = process.cwd(), abortSignal, throttleOutput, } = {}) => {
let stdoutBuffer = [];
let stderrBuffer = [];
const flush = () => {
if (stdoutBuffer.length) {
// eslint-disable-next-line no-console
console.log(stdoutBuffer.join('\n'));
}
if (stderrBuffer.length) {
// eslint-disable-next-line no-console
console.error(stderrBuffer.join('\n'));
}
stdoutBuffer = [];
stderrBuffer = [];
};
const output = (0, throttle_debounce_1.throttle)(throttleOutput === null || throttleOutput === void 0 ? void 0 : throttleOutput.delay, () => {
flush();
}, {
noLeading: true,
});
const colorText = chalk_1.default.hex((0, randomcolor_1.default)({ luminosity: 'dark' }));
return async (pieces, ...args) => {
zx_1.$.cwd = cwd;
// eslint-disable-next-line promise/prefer-await-to-then
const processPromise = (0, zx_1.$)(pieces, ...args)
.nothrow()
.quiet();
(async () => {
for await (const chunk of processPromise.stdout) {
// TODO we might want to make this configurable (e.g. behind a debug flag), because these logs might provide valuable context when debugging shutdown logic.
if (abortSignal === null || abortSignal === void 0 ? void 0 : abortSignal.aborted) {
return;
}
const message = prefixLines(chunk.toString().trimEnd(), colorText(taskId) + ' > ');
if (throttleOutput === null || throttleOutput === void 0 ? void 0 : throttleOutput.delay) {
stdoutBuffer.push(message);
output();
}
else {
// eslint-disable-next-line no-console
console.log(message);
}
}
})();
(async () => {
for await (const chunk of processPromise.stderr) {
// TODO we might want to make this configurable (e.g. behind a debug flag), because these logs might provide valuable context when debugging shutdown logic.
if (abortSignal === null || abortSignal === void 0 ? void 0 : abortSignal.aborted) {
return;
}
const message = prefixLines(chunk.toString().trimEnd(), colorText(taskId) + ' > ');
if (throttleOutput === null || throttleOutput === void 0 ? void 0 : throttleOutput.delay) {
stderrBuffer.push(message);
output();
}
else {
// eslint-disable-next-line no-console
console.error(message);
}
}
})();
if (abortSignal) {
const kill = () => {
processPromise.kill();
};
abortSignal.addEventListener('abort', kill, {
once: true,
});
// eslint-disable-next-line promise/prefer-await-to-then
processPromise.finally(() => {
abortSignal.removeEventListener('abort', kill);
});
}
const result = await processPromise;
flush();
if (result.exitCode === 0) {
return result;
}
if (abortSignal === null || abortSignal === void 0 ? void 0 : abortSignal.aborted) {
return result;
}
log.error('task %s exited with an error', taskId);
throw new Error('Program exited with code ' + result.exitCode + '.');
};
};
exports.createSpawn = createSpawn;
//# sourceMappingURL=createSpawn.js.map