mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-07 08:33:50 +08:00
Support the env option of spawnSync
`spawnSync(cmd, args, { env })` replaces the child's environment with
the given record, as spawn and execFileSync already do, alone or with
the timeout, killSignal and stdio options. The fallback typings declare
the option.
This commit is contained in:
+3
-1
@@ -3512,7 +3512,8 @@ declare module "child_process" {
|
||||
* killSignal at the deadline and the result carries error: ETIMEDOUT +
|
||||
* the signal, never a throw), killSignal (a signal NAME literal),
|
||||
* stdio ("ignore"/"inherit"/"pipe" or a 3-tuple of those — captures
|
||||
* read "" for non-piped streams), and windowsHide (a POSIX no-op). */
|
||||
* read "" for non-piped streams), env (replaces the child's
|
||||
* environment), and windowsHide (a POSIX no-op). */
|
||||
export function spawnSync(
|
||||
command: string,
|
||||
args?: string[],
|
||||
@@ -3521,6 +3522,7 @@ declare module "child_process" {
|
||||
timeout?: number;
|
||||
killSignal?: string;
|
||||
stdio?: "pipe" | "ignore" | "inherit" | ("pipe" | "ignore" | "inherit")[];
|
||||
env?: { [k: string]: string | undefined };
|
||||
windowsHide?: boolean;
|
||||
},
|
||||
): SpawnSyncReturns;
|
||||
|
||||
@@ -754,6 +754,7 @@ export const LIB_FN_SYMS: Record<string, string> = {
|
||||
"cp.spawnSync": "scr_spawn_sync",
|
||||
"cp.spawnSyncOpts": "scr_spawn_sync_opts",
|
||||
"cp.spawnSyncStdioStr": "scr_spawn_sync_stdio_str",
|
||||
"cp.spawnSyncEnv": "scr_spawn_sync_env",
|
||||
"cp.execSync": "scr_exec_sync",
|
||||
"cp.execCapture": "scr_exec_capture",
|
||||
"spawnRes.stdout": "scr_spawn_res_stdout",
|
||||
|
||||
@@ -118,6 +118,7 @@ const INERT_LIB_CALLS = new Set([
|
||||
"worker.parentPort",
|
||||
"worker.root",
|
||||
"cp.spawnSyncOpts",
|
||||
"cp.spawnSyncEnv",
|
||||
]);
|
||||
|
||||
/** jsval conversions (ir/jsval.ts): no user code, no stores. */
|
||||
|
||||
@@ -115,6 +115,8 @@ export function lowerSpawnSyncCall(lowerer: Lowerer, expr: ts.CallExpression, lo
|
||||
// "pipe"`) — accepted when its TYPE proves every arm is a supported
|
||||
// literal; the runtime maps the value to the modes at the call.
|
||||
let stdioStr: IrExpr | null = null;
|
||||
// The `env` option's [k,v,...] pairs, which replace the child's environment.
|
||||
let envPairs: IrExpr | null = null;
|
||||
let plain = true; // no behavior-changing option: the historical libCall
|
||||
|
||||
if (optsNode) {
|
||||
@@ -227,11 +229,16 @@ export function lowerSpawnSyncCall(lowerer: Lowerer, expr: ts.CallExpression, lo
|
||||
case "windowsHide":
|
||||
lowerer.lowerExpr(m.value); // Node no-op on POSIX
|
||||
break;
|
||||
case "env":
|
||||
// A REPLACEMENT environment, as for spawn and execFileSync.
|
||||
envPairs = recordToEnvPairs(lowerer, m.value);
|
||||
plain = false;
|
||||
break;
|
||||
default:
|
||||
lowerer.noLowering(
|
||||
`spawnSync option '${m.name}'`,
|
||||
p,
|
||||
"encoding, timeout, killSignal, stdio, and windowsHide are the supported options",
|
||||
"encoding, timeout, killSignal, stdio, env, and windowsHide are the supported options",
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -239,6 +246,13 @@ export function lowerSpawnSyncCall(lowerer: Lowerer, expr: ts.CallExpression, lo
|
||||
if (plain) {
|
||||
return { kind: "libCall", fn: "cp.spawnSync", args: [cmd, argv], type: SPAWNRES_T, loc };
|
||||
}
|
||||
if (stdioStr !== null && envPairs !== null) {
|
||||
lowerer.noLowering(
|
||||
"spawnSync with a runtime stdio value and env",
|
||||
optsNode!,
|
||||
'pass stdio as a "pipe"/"ignore"/"inherit" literal when env is set',
|
||||
);
|
||||
}
|
||||
if (stdioStr !== null) {
|
||||
return {
|
||||
kind: "libCall",
|
||||
@@ -250,7 +264,7 @@ export function lowerSpawnSyncCall(lowerer: Lowerer, expr: ts.CallExpression, lo
|
||||
}
|
||||
return {
|
||||
kind: "libCall",
|
||||
fn: "cp.spawnSyncOpts",
|
||||
fn: envPairs !== null ? "cp.spawnSyncEnv" : "cp.spawnSyncOpts",
|
||||
args: [
|
||||
cmd,
|
||||
argv,
|
||||
@@ -259,6 +273,7 @@ export function lowerSpawnSyncCall(lowerer: Lowerer, expr: ts.CallExpression, lo
|
||||
numLit(inMode, loc),
|
||||
numLit(outMode, loc),
|
||||
numLit(errMode, loc),
|
||||
...(envPairs !== null ? [envPairs] : []),
|
||||
],
|
||||
type: SPAWNRES_T,
|
||||
loc,
|
||||
|
||||
@@ -1318,6 +1318,12 @@ export const LIB_FN_SIGS = defineLibFnSignatures({
|
||||
argTypes: [STRING, arrayOf(STRING), F64, STRING, F64, F64, F64],
|
||||
result: SPAWNRES_T,
|
||||
},
|
||||
/** cp.spawnSyncOpts with the `env` option: the trailing [k,v,...]
|
||||
* pairs replace the child's environment (recordToEnvPairs). */
|
||||
"cp.spawnSyncEnv": {
|
||||
argTypes: [STRING, arrayOf(STRING), F64, STRING, F64, F64, F64, arrayOf(STRING)],
|
||||
result: SPAWNRES_T,
|
||||
},
|
||||
/** cp.spawnSyncOpts with the stdio carried as a RUNTIME string —
|
||||
* "pipe" | "ignore" | "inherit", proven by the call site's TYPE (the
|
||||
* defaultRunner idiom `stdio: options?.stdio ?? "pipe"`); the runtime
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/7030-spawn-sync-env.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -1105,9 +1105,9 @@ char **scr_child_argv(ScrStr *cmd, ScrArr *args) {
|
||||
|
||||
static ScrSpawnRes *scr_spawn_sync_core(ScrStr *cmd, ScrArr *args, double timeout_ms,
|
||||
int killsig, int in_mode, int out_mode,
|
||||
int err_mode) {
|
||||
int err_mode, ScrArr *env_pairs) {
|
||||
ScrWinSyncRes w;
|
||||
scr_win_run_sync(cmd, args, NULL, in_mode, out_mode, err_mode, NULL, NULL,
|
||||
scr_win_run_sync(cmd, args, NULL, in_mode, out_mode, err_mode, NULL, env_pairs,
|
||||
timeout_ms, &w);
|
||||
if (w.spawn_errname != NULL) {
|
||||
free(w.out.data);
|
||||
@@ -1136,19 +1136,26 @@ static ScrSpawnRes *scr_spawn_sync_core(ScrStr *cmd, ScrArr *args, double timeou
|
||||
}
|
||||
|
||||
ScrSpawnRes *scr_spawn_sync(ScrStr *cmd, ScrArr *args) {
|
||||
return scr_spawn_sync_core(cmd, args, 0, SIGTERM, 0, 0, 0);
|
||||
return scr_spawn_sync_core(cmd, args, 0, SIGTERM, 0, 0, 0, NULL);
|
||||
}
|
||||
|
||||
ScrSpawnRes *scr_spawn_sync_opts(ScrStr *cmd, ScrArr *args, double timeout_ms,
|
||||
ScrStr *killsignal, double in_mode,
|
||||
double out_mode, double err_mode) {
|
||||
return scr_spawn_sync_env(cmd, args, timeout_ms, killsignal, in_mode, out_mode, err_mode,
|
||||
NULL);
|
||||
}
|
||||
|
||||
ScrSpawnRes *scr_spawn_sync_env(ScrStr *cmd, ScrArr *args, double timeout_ms,
|
||||
ScrStr *killsignal, double in_mode, double out_mode,
|
||||
double err_mode, ScrArr *env_pairs) {
|
||||
int killsig = SIGTERM;
|
||||
if (killsignal->len > 0) {
|
||||
int resolved = scr_signal_from_name(killsignal);
|
||||
if (resolved > 0) killsig = resolved;
|
||||
}
|
||||
return scr_spawn_sync_core(cmd, args, timeout_ms, killsig, (int)in_mode,
|
||||
(int)out_mode, (int)err_mode);
|
||||
(int)out_mode, (int)err_mode, env_pairs);
|
||||
}
|
||||
|
||||
ScrSpawnRes *scr_spawn_sync_stdio_str(ScrStr *cmd, ScrArr *args, double timeout_ms,
|
||||
@@ -3101,9 +3108,14 @@ static ScrSpawnRes *scr_spawn_res_new(bool has_status, double status,
|
||||
* documented divergence). timeout_ms > 0 sends `killsig` at the deadline
|
||||
* and the result carries error: ETIMEDOUT + the signal — never a throw,
|
||||
* Node's spawnSync shape. */
|
||||
static char **scr_exec_envp(ScrArr *pairs);
|
||||
static void scr_exec_envp_free(char **envp);
|
||||
|
||||
/* env_pairs: NULL inherits the parent's environment; else [k,v,...]
|
||||
* replaces it, as Node's `env` option does. */
|
||||
static ScrSpawnRes *scr_spawn_sync_core(ScrStr *cmd, ScrArr *args, double timeout_ms,
|
||||
int killsig, int in_mode, int out_mode,
|
||||
int err_mode) {
|
||||
int err_mode, ScrArr *env_pairs) {
|
||||
if (out_mode == 2 || err_mode == 2) {
|
||||
/* Inherited output fds: flush the parent's buffers first so earlier
|
||||
* logs precede the child's writes (Node's practical ordering). */
|
||||
@@ -3138,10 +3150,13 @@ static ScrSpawnRes *scr_spawn_sync_core(ScrStr *cmd, ScrArr *args, double timeou
|
||||
}
|
||||
|
||||
char **argv = scr_child_argv(cmd, args);
|
||||
char **envp = env_pairs != NULL ? scr_exec_envp(env_pairs) : NULL;
|
||||
pid_t pid = -1;
|
||||
int spawn_err = posix_spawnp(&pid, cmd->data, &fa, NULL, argv, environ);
|
||||
int spawn_err = posix_spawnp(&pid, cmd->data, &fa, NULL, argv,
|
||||
envp != NULL ? envp : environ);
|
||||
posix_spawn_file_actions_destroy(&fa);
|
||||
free(argv);
|
||||
scr_exec_envp_free(envp);
|
||||
if (cap_out) close(outfd[1]);
|
||||
if (cap_err) close(errfd[1]);
|
||||
|
||||
@@ -3242,7 +3257,7 @@ static ScrSpawnRes *scr_spawn_sync_core(ScrStr *cmd, ScrArr *args, double timeou
|
||||
}
|
||||
|
||||
ScrSpawnRes *scr_spawn_sync(ScrStr *cmd, ScrArr *args) {
|
||||
return scr_spawn_sync_core(cmd, args, 0, SIGTERM, 0, 0, 0);
|
||||
return scr_spawn_sync_core(cmd, args, 0, SIGTERM, 0, 0, 0, NULL);
|
||||
}
|
||||
|
||||
/* The options entry (cp.spawnSyncOpts): killSignal as a NAME ("" = the
|
||||
@@ -3252,13 +3267,20 @@ ScrSpawnRes *scr_spawn_sync(ScrStr *cmd, ScrArr *args) {
|
||||
ScrSpawnRes *scr_spawn_sync_opts(ScrStr *cmd, ScrArr *args, double timeout_ms,
|
||||
ScrStr *killsignal, double in_mode,
|
||||
double out_mode, double err_mode) {
|
||||
return scr_spawn_sync_env(cmd, args, timeout_ms, killsignal, in_mode, out_mode, err_mode,
|
||||
NULL);
|
||||
}
|
||||
|
||||
ScrSpawnRes *scr_spawn_sync_env(ScrStr *cmd, ScrArr *args, double timeout_ms,
|
||||
ScrStr *killsignal, double in_mode, double out_mode,
|
||||
double err_mode, ScrArr *env_pairs) {
|
||||
int killsig = SIGTERM;
|
||||
if (killsignal->len > 0) {
|
||||
int resolved = scr_signal_from_name(killsignal);
|
||||
if (resolved > 0) killsig = resolved;
|
||||
}
|
||||
return scr_spawn_sync_core(cmd, args, timeout_ms, killsig, (int)in_mode,
|
||||
(int)out_mode, (int)err_mode);
|
||||
(int)out_mode, (int)err_mode, env_pairs);
|
||||
}
|
||||
|
||||
/* The runtime-string stdio entry (cp.spawnSyncStdioStr): the compiler
|
||||
|
||||
@@ -23,6 +23,11 @@ ScrSpawnRes *scr_spawn_sync(ScrStr *cmd, ScrArr *args); /* +1, never throws */
|
||||
ScrSpawnRes *scr_spawn_sync_opts(ScrStr *cmd, ScrArr *args, double timeout_ms,
|
||||
ScrStr *killsignal, double in_mode,
|
||||
double out_mode, double err_mode);
|
||||
/* scr_spawn_sync_opts with an `env` option: [k,v,...] pairs replace the
|
||||
* child's environment (NULL inherits the parent's). */
|
||||
ScrSpawnRes *scr_spawn_sync_env(ScrStr *cmd, ScrArr *args, double timeout_ms,
|
||||
ScrStr *killsignal, double in_mode, double out_mode,
|
||||
double err_mode, ScrArr *env_pairs);
|
||||
/* The runtime-string stdio entry: the compiler proved the value is
|
||||
* "pipe" | "ignore" | "inherit" by type; the mode mapping happens here. */
|
||||
ScrSpawnRes *scr_spawn_sync_stdio_str(ScrStr *cmd, ScrArr *args, double timeout_ms,
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// spawnSync's env option replaces the child's environment, alone or with
|
||||
// the other options.
|
||||
import { spawnSync } from "node:child_process";
|
||||
|
||||
const merged = spawnSync("sh", ["-c", 'echo "$GREETING $HOME_SET"'], {
|
||||
env: { ...process.env, GREETING: "hello", HOME_SET: process.env.HOME ? "yes" : "no" },
|
||||
encoding: "utf8",
|
||||
});
|
||||
console.log(merged.stdout.trim(), merged.status);
|
||||
|
||||
const only = spawnSync("/bin/sh", ["-c", 'echo "${GREETING:-unset} ${HOME:-nohome}"'], {
|
||||
env: { GREETING: "only" },
|
||||
encoding: "utf8",
|
||||
});
|
||||
console.log(only.stdout.trim());
|
||||
|
||||
const empty = spawnSync("/bin/sh", ["-c", "env | grep -c ."], { env: {}, encoding: "utf8" });
|
||||
console.log(Number(empty.stdout.trim()) <= 3, empty.status);
|
||||
|
||||
const quiet = spawnSync("sh", ["-c", 'echo "$MODE" >&2; exit 4'], {
|
||||
env: { PATH: process.env.PATH ?? "/usr/bin:/bin", MODE: "loud" },
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
timeout: 5000,
|
||||
});
|
||||
console.log(quiet.status, quiet.stderr.toString().trim(), quiet.stdout.toString() === "");
|
||||
|
||||
const missing = spawnSync("/nonexistent/tool", [], { env: { A: "1" } });
|
||||
console.log(missing.status, missing.error?.message);
|
||||
@@ -0,0 +1 @@
|
||||
{ "plain": 2460, "san": 4060 }
|
||||
Reference in New Issue
Block a user