pkg-fetch/lib/spawn.js

79 lines
2.2 KiB
JavaScript

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