| 62 | * @param {object} [options] |
| 63 | */ |
| 64 | export async function exec(command, args, options) { |
| 65 | return new Promise((resolve, reject) => { |
| 66 | const _process = spawn(command, args, { |
| 67 | stdio: [ |
| 68 | 'ignore', // stdin |
| 69 | 'pipe', // stdout |
| 70 | 'pipe', // stderr |
| 71 | ], |
| 72 | ...options, |
| 73 | shell: process.platform === 'win32', |
| 74 | }) |
| 75 | |
| 76 | /** |
| 77 | * @type {Buffer[]} |
| 78 | */ |
| 79 | const stderrChunks = [] |
| 80 | /** |
| 81 | * @type {Buffer[]} |
| 82 | */ |
| 83 | const stdoutChunks = [] |
| 84 | |
| 85 | _process.stderr?.on('data', chunk => { |
| 86 | stderrChunks.push(chunk) |
| 87 | }) |
| 88 | |
| 89 | _process.stdout?.on('data', chunk => { |
| 90 | stdoutChunks.push(chunk) |
| 91 | }) |
| 92 | |
| 93 | _process.on('error', error => { |
| 94 | reject(error) |
| 95 | }) |
| 96 | |
| 97 | _process.on('exit', code => { |
| 98 | const ok = code === 0 |
| 99 | const stderr = Buffer.concat(stderrChunks).toString().trim() |
| 100 | const stdout = Buffer.concat(stdoutChunks).toString().trim() |
| 101 | |
| 102 | if (ok) { |
| 103 | const result = { ok, code, stderr, stdout } |
| 104 | resolve(result) |
| 105 | } else { |
| 106 | reject( |
| 107 | new Error( |
| 108 | `Failed to execute command: ${command} ${args.join(' ')}: ${stderr}`, |
| 109 | ), |
| 110 | ) |
| 111 | } |
| 112 | }) |
| 113 | }) |
| 114 | } |