mirror of
https://github.com/denoland/deno.git
synced 2024-12-23 15:49:44 -05:00
feat: Stabilize Deno.Command API (#17628)
This commit stabilizes "Deno.Command" API with all its related APIs. "--unstable" flag is no longer required to use this API.
This commit is contained in:
parent
9e3d433249
commit
f917d2e2c1
7 changed files with 318 additions and 475 deletions
|
@ -29,15 +29,6 @@ const UNSTABLE_DENO_PROPS: &[&str] = &[
|
|||
"removeSignalListener",
|
||||
"shutdown",
|
||||
"umask",
|
||||
"Child",
|
||||
"ChildProcess",
|
||||
"ChildStatus",
|
||||
"SpawnOutput",
|
||||
"command",
|
||||
"Command",
|
||||
"CommandOptions",
|
||||
"CommandStatus",
|
||||
"CommandOutput",
|
||||
"serve",
|
||||
"ServeInit",
|
||||
"ServeTlsInit",
|
||||
|
|
212
cli/tsc/dts/lib.deno.ns.d.ts
vendored
212
cli/tsc/dts/lib.deno.ns.d.ts
vendored
|
@ -3958,6 +3958,218 @@ declare namespace Deno {
|
|||
*/
|
||||
export function run<T extends RunOptions = RunOptions>(opt: T): Process<T>;
|
||||
|
||||
/** Create a child process.
|
||||
*
|
||||
* If any stdio options are not set to `"piped"`, accessing the corresponding
|
||||
* field on the `Command` or its `CommandOutput` will throw a `TypeError`.
|
||||
*
|
||||
* If `stdin` is set to `"piped"`, the `stdin` {@linkcode WritableStream}
|
||||
* needs to be closed manually.
|
||||
*
|
||||
* @example Spawn a subprocess and pipe the output to a file
|
||||
*
|
||||
* ```ts
|
||||
* const command = new Deno.Command(Deno.execPath(), {
|
||||
* args: [
|
||||
* "eval",
|
||||
* "console.log('Hello World')",
|
||||
* ],
|
||||
* stdin: "piped",
|
||||
* });
|
||||
* const child = command.spawn();
|
||||
*
|
||||
* // open a file and pipe the subprocess output to it.
|
||||
* child.stdout.pipeTo(Deno.openSync("output").writable);
|
||||
*
|
||||
* // manually close stdin
|
||||
* child.stdin.close();
|
||||
* const status = await child.status;
|
||||
* ```
|
||||
*
|
||||
* @example Spawn a subprocess and collect its output
|
||||
*
|
||||
* ```ts
|
||||
* const command = new Deno.Command(Deno.execPath(), {
|
||||
* args: [
|
||||
* "eval",
|
||||
* "console.log('hello'); console.error('world')",
|
||||
* ],
|
||||
* });
|
||||
* const { code, stdout, stderr } = await command.output();
|
||||
* console.assert(code === 0);
|
||||
* console.assert("hello\n" === new TextDecoder().decode(stdout));
|
||||
* console.assert("world\n" === new TextDecoder().decode(stderr));
|
||||
* ```
|
||||
*
|
||||
* @example Spawn a subprocess and collect its output synchronously
|
||||
*
|
||||
* ```ts
|
||||
* const command = new Deno.Command(Deno.execPath(), {
|
||||
* args: [
|
||||
* "eval",
|
||||
* "console.log('hello'); console.error('world')",
|
||||
* ],
|
||||
* });
|
||||
* const { code, stdout, stderr } = command.outputSync();
|
||||
* console.assert(code === 0);
|
||||
* console.assert("hello\n" === new TextDecoder().decode(stdout));
|
||||
* console.assert("world\n" === new TextDecoder().decode(stderr));
|
||||
* ```
|
||||
*
|
||||
* @category Sub Process
|
||||
*/
|
||||
export class Command {
|
||||
constructor(command: string | URL, options?: CommandOptions);
|
||||
/**
|
||||
* Executes the {@linkcode Deno.Command}, waiting for it to finish and
|
||||
* collecting all of its output.
|
||||
* If `spawn()` was called, calling this function will collect the remaining
|
||||
* output.
|
||||
*
|
||||
* Will throw an error if `stdin: "piped"` is set.
|
||||
*
|
||||
* If options `stdout` or `stderr` are not set to `"piped"`, accessing the
|
||||
* corresponding field on {@linkcode Deno.CommandOutput} will throw a `TypeError`.
|
||||
*/
|
||||
output(): Promise<CommandOutput>;
|
||||
/**
|
||||
* Synchronously executes the {@linkcode Deno.Command}, waiting for it to
|
||||
* finish and collecting all of its output.
|
||||
*
|
||||
* Will throw an error if `stdin: "piped"` is set.
|
||||
*
|
||||
* If options `stdout` or `stderr` are not set to `"piped"`, accessing the
|
||||
* corresponding field on {@linkcode Deno.CommandOutput} will throw a `TypeError`.
|
||||
*/
|
||||
outputSync(): CommandOutput;
|
||||
/**
|
||||
* Spawns a streamable subprocess, allowing to use the other methods.
|
||||
*/
|
||||
spawn(): ChildProcess;
|
||||
}
|
||||
|
||||
/**
|
||||
* The interface for handling a child process returned from
|
||||
* {@linkcode Deno.Command.spawn}.
|
||||
*
|
||||
* @category Sub Process
|
||||
*/
|
||||
export class ChildProcess {
|
||||
get stdin(): WritableStream<Uint8Array>;
|
||||
get stdout(): ReadableStream<Uint8Array>;
|
||||
get stderr(): ReadableStream<Uint8Array>;
|
||||
readonly pid: number;
|
||||
/** Get the status of the child. */
|
||||
readonly status: Promise<CommandStatus>;
|
||||
|
||||
/** Waits for the child to exit completely, returning all its output and
|
||||
* status. */
|
||||
output(): Promise<CommandOutput>;
|
||||
/** Kills the process with given {@linkcode Deno.Signal}.
|
||||
*
|
||||
* @param [signo="SIGTERM"]
|
||||
*/
|
||||
kill(signo?: Signal): void;
|
||||
|
||||
/** Ensure that the status of the child process prevents the Deno process
|
||||
* from exiting. */
|
||||
ref(): void;
|
||||
/** Ensure that the status of the child process does not block the Deno
|
||||
* process from exiting. */
|
||||
unref(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options which can be set when calling {@linkcode Deno.Command}.
|
||||
*
|
||||
* @category Sub Process
|
||||
*/
|
||||
export interface CommandOptions {
|
||||
/** Arguments to pass to the process. */
|
||||
args?: string[];
|
||||
/**
|
||||
* The working directory of the process.
|
||||
*
|
||||
* If not specified, the `cwd` of the parent process is used.
|
||||
*/
|
||||
cwd?: string | URL;
|
||||
/**
|
||||
* Clear environmental variables from parent process.
|
||||
*
|
||||
* Doesn't guarantee that only `env` variables are present, as the OS may
|
||||
* set environmental variables for processes.
|
||||
*
|
||||
* @default {false}
|
||||
*/
|
||||
clearEnv?: boolean;
|
||||
/** Environmental variables to pass to the subprocess. */
|
||||
env?: Record<string, string>;
|
||||
/**
|
||||
* Sets the child process’s user ID. This translates to a setuid call in the
|
||||
* child process. Failure in the set uid call will cause the spawn to fail.
|
||||
*/
|
||||
uid?: number;
|
||||
/** Similar to `uid`, but sets the group ID of the child process. */
|
||||
gid?: number;
|
||||
/**
|
||||
* An {@linkcode AbortSignal} that allows closing the process using the
|
||||
* corresponding {@linkcode AbortController} by sending the process a
|
||||
* SIGTERM signal.
|
||||
*
|
||||
* Not supported in {@linkcode Deno.Command.outputSync}.
|
||||
*/
|
||||
signal?: AbortSignal;
|
||||
|
||||
/** How `stdin` of the spawned process should be handled.
|
||||
*
|
||||
* Defaults to `"inherit"` for `output` & `outputSync`,
|
||||
* and `"inherit"` for `spawn`. */
|
||||
stdin?: "piped" | "inherit" | "null";
|
||||
/** How `stdout` of the spawned process should be handled.
|
||||
*
|
||||
* Defaults to `"piped"` for `output` & `outputSync`,
|
||||
* and `"inherit"` for `spawn`. */
|
||||
stdout?: "piped" | "inherit" | "null";
|
||||
/** How `stderr` of the spawned process should be handled.
|
||||
*
|
||||
* Defaults to `"piped"` for `output` & `outputSync`,
|
||||
* and `"inherit"` for `spawn`. */
|
||||
stderr?: "piped" | "inherit" | "null";
|
||||
|
||||
/** Skips quoting and escaping of the arguments on windows. This option
|
||||
* is ignored on non-windows platforms.
|
||||
*
|
||||
* @default {false} */
|
||||
windowsRawArguments?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @category Sub Process
|
||||
*/
|
||||
export interface CommandStatus {
|
||||
/** If the child process exits with a 0 status code, `success` will be set
|
||||
* to `true`, otherwise `false`. */
|
||||
success: boolean;
|
||||
/** The exit code of the child process. */
|
||||
code: number;
|
||||
/** The signal associated with the child process. */
|
||||
signal: Signal | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The interface returned from calling {@linkcode Command.output} or
|
||||
* {@linkcode Command.outputSync} which represents the result of spawning the
|
||||
* child process.
|
||||
*
|
||||
* @category Sub Process
|
||||
*/
|
||||
export interface CommandOutput extends CommandStatus {
|
||||
/** The buffered output from the child process' `stdout`. */
|
||||
readonly stdout: Uint8Array;
|
||||
/** The buffered output from the child process' `stderr`. */
|
||||
readonly stderr: Uint8Array;
|
||||
}
|
||||
|
||||
/** Option which can be specified when performing {@linkcode Deno.inspect}.
|
||||
*
|
||||
* @category Console and Debugging */
|
||||
|
|
218
cli/tsc/dts/lib.deno.unstable.d.ts
vendored
218
cli/tsc/dts/lib.deno.unstable.d.ts
vendored
|
@ -1407,224 +1407,6 @@ declare namespace Deno {
|
|||
*/
|
||||
export function upgradeHttpRaw(request: Request): [Deno.Conn, Uint8Array];
|
||||
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
* Create a child process.
|
||||
*
|
||||
* If any stdio options are not set to `"piped"`, accessing the corresponding
|
||||
* field on the `Command` or its `CommandOutput` will throw a `TypeError`.
|
||||
*
|
||||
* If `stdin` is set to `"piped"`, the `stdin` {@linkcode WritableStream}
|
||||
* needs to be closed manually.
|
||||
*
|
||||
* @example Spawn a subprocess and pipe the output to a file
|
||||
*
|
||||
* ```ts
|
||||
* const command = new Deno.Command(Deno.execPath(), {
|
||||
* args: [
|
||||
* "eval",
|
||||
* "console.log('Hello World')",
|
||||
* ],
|
||||
* stdin: "piped",
|
||||
* });
|
||||
* const child = command.spawn();
|
||||
*
|
||||
* // open a file and pipe the subprocess output to it.
|
||||
* child.stdout.pipeTo(Deno.openSync("output").writable);
|
||||
*
|
||||
* // manually close stdin
|
||||
* child.stdin.close();
|
||||
* const status = await child.status;
|
||||
* ```
|
||||
*
|
||||
* @example Spawn a subprocess and collect its output
|
||||
*
|
||||
* ```ts
|
||||
* const command = new Deno.Command(Deno.execPath(), {
|
||||
* args: [
|
||||
* "eval",
|
||||
* "console.log('hello'); console.error('world')",
|
||||
* ],
|
||||
* });
|
||||
* const { code, stdout, stderr } = await command.output();
|
||||
* console.assert(code === 0);
|
||||
* console.assert("hello\n" === new TextDecoder().decode(stdout));
|
||||
* console.assert("world\n" === new TextDecoder().decode(stderr));
|
||||
* ```
|
||||
*
|
||||
* @example Spawn a subprocess and collect its output synchronously
|
||||
*
|
||||
* ```ts
|
||||
* const command = new Deno.Command(Deno.execPath(), {
|
||||
* args: [
|
||||
* "eval",
|
||||
* "console.log('hello'); console.error('world')",
|
||||
* ],
|
||||
* });
|
||||
* const { code, stdout, stderr } = command.outputSync();
|
||||
* console.assert(code === 0);
|
||||
* console.assert("hello\n" === new TextDecoder().decode(stdout));
|
||||
* console.assert("world\n" === new TextDecoder().decode(stderr));
|
||||
* ```
|
||||
*
|
||||
* @category Sub Process
|
||||
*/
|
||||
export class Command {
|
||||
constructor(command: string | URL, options?: CommandOptions);
|
||||
/**
|
||||
* Executes the {@linkcode Deno.Command}, waiting for it to finish and
|
||||
* collecting all of its output.
|
||||
* If `spawn()` was called, calling this function will collect the remaining
|
||||
* output.
|
||||
*
|
||||
* Will throw an error if `stdin: "piped"` is set.
|
||||
*
|
||||
* If options `stdout` or `stderr` are not set to `"piped"`, accessing the
|
||||
* corresponding field on {@linkcode Deno.CommandOutput} will throw a `TypeError`.
|
||||
*/
|
||||
output(): Promise<CommandOutput>;
|
||||
/**
|
||||
* Synchronously executes the {@linkcode Deno.Command}, waiting for it to
|
||||
* finish and collecting all of its output.
|
||||
*
|
||||
* Will throw an error if `stdin: "piped"` is set.
|
||||
*
|
||||
* If options `stdout` or `stderr` are not set to `"piped"`, accessing the
|
||||
* corresponding field on {@linkcode Deno.CommandOutput} will throw a `TypeError`.
|
||||
*/
|
||||
outputSync(): CommandOutput;
|
||||
/**
|
||||
* Spawns a streamable subprocess, allowing to use the other methods.
|
||||
*/
|
||||
spawn(): ChildProcess;
|
||||
}
|
||||
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
* The interface for handling a child process returned from
|
||||
* {@linkcode Deno.Command.spawn}.
|
||||
*
|
||||
* @category Sub Process
|
||||
*/
|
||||
export class ChildProcess {
|
||||
get stdin(): WritableStream<Uint8Array>;
|
||||
get stdout(): ReadableStream<Uint8Array>;
|
||||
get stderr(): ReadableStream<Uint8Array>;
|
||||
readonly pid: number;
|
||||
/** Get the status of the child. */
|
||||
readonly status: Promise<CommandStatus>;
|
||||
|
||||
/** Waits for the child to exit completely, returning all its output and
|
||||
* status. */
|
||||
output(): Promise<CommandOutput>;
|
||||
/** Kills the process with given {@linkcode Deno.Signal}.
|
||||
*
|
||||
* @param [signo="SIGTERM"]
|
||||
*/
|
||||
kill(signo?: Signal): void;
|
||||
|
||||
/** Ensure that the status of the child process prevents the Deno process
|
||||
* from exiting. */
|
||||
ref(): void;
|
||||
/** Ensure that the status of the child process does not block the Deno
|
||||
* process from exiting. */
|
||||
unref(): void;
|
||||
}
|
||||
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
* Options which can be set when calling {@linkcode Deno.command}.
|
||||
*
|
||||
* @category Sub Process
|
||||
*/
|
||||
export interface CommandOptions {
|
||||
/** Arguments to pass to the process. */
|
||||
args?: readonly string[];
|
||||
/**
|
||||
* The working directory of the process.
|
||||
*
|
||||
* If not specified, the `cwd` of the parent process is used.
|
||||
*/
|
||||
cwd?: string | URL;
|
||||
/**
|
||||
* Clear environmental variables from parent process.
|
||||
*
|
||||
* Doesn't guarantee that only `env` variables are present, as the OS may
|
||||
* set environmental variables for processes.
|
||||
*
|
||||
* @default {false}
|
||||
*/
|
||||
clearEnv?: boolean;
|
||||
/** Environmental variables to pass to the subprocess. */
|
||||
env?: Record<string, string>;
|
||||
/**
|
||||
* Sets the child process’s user ID. This translates to a setuid call in the
|
||||
* child process. Failure in the set uid call will cause the spawn to fail.
|
||||
*/
|
||||
uid?: number;
|
||||
/** Similar to `uid`, but sets the group ID of the child process. */
|
||||
gid?: number;
|
||||
/**
|
||||
* An {@linkcode AbortSignal} that allows closing the process using the
|
||||
* corresponding {@linkcode AbortController} by sending the process a
|
||||
* SIGTERM signal.
|
||||
*
|
||||
* Not supported in {@linkcode Deno.Command.outputSync}.
|
||||
*/
|
||||
signal?: AbortSignal;
|
||||
|
||||
/** How `stdin` of the spawned process should be handled.
|
||||
*
|
||||
* Defaults to `"inherit"` for `output` & `outputSync`,
|
||||
* and `"inherit"` for `spawn`. */
|
||||
stdin?: "piped" | "inherit" | "null";
|
||||
/** How `stdout` of the spawned process should be handled.
|
||||
*
|
||||
* Defaults to `"piped"` for `output` & `outputSync`,
|
||||
* and `"inherit"` for `spawn`. */
|
||||
stdout?: "piped" | "inherit" | "null";
|
||||
/** How `stderr` of the spawned process should be handled.
|
||||
*
|
||||
* Defaults to `"piped"` for `output` & `outputSync`,
|
||||
* and `"inherit"` for `spawn`. */
|
||||
stderr?: "piped" | "inherit" | "null";
|
||||
|
||||
/** Skips quoting and escaping of the arguments on windows. This option
|
||||
* is ignored on non-windows platforms.
|
||||
*
|
||||
* @default {false} */
|
||||
windowsRawArguments?: boolean;
|
||||
}
|
||||
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
* @category Sub Process
|
||||
*/
|
||||
export interface CommandStatus {
|
||||
/** If the child process exits with a 0 status code, `success` will be set
|
||||
* to `true`, otherwise `false`. */
|
||||
success: boolean;
|
||||
/** The exit code of the child process. */
|
||||
code: number;
|
||||
/** The signal associated with the child process. */
|
||||
signal: Signal | null;
|
||||
}
|
||||
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
* The interface returned from calling {@linkcode Command.output} or
|
||||
* {@linkcode Command.outputSync} which represents the result of spawning the
|
||||
* child process.
|
||||
*
|
||||
* @category Sub Process
|
||||
*/
|
||||
export interface CommandOutput extends CommandStatus {
|
||||
/** The buffered output from the child process' `stdout`. */
|
||||
readonly stdout: Uint8Array;
|
||||
/** The buffered output from the child process' `stderr`. */
|
||||
readonly stderr: Uint8Array;
|
||||
}
|
||||
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
* Returns the Operating System uptime in number of seconds.
|
||||
|
|
|
@ -55,16 +55,19 @@ function spawnChildInner(opFn, command, apiName, {
|
|||
stderr,
|
||||
windowsRawArguments,
|
||||
}, apiName);
|
||||
return new Child(illegalConstructorKey, {
|
||||
return new ChildProcess(illegalConstructorKey, {
|
||||
...child,
|
||||
signal,
|
||||
});
|
||||
}
|
||||
|
||||
function createSpawnChild(opFn) {
|
||||
return function spawnChild(command, options = {}) {
|
||||
return spawnChildInner(opFn, command, "Deno.Command().spawn()", options);
|
||||
};
|
||||
function spawnChild(command, options = {}) {
|
||||
return spawnChildInner(
|
||||
ops.op_spawn_child,
|
||||
command,
|
||||
"Deno.Command().spawn()",
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
function collectOutput(readableStream) {
|
||||
|
@ -77,7 +80,7 @@ function collectOutput(readableStream) {
|
|||
return readableStreamCollectIntoUint8Array(readableStream);
|
||||
}
|
||||
|
||||
class Child {
|
||||
class ChildProcess {
|
||||
#rid;
|
||||
#waitPromiseId;
|
||||
#unrefed = false;
|
||||
|
@ -95,7 +98,6 @@ class Child {
|
|||
return this.#stdin;
|
||||
}
|
||||
|
||||
#stdoutPromiseId;
|
||||
#stdoutRid;
|
||||
#stdout = null;
|
||||
get stdout() {
|
||||
|
@ -105,7 +107,6 @@ class Child {
|
|||
return this.#stdout;
|
||||
}
|
||||
|
||||
#stderrPromiseId;
|
||||
#stderrRid;
|
||||
#stderr = null;
|
||||
get stderr() {
|
||||
|
@ -220,116 +221,106 @@ class Child {
|
|||
}
|
||||
}
|
||||
|
||||
function createSpawn(opFn) {
|
||||
return function spawn(command, options) {
|
||||
if (options?.stdin === "piped") {
|
||||
throw new TypeError(
|
||||
"Piped stdin is not supported for this function, use 'Deno.Command().spawn()' instead",
|
||||
);
|
||||
}
|
||||
return spawnChildInner(opFn, command, "Deno.Command().output()", options)
|
||||
.output();
|
||||
function spawn(command, options) {
|
||||
if (options?.stdin === "piped") {
|
||||
throw new TypeError(
|
||||
"Piped stdin is not supported for this function, use 'Deno.Command().spawn()' instead",
|
||||
);
|
||||
}
|
||||
return spawnChildInner(
|
||||
ops.op_spawn_child,
|
||||
command,
|
||||
"Deno.Command().output()",
|
||||
options,
|
||||
)
|
||||
.output();
|
||||
}
|
||||
|
||||
function spawnSync(command, {
|
||||
args = [],
|
||||
cwd = undefined,
|
||||
clearEnv = false,
|
||||
env = {},
|
||||
uid = undefined,
|
||||
gid = undefined,
|
||||
stdin = "null",
|
||||
stdout = "piped",
|
||||
stderr = "piped",
|
||||
windowsRawArguments = false,
|
||||
} = {}) {
|
||||
if (stdin === "piped") {
|
||||
throw new TypeError(
|
||||
"Piped stdin is not supported for this function, use 'Deno.Command().spawn()' instead",
|
||||
);
|
||||
}
|
||||
const result = ops.op_spawn_sync({
|
||||
cmd: pathFromURL(command),
|
||||
args: ArrayPrototypeMap(args, String),
|
||||
cwd: pathFromURL(cwd),
|
||||
clearEnv,
|
||||
env: ObjectEntries(env),
|
||||
uid,
|
||||
gid,
|
||||
stdin,
|
||||
stdout,
|
||||
stderr,
|
||||
windowsRawArguments,
|
||||
});
|
||||
return {
|
||||
success: result.status.success,
|
||||
code: result.status.code,
|
||||
signal: result.status.signal,
|
||||
get stdout() {
|
||||
if (result.stdout == null) {
|
||||
throw new TypeError("stdout is not piped");
|
||||
}
|
||||
return result.stdout;
|
||||
},
|
||||
get stderr() {
|
||||
if (result.stderr == null) {
|
||||
throw new TypeError("stderr is not piped");
|
||||
}
|
||||
return result.stderr;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createSpawnSync(opFn) {
|
||||
return function spawnSync(command, {
|
||||
args = [],
|
||||
cwd = undefined,
|
||||
clearEnv = false,
|
||||
env = {},
|
||||
uid = undefined,
|
||||
gid = undefined,
|
||||
stdin = "null",
|
||||
stdout = "piped",
|
||||
stderr = "piped",
|
||||
windowsRawArguments = false,
|
||||
} = {}) {
|
||||
if (stdin === "piped") {
|
||||
class Command {
|
||||
#command;
|
||||
#options;
|
||||
|
||||
constructor(command, options) {
|
||||
this.#command = command;
|
||||
this.#options = options;
|
||||
}
|
||||
|
||||
output() {
|
||||
if (this.#options?.stdin === "piped") {
|
||||
throw new TypeError(
|
||||
"Piped stdin is not supported for this function, use 'Deno.Command().spawn()' instead",
|
||||
"Piped stdin is not supported for this function, use 'Deno.Command.spawn()' instead",
|
||||
);
|
||||
}
|
||||
const result = opFn({
|
||||
cmd: pathFromURL(command),
|
||||
args: ArrayPrototypeMap(args, String),
|
||||
cwd: pathFromURL(cwd),
|
||||
clearEnv,
|
||||
env: ObjectEntries(env),
|
||||
uid,
|
||||
gid,
|
||||
stdin,
|
||||
stdout,
|
||||
stderr,
|
||||
windowsRawArguments,
|
||||
});
|
||||
return {
|
||||
success: result.status.success,
|
||||
code: result.status.code,
|
||||
signal: result.status.signal,
|
||||
get stdout() {
|
||||
if (result.stdout == null) {
|
||||
throw new TypeError("stdout is not piped");
|
||||
}
|
||||
return result.stdout;
|
||||
},
|
||||
get stderr() {
|
||||
if (result.stderr == null) {
|
||||
throw new TypeError("stderr is not piped");
|
||||
}
|
||||
return result.stderr;
|
||||
},
|
||||
return spawn(this.#command, this.#options);
|
||||
}
|
||||
|
||||
outputSync() {
|
||||
if (this.#options?.stdin === "piped") {
|
||||
throw new TypeError(
|
||||
"Piped stdin is not supported for this function, use 'Deno.Command.spawn()' instead",
|
||||
);
|
||||
}
|
||||
return spawnSync(this.#command, this.#options);
|
||||
}
|
||||
|
||||
spawn() {
|
||||
const options = {
|
||||
...(this.#options ?? {}),
|
||||
stdout: this.#options?.stdout ?? "inherit",
|
||||
stderr: this.#options?.stderr ?? "inherit",
|
||||
stdin: this.#options?.stdin ?? "inherit",
|
||||
};
|
||||
};
|
||||
return spawnChild(this.#command, options);
|
||||
}
|
||||
}
|
||||
|
||||
function createCommand(spawn, spawnSync, spawnChild) {
|
||||
return class Command {
|
||||
#command;
|
||||
#options;
|
||||
|
||||
constructor(command, options) {
|
||||
this.#command = command;
|
||||
this.#options = options;
|
||||
}
|
||||
|
||||
output() {
|
||||
if (this.#options?.stdin === "piped") {
|
||||
throw new TypeError(
|
||||
"Piped stdin is not supported for this function, use 'Deno.Command.spawn()' instead",
|
||||
);
|
||||
}
|
||||
return spawn(this.#command, this.#options);
|
||||
}
|
||||
|
||||
outputSync() {
|
||||
if (this.#options?.stdin === "piped") {
|
||||
throw new TypeError(
|
||||
"Piped stdin is not supported for this function, use 'Deno.Command.spawn()' instead",
|
||||
);
|
||||
}
|
||||
return spawnSync(this.#command, this.#options);
|
||||
}
|
||||
|
||||
spawn() {
|
||||
const options = {
|
||||
...(this.#options ?? {}),
|
||||
stdout: this.#options?.stdout ?? "inherit",
|
||||
stderr: this.#options?.stderr ?? "inherit",
|
||||
stdin: this.#options?.stdin ?? "inherit",
|
||||
};
|
||||
return spawnChild(this.#command, options);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const ChildProcess = Child;
|
||||
|
||||
export {
|
||||
Child,
|
||||
ChildProcess,
|
||||
createCommand,
|
||||
createSpawn,
|
||||
createSpawnChild,
|
||||
createSpawnSync,
|
||||
};
|
||||
export { ChildProcess, Command };
|
||||
|
|
|
@ -152,6 +152,9 @@ const denoNs = {
|
|||
consoleSize: tty.consoleSize,
|
||||
gid: os.gid,
|
||||
uid: os.uid,
|
||||
Command: spawn.Command,
|
||||
// TODO(bartlomieju): why is this exported?
|
||||
ChildProcess: spawn.ChildProcess,
|
||||
};
|
||||
|
||||
const denoNsUnstable = {
|
||||
|
@ -171,9 +174,6 @@ const denoNsUnstable = {
|
|||
flockSync: fs.flockSync,
|
||||
funlock: fs.funlock,
|
||||
funlockSync: fs.funlockSync,
|
||||
Child: spawn.Child,
|
||||
ChildProcess: spawn.ChildProcess,
|
||||
Command: spawn.Command,
|
||||
upgradeHttp: http.upgradeHttp,
|
||||
upgradeHttpRaw: flash.upgradeHttpRaw,
|
||||
};
|
||||
|
|
|
@ -62,7 +62,6 @@ import { errors } from "internal:runtime/js/01_errors.js";
|
|||
import * as webidl from "internal:deno_webidl/00_webidl.js";
|
||||
import DOMException from "internal:deno_web/01_dom_exception.js";
|
||||
import * as flash from "internal:deno_flash/01_http.js";
|
||||
import * as spawn from "internal:runtime/js/40_spawn.js";
|
||||
import {
|
||||
mainRuntimeGlobalProperties,
|
||||
setLanguage,
|
||||
|
@ -466,15 +465,6 @@ function bootstrapMainRuntime(runtimeOptions) {
|
|||
// a snapshot
|
||||
ObjectAssign(internals, {
|
||||
nodeUnstable: {
|
||||
Command: spawn.createCommand(
|
||||
spawn.createSpawn(ops.op_node_unstable_spawn_child),
|
||||
spawn.createSpawnSync(
|
||||
ops.op_node_unstable_spawn_sync,
|
||||
),
|
||||
spawn.createSpawnChild(
|
||||
ops.op_node_unstable_spawn_child,
|
||||
),
|
||||
),
|
||||
serve: flash.createServe(ops.op_node_unstable_flash_serve),
|
||||
upgradeHttpRaw: flash.upgradeHttpRaw,
|
||||
listenDatagram: net.createListenDatagram(
|
||||
|
@ -513,11 +503,6 @@ function bootstrapMainRuntime(runtimeOptions) {
|
|||
// the op function that needs to be passed will be invalidated by creating
|
||||
// a snapshot
|
||||
ObjectAssign(finalDenoNs, {
|
||||
Command: spawn.createCommand(
|
||||
spawn.createSpawn(ops.op_spawn_child),
|
||||
spawn.createSpawnSync(ops.op_spawn_sync),
|
||||
spawn.createSpawnChild(ops.op_spawn_child),
|
||||
),
|
||||
serve: flash.createServe(ops.op_flash_serve),
|
||||
listenDatagram: net.createListenDatagram(
|
||||
ops.op_net_listen_udp,
|
||||
|
@ -611,15 +596,6 @@ function bootstrapWorkerRuntime(
|
|||
// a snapshot
|
||||
ObjectAssign(internals, {
|
||||
nodeUnstable: {
|
||||
Command: spawn.createCommand(
|
||||
spawn.createSpawn(ops.op_node_unstable_spawn_child),
|
||||
spawn.createSpawnSync(
|
||||
ops.op_node_unstable_spawn_sync,
|
||||
),
|
||||
spawn.createSpawnChild(
|
||||
ops.op_node_unstable_spawn_child,
|
||||
),
|
||||
),
|
||||
serve: flash.createServe(ops.op_node_unstable_flash_serve),
|
||||
upgradeHttpRaw: flash.upgradeHttpRaw,
|
||||
listenDatagram: net.createListenDatagram(
|
||||
|
@ -650,11 +626,6 @@ function bootstrapWorkerRuntime(
|
|||
// the op function that needs to be passed will be invalidated by creating
|
||||
// a snapshot
|
||||
ObjectAssign(finalDenoNs, {
|
||||
Command: spawn.createCommand(
|
||||
spawn.createSpawn(ops.op_spawn_child),
|
||||
spawn.createSpawnSync(ops.op_spawn_sync),
|
||||
spawn.createSpawnChild(ops.op_spawn_child),
|
||||
),
|
||||
serve: flash.createServe(ops.op_flash_serve),
|
||||
listenDatagram: net.createListenDatagram(
|
||||
ops.op_net_listen_udp,
|
||||
|
|
|
@ -31,10 +31,8 @@ pub fn init() -> Extension {
|
|||
Extension::builder("deno_spawn")
|
||||
.ops(vec![
|
||||
op_spawn_child::decl(),
|
||||
op_node_unstable_spawn_child::decl(),
|
||||
op_spawn_wait::decl(),
|
||||
op_spawn_sync::decl(),
|
||||
op_node_unstable_spawn_sync::decl(),
|
||||
])
|
||||
.build()
|
||||
}
|
||||
|
@ -125,75 +123,11 @@ pub struct SpawnOutput {
|
|||
stderr: Option<ZeroCopyBuf>,
|
||||
}
|
||||
|
||||
fn node_unstable_create_command(
|
||||
state: &mut OpState,
|
||||
args: SpawnArgs,
|
||||
api_name: &str,
|
||||
) -> Result<std::process::Command, AnyError> {
|
||||
state
|
||||
.borrow_mut::<PermissionsContainer>()
|
||||
.check_run(&args.cmd, api_name)?;
|
||||
|
||||
let mut command = std::process::Command::new(args.cmd);
|
||||
|
||||
#[cfg(windows)]
|
||||
if args.windows_raw_arguments {
|
||||
for arg in args.args.iter() {
|
||||
command.raw_arg(arg);
|
||||
}
|
||||
} else {
|
||||
command.args(args.args);
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
command.args(args.args);
|
||||
|
||||
if let Some(cwd) = args.cwd {
|
||||
command.current_dir(cwd);
|
||||
}
|
||||
|
||||
if args.clear_env {
|
||||
command.env_clear();
|
||||
}
|
||||
command.envs(args.env);
|
||||
|
||||
#[cfg(unix)]
|
||||
if let Some(gid) = args.gid {
|
||||
command.gid(gid);
|
||||
}
|
||||
#[cfg(unix)]
|
||||
if let Some(uid) = args.uid {
|
||||
command.uid(uid);
|
||||
}
|
||||
#[cfg(unix)]
|
||||
// TODO(bartlomieju):
|
||||
#[allow(clippy::undocumented_unsafe_blocks)]
|
||||
unsafe {
|
||||
command.pre_exec(|| {
|
||||
libc::setgroups(0, std::ptr::null());
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
command.stdin(args.stdio.stdin.as_stdio());
|
||||
command.stdout(match args.stdio.stdout {
|
||||
Stdio::Inherit => StdioOrRid::Rid(1).as_stdio(state)?,
|
||||
value => value.as_stdio(),
|
||||
});
|
||||
command.stderr(match args.stdio.stderr {
|
||||
Stdio::Inherit => StdioOrRid::Rid(2).as_stdio(state)?,
|
||||
value => value.as_stdio(),
|
||||
});
|
||||
|
||||
Ok(command)
|
||||
}
|
||||
|
||||
fn create_command(
|
||||
state: &mut OpState,
|
||||
args: SpawnArgs,
|
||||
api_name: &str,
|
||||
) -> Result<std::process::Command, AnyError> {
|
||||
super::check_unstable(state, "Deno.Command");
|
||||
state
|
||||
.borrow_mut::<PermissionsContainer>()
|
||||
.check_run(&args.cmd, api_name)?;
|
||||
|
@ -223,12 +157,10 @@ fn create_command(
|
|||
|
||||
#[cfg(unix)]
|
||||
if let Some(gid) = args.gid {
|
||||
super::check_unstable(state, "Deno.CommandOptions.gid");
|
||||
command.gid(gid);
|
||||
}
|
||||
#[cfg(unix)]
|
||||
if let Some(uid) = args.uid {
|
||||
super::check_unstable(state, "Deno.CommandOptions.uid");
|
||||
command.uid(uid);
|
||||
}
|
||||
#[cfg(unix)]
|
||||
|
@ -313,16 +245,6 @@ fn op_spawn_child(
|
|||
spawn_child(state, command)
|
||||
}
|
||||
|
||||
#[op]
|
||||
fn op_node_unstable_spawn_child(
|
||||
state: &mut OpState,
|
||||
args: SpawnArgs,
|
||||
api_name: String,
|
||||
) -> Result<Child, AnyError> {
|
||||
let command = node_unstable_create_command(state, args, &api_name)?;
|
||||
spawn_child(state, command)
|
||||
}
|
||||
|
||||
#[op]
|
||||
async fn op_spawn_wait(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
|
@ -365,29 +287,3 @@ fn op_spawn_sync(
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
#[op]
|
||||
fn op_node_unstable_spawn_sync(
|
||||
state: &mut OpState,
|
||||
args: SpawnArgs,
|
||||
) -> Result<SpawnOutput, AnyError> {
|
||||
let stdout = matches!(args.stdio.stdout, Stdio::Piped);
|
||||
let stderr = matches!(args.stdio.stderr, Stdio::Piped);
|
||||
let output =
|
||||
node_unstable_create_command(state, args, "Deno.Command().outputSync()")?
|
||||
.output()?;
|
||||
|
||||
Ok(SpawnOutput {
|
||||
status: output.status.try_into()?,
|
||||
stdout: if stdout {
|
||||
Some(output.stdout.into())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
stderr: if stderr {
|
||||
Some(output.stderr.into())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue