UNPKG

gulp-emu

Version:
107 lines (105 loc) 4 kB
// @ts-check const { spawn } = require("child_process"); const chalk = require("chalk"); const { Cancelable, CancelError } = require("@esfx/cancelable"); const log = require("fancy-log"); const isWindows = /^win/.test(process.platform); /** * @param {string} cmd * @param {string[]} args * @param {object} options * @param {boolean} [options.ignoreExitCode] * @param {boolean} [options.verbose] * @param {Cancelable} [options.cancelToken] * @param {string} [options.cwd] * @param {boolean} [options.waitForExit=true] * @returns {Promise<{exitCode: number}>} */ function exec(cmd, args = [], { ignoreExitCode, verbose, cancelToken = Cancelable.none, cwd, waitForExit = true } = {}) { return new Promise((resolve, reject) => { Cancelable.throwIfSignaled(cancelToken); const shell = isWindows ? "cmd" : "/bin/sh"; const shellArgs = isWindows ? ["/c", cmd.includes(" ") ? `"${cmd}"` : cmd, ...args] : ["-c", `${cmd} ${args.join(" ")}`]; if (verbose) log(`> ${chalk.green(cmd)} ${args.join(" ")}`); const child = spawn(shell, shellArgs, { stdio: "inherit", cwd, windowsVerbatimArguments: true }); const reg = Cancelable.subscribe(cancelToken, () => { child.removeAllListeners(); if (verbose) log(`${chalk.red("killing")} '${chalk.green(cmd)} ${args.join(" ")}'...`); child.kill("SIGINT"); child.kill("SIGTERM"); reject(new CancelError()); }); if (waitForExit) { child.on("exit", (exitCode) => { child.removeAllListeners(); reg.unsubscribe(); if (exitCode === 0 || ignoreExitCode) { resolve({ exitCode }); } else { reject(new Error(`Process exited with code: ${exitCode}`)); } }); child.on("error", error => { child.removeAllListeners(); reg.unsubscribe(); reject(error); }); } else { child.unref(); resolve({ exitCode: 0 }); } }); } exports.exec = exec; class ArgsBuilder { constructor(args = []) { this.args = args; } addValue(value) { if (value === undefined) return; if (Array.isArray(value)) { for (const v of value) { this.addValue(v); } } else { this.args.push(value); } } addSwitch(name, value, defaultValue) { if (!name || value === undefined || value === defaultValue) return; if (Array.isArray(value)) { for (const v of value) { this.addSwitch(name, v, defaultValue); } } else if (typeof name === "object") { for (const key of Object.keys(name)) { this.addSwitch(key, name[key], defaultValue && typeof defaultValue === "object" ? defaultValue[key] : defaultValue); } } else if (typeof name === "string") { const [prefix, suffix] = name.startsWith("--") ? ["--", name.slice(2)] : name.startsWith("-") ? ["-", name.slice(1)] : name.startsWith("//") ? ["//", name.slice(2)] : name.startsWith("/") ? ["/", name.slice(1)] : name.length === 1 ? ["-", name] : ["--", name]; if (typeof value === "boolean") { name = `${prefix}${value ? "" : prefix.startsWith("/") ? "no" : "no-"}${suffix}`; this.args.push(name); } else { name = `${prefix}${suffix}`; this.args.push(name, value); } } } [Symbol.iterator]() { return this.args.values(); } } exports.ArgsBuilder = ArgsBuilder;