UNPKG

extra-child-process

Version:

Useful additions to inbuilt child_process module.

296 lines (293 loc) 10.4 kB
import { EOL } from 'os'; import * as C from 'child_process'; export { ChildProcess, execFileSync, execSync, fork, spawn, spawnSync } from 'child_process'; import * as F from 'fs'; import * as P from 'path'; class ExecAsyncHandler { constructor() { this.resolve = null; this.reject = null; this.callback = (err, stdout, stderr) => { if (err != null) this.reject(Object.assign(err, { stdout, stderr })); else this.resolve({ stdout, stderr }); }; this.promise = new Promise((resolve, reject) => { this.resolve = resolve; this.reject = reject; }); } } function execAsync(command, options) { var e = new ExecAsyncHandler(); var child = C.exec(command, options, e.callback); return Object.assign(e.promise, { child }); } function exec(...args) { if (typeof args[args.length - 1] === "function") C.exec.apply(null, args); else return execAsync.apply(null, args); } function execFileAsync(file, args, options) { var e = new ExecAsyncHandler(); var _args = args instanceof Array ? args : null; var _options = args instanceof Array ? options : args; var child = C.execFile(file, _args, _options, e.callback); return Object.assign(e.promise, { child }); } function execFile(...args) { if (typeof args[args.length - 1] === "function") C.execFile.apply(null, args); else return execFileAsync.apply(null, args); } const MAX_BUFFER = 1024 * 1024; class StreamBuffer { constructor(stream, encoding = "buffer", maxBuffer = MAX_BUFFER) { this.size = 0; this.data = []; this.encoding = encoding; if (encoding && encoding !== "buffer") stream.setEncoding(encoding); stream.on("data", chunk => { if (this.size + chunk.length > maxBuffer) return; this.data.push(chunk); this.size += chunk.length; }); } value() { if (!this.encoding || this.encoding === "buffer") return Buffer.concat(this.data); return this.data.join(""); } } function spawnAsync(command, args, options) { var _args = args instanceof Array ? args : null; var _options = args instanceof Array ? options : args; var encoding = (_options === null || _options === void 0 ? void 0 : _options.encoding) || "buffer"; var maxBuffer = (_options === null || _options === void 0 ? void 0 : _options.maxBuffer) || MAX_BUFFER; var child = C.spawn(command, _args, _options); var outbuf = new StreamBuffer(child.stdout, encoding, maxBuffer); var errbuf = new StreamBuffer(child.stderr, encoding, maxBuffer); var promise = new Promise((resolve, reject) => { child.on("error", err => { var { pid, exitCode, signalCode } = child; var stdout = outbuf.value(); var stderr = errbuf.value(); var output = [null, stdout, stderr]; reject(Object.assign(err, { pid, output, stdout, stderr, status: exitCode, signal: signalCode })); }); child.on("close", (code, signal) => { var { pid } = child; var stdout = outbuf.value(); var stderr = errbuf.value(); var output = [null, stdout, stderr]; resolve({ pid, output, stdout, stderr, status: code, signal }); }); }); return Object.assign(promise, { child }); } function whichWin32(dir, entries, exts, ft) { var apath = null, afile = null; var aprio = Number.MAX_SAFE_INTEGER; for (var e of entries) { if (!e.isFile()) continue; var ext = P.extname(e.name); var file = P.basename(e.name, ext); if (afile != null && file !== afile) break; if (!ft(file)) continue; var prio = exts.indexOf((ext || ".?").toLowerCase()); if (prio < 0 || prio >= aprio) continue; apath = P.join(dir, e.name); afile = file; aprio = prio; } return apath; } function whichAllWin32(dir, entries, exts, ft) { var a = []; for (var e of entries) { if (!e.isFile()) continue; var ext = P.extname(e.name); var file = P.basename(e.name, ext); if (!ft(file)) continue; var prio = exts.indexOf((ext || ".?").toLowerCase()); if (prio >= 0) a.push({ file, prio, path: P.join(dir, e.name) }); } a.sort((a, b) => a.file.localeCompare(b.file) || a.prio - b.prio); return a.map(x => x.path); } function whichLinuxSync(dir, entries, ft) { for (var e of entries) { if (!e.isFile()) continue; if (!ft(e.name)) continue; var path = P.join(dir, e.name); try { F.accessSync(path, F.constants.X_OK); } catch (_a) { continue; } return path; } return null; } function whichAllLinuxSync(dir, entries, ft) { var a = []; for (var e of entries) { if (!e.isFile()) continue; if (!ft(e.name)) continue; var path = P.join(dir, e.name); try { F.accessSync(path, F.constants.X_OK); a.push(path); } catch (_a) { } } return a.sort(); } function whichLinuxAsync(dir, entries, ft) { return new Promise(resolve => { var n = 0, paths = []; var best = Number.MAX_SAFE_INTEGER; for (var e of entries) { if (!e.isFile()) continue; if (!ft(e.name)) continue; let i = paths.push(null) - 1; let path = P.join(dir, e.name); F.access(path, F.constants.X_OK, err => { paths[i] = err ? "" : path; --n; if (!err) best = Math.min(best, i); if (best === Number.MAX_SAFE_INTEGER) { if (n === 0) resolve(null); } else if (!paths.slice(0, best).includes(null)) resolve(paths[best]); }); ++n; } }); } function whichAllLinuxAsync(dir, entries, ft) { return new Promise(resolve => { var a = [], n = 0; for (var e of entries) { if (!e.isFile()) continue; if (!ft(e.name)) continue; let path = P.join(dir, e.name); F.access(path, F.constants.X_OK, err => { if (!err) a.push(path); if (--n === 0) resolve(a.sort()); }); ++n; } }); } function whichFnSync(dirs, exts, ft) { for (var dir of dirs) { var entries = F.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name)); var path = EOL === "\n" ? whichLinuxSync(dir, entries, ft) : whichWin32(dir, entries, exts, ft); if (path) return path; } return null; } function whichAllFnSync(dirs, exts, ft) { var a = []; for (var dir of dirs) { var entries = F.readdirSync(dir, { withFileTypes: true }); var paths = EOL === "\n" ? whichAllLinuxSync(dir, entries, ft) : whichAllWin32(dir, entries, exts, ft); a.push(...paths); } return a; } async function whichFnAsync(dirs, exts, ft) { var pathps = []; for (let dir of dirs) { var p = F.promises.readdir(dir, { withFileTypes: true }).then(entries => entries.sort((a, b) => a.name.localeCompare(b.name))); pathps.push(p.then(entries => EOL === "\n" ? whichLinuxAsync(dir, entries, ft) : whichWin32(dir, entries, exts, ft))); } for (var pathp of pathps) { var path = await pathp; if (path) return path; } return null; } async function whichAllFnAsync(dirs, exts, ft) { var pathps = [], a = []; for (let dir of dirs) { var p = F.promises.readdir(dir, { withFileTypes: true }); pathps.push(p.then(entries => EOL === "\n" ? whichAllLinuxAsync(dir, entries, ft) : whichAllWin32(dir, entries, exts, ft))); } for (var pathp of pathps) a.push(...(await pathp)); return a; } function whichDirs(options) { var sep = EOL === "\n" ? ":" : ";"; var dirs = (options === null || options === void 0 ? void 0 : options.paths) || process.env.PATH.split(sep); return EOL !== "\n" && (options === null || options === void 0 ? void 0 : options.cwd) ? new Set([options.cwd, ...dirs]) : new Set(dirs); } function whichExts(options) { return (options === null || options === void 0 ? void 0 : options.extnames) || (process.env.PATHEXT || "").toLowerCase().split(";"); } function whichTestFunction(cmd) { if (typeof cmd === "function") return cmd; if (typeof cmd === "string") return file => file === cmd; return file => cmd.test(file); } function whichSync(cmd, options) { return whichFnSync(whichDirs(options), whichExts(options), whichTestFunction(cmd)); } function whichAllSync(cmd, options) { return whichAllFnSync(whichDirs(options), whichExts(options), whichTestFunction(cmd)); } function whichAsync(cmd, options) { return whichFnAsync(whichDirs(options), whichExts(options), whichTestFunction(cmd)); } function whichAllAsync(cmd, options) { return whichAllFnAsync(whichDirs(options), whichExts(options), whichTestFunction(cmd)); } function which(cmd, options, callback) { if (typeof callback === "function") whichAsync(cmd, options).then(bin => callback(null, bin), callback); else if (typeof options === "function") whichAsync(cmd).then(bin => options(null, bin), options); else return whichAsync(cmd, options); } function whichAll(cmd, options, callback) { if (typeof callback === "function") whichAllAsync(cmd, options).then(bin => callback(null, bin), callback); else if (typeof options === "function") whichAllAsync(cmd).then(bin => options(null, bin), options); else return whichAllAsync(cmd, options); } export { exec, execAsync, execFile, execFileAsync, spawnAsync, which, whichAll, whichAllAsync, whichAllSync, whichAsync, whichSync };