UNPKG

@j-o-r/sh

Version:

Execute shell commands on Linux-based systems from javascript

142 lines (132 loc) 4.96 kB
import SHExec from './SHExecute.js'; /** * @typedef {Object} SpawnSyncResponse * @property {number} status - The exit code of the child process. A value of `0` indicates success. * @property {Buffer|null} signal - The signal used to terminate the process, if any. * @property {Array<string|null>} output - An array containing the standard output and standard error of the child process. * @property {number} pid - The process ID of the child process. * @property {Buffer|null} stdout - The standard output of the child process. * @property {Buffer|null} stderr - The standard error of the child process. */ /** * @typedef {Object} SHOptions * @property {string} [cwd] - Current working directory of the child process. * @property {Object} [env] - Environment key-value pairs. * @property {Array|string} [argv0] - Explicitly set the value of `argv[0]` sent to the child process. * @property {boolean} [detached=false] - If true, the child will be a process group leader. * @property {number} [uid] - Sets the user identity of the process. * @property {number} [gid] - Sets the group identity of the process. * @property {StdioOptions|StdioOption} [stdio='pipe'] - Child's stdio configuration. * @property {boolean|string} [shell="bash"] - If true, runs command inside a shell. * @property {number} [timeout=0] - In milliseconds, specifies when to terminate the child process. * @property {string|Buffer|URL} [input] - The input to write to stdin. */ /** * @typedef {('pipe' | 'ignore' | 'inherit' | number)} StdioOption * @description Defines the stdio configuration for each of the standard streams. * * - 'pipe' creates a pipe between the child process and the parent process. * The parent end of the pipe is exposed as a property on the `ChildProcess` object. * - 'ignore' indicates that the child process's corresponding stdio file descriptor will be ignored. * - 'inherit' passes the corresponding stdio stream to/from the child process. * - Stream object to be used for the stdio stream. * - Positive integer representing a file descriptor to be used for the stdio stream. */ /** * @typedef {Array<StdioOption>|StdioOption} StdioOptions * @description * Configures the stdio streams for the child process. This can be an array or a single StdioOption. * * Array Form: Specify the configuration for [stdin, stdout, stderr]. * - If array length is more than 3, additional positions correspond to extra streams. * Single Value: This value will be applied to stdin, stdout, and stderr. * * Examples: * - ['pipe', 'pipe', 'ignore']: Pipe stdin and stdout, ignore stderr. * - 'inherit': Inherit all stdio streams from the parent. */ /** * Merge property values while maintaining the fixed set of props from the predefined object * @param {SHOptions} predefined - options * @param {import('child_process').SpawnOptions | import('child_process').SpawnSyncOptions} options * @returns {SHOptions} */ const mergeOptions = (predefined, options) => { const mergedObj = { ...predefined }; for (const key in options) { if (options[key] !== undefined) { mergedObj[key] = options[key]; } } return mergedObj; } /** @type {SHOptions} */ const defaultOptions = { cwd: process.cwd(), env: process.env, shell: 'bash', stdio: ['inherit', 'pipe', 'pipe'], timeout: 0 // when 0 there is no timeout }; class SHDispatch { // #prefix = 'set -euo pipefail;/usr/bin/env' #prefix = 'set -euo pipefail;/usr/bin/env -S' #cmd = ''; #options = {}; /** * @type {SHExec} */ #proc; /** * @param {string} cmd - cmd to execute */ constructor(cmd) { if (!cmd || cmd === '') { throw new Error('Undefined command'); } this.#cmd = cmd; this.#options = defaultOptions } /** * @param {import('child_process').SpawnOptions | import('child_process').SpawnSyncOptions} options * @param {string} [prefix] - command prefix e.g (default) '/usr/bin/env' * @returns {SHDispatch} */ options(options, prefix) { if (prefix && typeof prefix === 'string') { this.#prefix = prefix; } if (options.stdio && typeof options.stdio === 'string') { // convert stdio to array // This sets the default io values // but can be overwritten when having a payload const io = options.stdio; options.stdio = Array(3).fill(io); } this.#options = mergeOptions(defaultOptions, options) return this; } /** * @param {string} [payload] * @returns {Promise<string>} */ run(payload) { this.#proc = new SHExec(this.#cmd, this.#prefix, this.#options); return this.#proc.run(payload); } /** * Works for terminal screen takeovers like editors * @param {string} [payload] * @returns {import('child_process').SpawnSyncReturns} */ runSync(payload) { return new SHExec(this.#cmd, this.#prefix, this.#options).runSync(payload); } async kill(signal = 'SIGTERM') { let res = []; res = await this.#proc.kill(signal); this.#proc = undefined; return res; } } export default SHDispatch