|
1 | 1 | import { isString } from "@/remeda"; |
2 | | -import { ResultAsync } from "@/result"; |
| 2 | +import { Result } from "@/result"; |
3 | 3 |
|
4 | 4 | import { errorToMessage } from "./error"; |
5 | 5 | import { PromiseWithResolvers } from "./promise"; |
6 | 6 |
|
7 | | -export function $(cmd: string): ResultAsync<{ stdout: string; stderr: string }, string>; |
8 | | -export function $( |
| 7 | +export interface ShellExecResult { |
| 8 | + stdout: string; |
| 9 | + stderr: string; |
| 10 | +} |
| 11 | + |
| 12 | +const REGEXP_NULL_CHAR = /\x00+/g; |
| 13 | +const REGEXP_SAFE_CHARS = /^[A-Za-z0-9,:=_./-]+$/; |
| 14 | +const REGEXP_SINGLE_QUOTES = /'+/g; |
| 15 | + |
| 16 | +export async function $(cmd: string): Promise<Result<ShellExecResult, string>>; |
| 17 | +export async function $( |
9 | 18 | cmd: TemplateStringsArray, |
10 | 19 | ...values: any[] |
11 | | -): ResultAsync<{ stdout: string; stderr: string }, string>; |
12 | | -export function $(cmd: string | TemplateStringsArray, ...values: any[]) { |
| 20 | +): Promise<Result<ShellExecResult, string>>; |
| 21 | +export async function $(cmd: string | TemplateStringsArray, ...values: any[]) { |
| 22 | + const { exec } = await import("node:child_process"); |
| 23 | + |
13 | 24 | const command = isString(cmd) |
14 | 25 | ? cmd |
15 | 26 | : cmd.reduce((acc, part, index) => acc + part + (values[index] ?? ""), ""); |
16 | 27 |
|
17 | | - const promise = import("node:child_process").then(({ exec }) => { |
18 | | - const { promise, reject, resolve } = PromiseWithResolvers(); |
| 28 | + const fn = async () => { |
| 29 | + const { promise, reject, resolve } = PromiseWithResolvers<ShellExecResult>(); |
19 | 30 |
|
20 | 31 | exec(command, (error, stdout, stderr) => { |
21 | 32 | if (error) { |
22 | 33 | reject(error); |
23 | 34 | } else { |
24 | | - resolve({ stdout, stderr }); |
| 35 | + resolve({ stdout: stdout.trim(), stderr: stderr.trim() }); |
25 | 36 | } |
26 | 37 | }); |
27 | 38 |
|
28 | | - return promise; |
29 | | - }); |
| 39 | + return await promise; |
| 40 | + }; |
| 41 | + const onThrow = errorToMessage(`Failed to execute command: ${cmd}`); |
| 42 | + const result = Result.try(fn, onThrow); |
30 | 43 |
|
31 | | - return ResultAsync.fromPromise(promise, errorToMessage(`Failed to execute command: ${cmd}`)); |
| 44 | + return result; |
32 | 45 | } |
33 | 46 |
|
34 | | -const REGEXP_NULL_CHAR = /\x00+/g; |
35 | | -const REGEXP_SAFE_CHARS = /^[A-Za-z0-9,:=_./-]+$/; |
36 | | -const REGEXP_SINGLE_QUOTES = /'+/g; |
37 | | - |
38 | 47 | export const quoteShellArg = (arg: string) => { |
39 | 48 | if (!arg) return "''"; |
40 | 49 |
|
|
0 commit comments