2023-04-25 11:01:25 +00:00
|
|
|
import { execFile } from 'child_process'
|
2023-06-01 18:01:49 +00:00
|
|
|
import { nicelog } from './nicelog'
|
2023-04-25 11:01:25 +00:00
|
|
|
|
2024-05-22 15:55:49 +00:00
|
|
|
interface ExecOpts {
|
2023-04-25 11:01:25 +00:00
|
|
|
pwd?: string
|
|
|
|
processStdoutLine?: (line: string) => void
|
|
|
|
processStderrLine?: (line: string) => void
|
2023-05-02 16:13:09 +00:00
|
|
|
env?: Partial<NodeJS.ProcessEnv>
|
|
|
|
}
|
|
|
|
|
|
|
|
export function prefixOutput(prefix: string) {
|
|
|
|
return {
|
|
|
|
processStdoutLine: (line: string) => process.stdout.write(`${prefix}${line}\n`),
|
|
|
|
processStderrLine: (line: string) => process.stderr.write(`${prefix}${line}\n`),
|
|
|
|
}
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export async function exec(
|
|
|
|
command: string,
|
|
|
|
args: (string | null)[],
|
|
|
|
{
|
|
|
|
pwd = process.cwd(),
|
|
|
|
processStdoutLine = (line) => process.stdout.write(`${line}\n`),
|
|
|
|
processStderrLine = (line) => process.stderr.write(`${line}\n`),
|
2023-05-02 16:13:09 +00:00
|
|
|
env,
|
2023-04-25 11:01:25 +00:00
|
|
|
}: ExecOpts = {}
|
|
|
|
): Promise<string> {
|
2023-06-01 18:01:49 +00:00
|
|
|
nicelog(`> $ ${command} ${args.join(' ')} (in ${pwd}))`)
|
2023-04-25 11:01:25 +00:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
const data: string[] = []
|
|
|
|
|
|
|
|
const childProcess = execFile(
|
|
|
|
command,
|
|
|
|
args.filter((arg): arg is string => !!arg),
|
2023-05-02 16:13:09 +00:00
|
|
|
{ cwd: pwd, env: { ...process.env, ...env } },
|
2023-04-25 11:01:25 +00:00
|
|
|
(err) => {
|
|
|
|
if (err) reject(err)
|
|
|
|
else resolve(data.join(''))
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
let pendingStdoutLine = ''
|
|
|
|
childProcess.stdout!.on('data', (chunk) => {
|
|
|
|
const chunkString: string = chunk.toString('utf-8')
|
|
|
|
data.push(chunkString)
|
|
|
|
|
|
|
|
const lines = chunkString.split('\n')
|
|
|
|
lines[0] = pendingStdoutLine + lines[0]
|
|
|
|
pendingStdoutLine = lines.pop() ?? ''
|
|
|
|
|
|
|
|
for (const line of lines) {
|
|
|
|
processStdoutLine(line)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
childProcess.stdout!.on('close', () => {
|
|
|
|
processStdoutLine(pendingStdoutLine)
|
|
|
|
})
|
|
|
|
|
|
|
|
let pendingStderrLine = ''
|
|
|
|
childProcess.stderr!.on('data', (chunk) => {
|
|
|
|
const chunkString: string = chunk.toString('utf-8')
|
|
|
|
data.push(chunkString)
|
|
|
|
|
|
|
|
const lines = chunkString.split('\n')
|
|
|
|
lines[0] = pendingStderrLine + lines[0]
|
|
|
|
pendingStderrLine = lines.pop() ?? ''
|
|
|
|
|
|
|
|
for (const line of lines) {
|
|
|
|
processStderrLine(line)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
childProcess.stderr!.on('close', () => {
|
|
|
|
processStderrLine(pendingStderrLine)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|