UNPKG

@plugjs/plug

Version:
84 lines 3.77 kB
import { spawn } from 'node:child_process'; import path from 'node:path'; import readline from 'node:readline'; import { assert, BuildFailure } from "../asserts.js"; import { $p, logOptions } from "../logging.js"; import { getCurrentWorkingDirectory, resolveDirectory } from "../paths.js"; export async function execChild(cmd, args, options = {}, context) { const { env = {}, // default empty environment shell = false, // by default do not use a shell cwd = undefined, // by default use "process.cwd()" coverageDir, // default "undefined" (pass throug from env) ...extraOptions } = options; const childCwd = cwd ? context.resolve(cwd) : getCurrentWorkingDirectory(); assert(resolveDirectory(childCwd), `Current working directory ${$p(childCwd)} does not exist`); // Figure out the PATH environment variable const childPaths = []; // The `.../node_modules/.bin` path relative to the current working dir */ const baseNodePath = context.resolve('@node_modules', '.bin'); if (resolveDirectory(baseNodePath)) childPaths.push(baseNodePath); // The `.../node_bodules/.bin` path relative to the buildDir */ const buildNodePath = context.resolve('./node_modules', '.bin'); if (resolveDirectory(buildNodePath)) childPaths.push(buildNodePath); // Any other paths either from `process.env` or `env` (which overrides it) const extraPath = env['PATH'] || process.env['PATH']; if (extraPath) childPaths.push(extraPath); // Build our environment variables record const PATH = childPaths.join(path.delimiter); const childEnv = { ...process.env, // environment from current running process ...env, // environment configured from "execChild" arguments ...logOptions.forkEnv(), // forked log options for child plugjs PATH, // path with all ".../node_modules/.bin" directories }; // Instrument coverage directory if needed if (coverageDir) childEnv['NODE_V8_COVERAGE'] = context.resolve(coverageDir); // Prepare the options for calling `spawn` const childOptions = { ...extraOptions, stdio: ['ignore', 'pipe', 'pipe'], cwd: childCwd, env: childEnv, shell, }; // Spawn our subprocess and monitor its stdout/stderr context.log.info('Executing', [cmd, ...args]); context.log.debug('Child process options', childOptions); const child = spawn(cmd, args, childOptions); try { context.log.info('Child process PID', child.pid); // Standard output to "notice" if (child.stdout) { const out = readline.createInterface(child.stdout); out.on('line', (line) => context.log.notice(line || '\u00a0')); } // Standard error to "warning" if (child.stderr) { const err = readline.createInterface(child.stderr); err.on('line', (line) => context.log.warn(line || '\u00a0')); } } catch (error) { // If something happens before returning our promise, kill the child... child.kill(); throw error; } // Return our promise from the spawn events return new Promise((resolve, reject) => { child.on('error', (error) => reject(error)); child.on('exit', (code, signal) => { if (code === 0) return resolve(); if (signal) return reject(BuildFailure.withMessage(`Child process exited with signal ${signal}`)); if (code) return reject(BuildFailure.withMessage(`Child process exited with code ${code}`)); reject(BuildFailure.withMessage('Child process failed for an unknown reason')); }); }); } //# sourceMappingURL=exec.js.map