mirror of
https://github.com/denoland/deno.git
synced 2025-08-04 19:08:15 +00:00
fix(ext/node): support input option in spawnSync (#28792)
This commit is contained in:
parent
d1b4fcd77c
commit
3f510d2f0e
4 changed files with 99 additions and 3 deletions
|
@ -59,6 +59,7 @@ import { Socket } from "node:net";
|
|||
import {
|
||||
kDetached,
|
||||
kExtraStdio,
|
||||
kInputOption,
|
||||
kIpc,
|
||||
kNeedsNpmProcessState,
|
||||
} from "ext:deno_process/40_process.js";
|
||||
|
@ -985,6 +986,27 @@ function parseSpawnSyncOutputStreams(
|
|||
}
|
||||
}
|
||||
|
||||
function normalizeInput(input: unknown) {
|
||||
if (input == null) {
|
||||
return null;
|
||||
}
|
||||
if (typeof input === "string") {
|
||||
return Buffer.from(input);
|
||||
}
|
||||
if (input instanceof Uint8Array) {
|
||||
return input;
|
||||
}
|
||||
if (input instanceof DataView) {
|
||||
return Buffer.from(input.buffer, input.byteOffset, input.byteLength);
|
||||
}
|
||||
throw new ERR_INVALID_ARG_TYPE("input", [
|
||||
"string",
|
||||
"Buffer",
|
||||
"TypedArray",
|
||||
"DataView",
|
||||
], input);
|
||||
}
|
||||
|
||||
export function spawnSync(
|
||||
command: string,
|
||||
args: string[],
|
||||
|
@ -992,6 +1014,7 @@ export function spawnSync(
|
|||
): SpawnSyncResult {
|
||||
const {
|
||||
env = Deno.env.toObject(),
|
||||
input,
|
||||
stdio = ["pipe", "pipe", "pipe"],
|
||||
shell = false,
|
||||
cwd,
|
||||
|
@ -1008,6 +1031,7 @@ export function spawnSync(
|
|||
_channel, // TODO(kt3k): handle this correctly
|
||||
] = normalizeStdioOption(stdio);
|
||||
[command, args] = buildCommand(command, args ?? [], shell);
|
||||
const input_ = normalizeInput(input);
|
||||
|
||||
const result: SpawnSyncResult = {};
|
||||
try {
|
||||
|
@ -1021,6 +1045,7 @@ export function spawnSync(
|
|||
uid,
|
||||
gid,
|
||||
windowsRawArguments: windowsVerbatimArguments,
|
||||
[kInputOption]: input_,
|
||||
}).outputSync();
|
||||
|
||||
const status = output.signal ? null : output.code;
|
||||
|
|
|
@ -41,6 +41,9 @@ import {
|
|||
writableStreamForRid,
|
||||
} from "ext:deno_web/06_streams.js";
|
||||
|
||||
// The key for private `input` option for `Deno.Command`
|
||||
const kInputOption = Symbol("kInputOption");
|
||||
|
||||
function opKill(pid, signo, apiName) {
|
||||
op_kill(pid, signo, apiName);
|
||||
}
|
||||
|
@ -404,6 +407,7 @@ function spawnSync(command, {
|
|||
stdout = "piped",
|
||||
stderr = "piped",
|
||||
windowsRawArguments = false,
|
||||
[kInputOption]: input,
|
||||
} = { __proto__: null }) {
|
||||
if (stdin === "piped") {
|
||||
throw new TypeError(
|
||||
|
@ -425,6 +429,7 @@ function spawnSync(command, {
|
|||
extraStdio: [],
|
||||
detached: false,
|
||||
needsNpmProcessState: false,
|
||||
input,
|
||||
});
|
||||
return {
|
||||
success: result.status.success,
|
||||
|
@ -484,4 +489,4 @@ class Command {
|
|||
}
|
||||
}
|
||||
|
||||
export { ChildProcess, Command, kill, Process, run };
|
||||
export { ChildProcess, Command, kill, kInputOption, Process, run };
|
||||
|
|
|
@ -20,6 +20,7 @@ use deno_core::op2;
|
|||
use deno_core::serde_json;
|
||||
use deno_core::AsyncMutFuture;
|
||||
use deno_core::AsyncRefCell;
|
||||
use deno_core::JsBuffer;
|
||||
use deno_core::OpState;
|
||||
use deno_core::RcRef;
|
||||
use deno_core::Resource;
|
||||
|
@ -193,6 +194,8 @@ pub struct SpawnArgs {
|
|||
#[serde(flatten)]
|
||||
stdio: ChildStdio,
|
||||
|
||||
input: Option<JsBuffer>,
|
||||
|
||||
extra_stdio: Vec<Stdio>,
|
||||
detached: bool,
|
||||
needs_npm_process_state: bool,
|
||||
|
@ -437,6 +440,8 @@ fn create_command(
|
|||
|
||||
if args.stdio.stdin.is_ipc() {
|
||||
args.ipc = Some(0);
|
||||
} else if args.input.is_some() {
|
||||
command.stdin(std::process::Stdio::piped());
|
||||
} else {
|
||||
command.stdin(args.stdio.stdin.as_stdio(state)?);
|
||||
}
|
||||
|
@ -936,13 +941,31 @@ fn op_spawn_sync(
|
|||
) -> Result<SpawnOutput, ProcessError> {
|
||||
let stdout = matches!(args.stdio.stdout, StdioOrRid::Stdio(Stdio::Piped));
|
||||
let stderr = matches!(args.stdio.stderr, StdioOrRid::Stdio(Stdio::Piped));
|
||||
let input = args.input.clone();
|
||||
let (mut command, _, _, _) =
|
||||
create_command(state, args, "Deno.Command().outputSync()")?;
|
||||
let output = command.output().map_err(|e| ProcessError::SpawnFailed {
|
||||
|
||||
let mut child = command.spawn().map_err(|e| ProcessError::SpawnFailed {
|
||||
command: command.get_program().to_string_lossy().to_string(),
|
||||
error: Box::new(e.into()),
|
||||
})?;
|
||||
|
||||
if let Some(input) = input {
|
||||
let mut stdin = child.stdin.take().ok_or_else(|| {
|
||||
ProcessError::Io(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"stdin is not available",
|
||||
))
|
||||
})?;
|
||||
stdin.write_all(&input)?;
|
||||
stdin.flush()?;
|
||||
}
|
||||
let output =
|
||||
child
|
||||
.wait_with_output()
|
||||
.map_err(|e| ProcessError::SpawnFailed {
|
||||
command: command.get_program().to_string_lossy().to_string(),
|
||||
error: Box::new(e.into()),
|
||||
})?;
|
||||
Ok(SpawnOutput {
|
||||
status: output.status.try_into()?,
|
||||
stdout: if stdout {
|
||||
|
|
|
@ -1128,3 +1128,46 @@ Deno.test(async function noWarningsFlag() {
|
|||
|
||||
await timeout.promise;
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "[node/child_process] spawnSync supports input option",
|
||||
fn() {
|
||||
const text = " console.log('hello')";
|
||||
const expected = `console.log("hello");\n`;
|
||||
{
|
||||
const { stdout } = spawnSync(Deno.execPath(), ["fmt", "-"], {
|
||||
input: text,
|
||||
});
|
||||
assertEquals(stdout.toString(), expected);
|
||||
}
|
||||
{
|
||||
const { stdout } = spawnSync(Deno.execPath(), ["fmt", "-"], {
|
||||
input: Buffer.from(text),
|
||||
});
|
||||
assertEquals(stdout.toString(), expected);
|
||||
}
|
||||
{
|
||||
const { stdout } = spawnSync(Deno.execPath(), ["fmt", "-"], {
|
||||
input: new TextEncoder().encode(text),
|
||||
});
|
||||
assertEquals(stdout.toString(), expected);
|
||||
}
|
||||
{
|
||||
const { stdout } = spawnSync(Deno.execPath(), ["fmt", "-"], {
|
||||
input: new DataView(Buffer.from(text).buffer),
|
||||
});
|
||||
assertEquals(stdout.toString(), expected);
|
||||
}
|
||||
|
||||
assertThrows(
|
||||
() => {
|
||||
spawnSync(Deno.execPath(), ["fmt", "-"], {
|
||||
// deno-lint-ignore no-explicit-any
|
||||
input: {} as any,
|
||||
});
|
||||
},
|
||||
Error,
|
||||
'The "input" argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received an instance of Object',
|
||||
);
|
||||
},
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue