import byline from 'byline'; import chipo from 'child_process'; import fs from 'fs'; import { log } from './log.js'; const MAX_LINES = 20; const DEBUG_THRESHOLDS = false; function errorLines (lines) { return lines.slice(-MAX_LINES) .map((line) => line[1]).join('\n'); } export function spawn (cmd, args, opts) { const child = chipo.spawn(cmd, args, opts); const stdout = byline(child.stdout); const stderr = byline(child.stderr); const lines = []; let onData = function (data) { const time = (new Date()).getTime(); lines.push([ time, data.toString() ]); // TODO chalk stdout/stderr? const { thresholds } = this; // eslint-disable-line no-invalid-this if (thresholds) { for (const key in thresholds) { if (data.indexOf(key) >= 0) { const p = thresholds[key]; log.showProgress(p); if (DEBUG_THRESHOLDS) { lines.push([ time, '************' ]); lines.push([ time, p + ': ' + key ]); lines.push([ time, '************' ]); } } } } }; const promise = new Promise((resolve, reject) => { child.on('error', (error) => { console.error(errorLines(lines)); // dont use `log` here reject(error); }); child.on('close', (code) => { if (code) { console.error(errorLines(lines)); // dont use `log` here return reject(new Error(`${cmd} failed with code ${code}`)); } resolve(); }); }); onData = onData.bind(promise); if (stdout) stdout.on('data', onData); if (stderr) stderr.on('data', onData); promise.child = child; promise.lines = lines; return promise; } export function progress (promise, thresholds) { promise.thresholds = thresholds; const { child, lines } = promise; log.enableProgress(promise.child.spawnfile); log.showProgress(0); const start = (new Date()).getTime(); child.on('close', () => { if (DEBUG_THRESHOLDS) { const finish = (new Date()).getTime(); const content = lines.map((line) => ((100 * (line[0] - start) / (finish - start)) | 0) + ': ' + line[1] ).join('\n'); fs.writeFileSync(child.spawnfile + '.debug', content); } log.disableProgress(); }); }