@j-o-r/sh
Version:
Execute shell commands on Linux-based systems from javascript
142 lines (132 loc) • 4.96 kB
JavaScript
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