1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-29 10:39:10 -05:00
denoland-deno/cli/js/process.ts

140 lines
3.6 KiB
TypeScript
Raw Normal View History

2020-01-02 15:13:47 -05:00
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
2020-07-06 21:45:39 -04:00
import { File } from "./files.ts";
import { close } from "./ops/resources.ts";
import type { Closer, Reader, Writer } from "./io.ts";
import { readAll } from "./buffer.ts";
import { kill, runStatus as runStatusOp, run as runOp } from "./ops/process.ts";
// TODO Maybe extend VSCode's 'CommandOptions'?
// See https://code.visualstudio.com/docs/editor/tasks-appendix#_schema-for-tasksjson
export interface RunOptions {
cmd: string[];
cwd?: string;
2020-07-06 21:45:39 -04:00
env?: Record<string, string>;
stdout?: "inherit" | "piped" | "null" | number;
stderr?: "inherit" | "piped" | "null" | number;
stdin?: "inherit" | "piped" | "null" | number;
}
2019-08-26 08:50:21 -04:00
async function runStatus(rid: number): Promise<ProcessStatus> {
const res = await runStatusOp(rid);
2019-08-26 08:50:21 -04:00
if (res.gotSignal) {
const signal = res.exitSignal;
return { success: false, code: 128 + signal, signal };
} else if (res.exitCode != 0) {
return { success: false, code: res.exitCode };
} else {
return { success: true, code: 0 };
}
}
export class Process<T extends RunOptions = RunOptions> {
readonly rid: number;
readonly pid: number;
readonly stdin!: T["stdin"] extends "piped"
? Writer & Closer
: (Writer & Closer) | null;
readonly stdout!: T["stdout"] extends "piped"
? Reader & Closer
: (Reader & Closer) | null;
readonly stderr!: T["stderr"] extends "piped"
? Reader & Closer
: (Reader & Closer) | null;
// @internal
2019-08-26 08:50:21 -04:00
constructor(res: RunResponse) {
this.rid = res.rid;
this.pid = res.pid;
2019-08-26 08:50:21 -04:00
if (res.stdinRid && res.stdinRid > 0) {
this.stdin = (new File(res.stdinRid) as unknown) as Process<T>["stdin"];
}
2019-08-26 08:50:21 -04:00
if (res.stdoutRid && res.stdoutRid > 0) {
this.stdout = (new File(res.stdoutRid) as unknown) as Process<
T
>["stdout"];
}
2019-08-26 08:50:21 -04:00
if (res.stderrRid && res.stderrRid > 0) {
this.stderr = (new File(res.stderrRid) as unknown) as Process<
T
>["stderr"];
}
}
2020-03-20 09:38:34 -04:00
status(): Promise<ProcessStatus> {
return runStatus(this.rid);
}
2018-11-30 13:44:05 -05:00
async output(): Promise<Uint8Array> {
if (!this.stdout) {
throw new TypeError("stdout was not piped");
2018-11-30 13:44:05 -05:00
}
try {
return await readAll(this.stdout as Reader & Closer);
2018-11-30 13:44:05 -05:00
} finally {
(this.stdout as Reader & Closer).close();
2018-11-30 13:44:05 -05:00
}
}
2019-03-28 16:09:46 -04:00
async stderrOutput(): Promise<Uint8Array> {
if (!this.stderr) {
throw new TypeError("stderr was not piped");
2019-03-28 16:09:46 -04:00
}
try {
return await readAll(this.stderr as Reader & Closer);
2019-03-28 16:09:46 -04:00
} finally {
(this.stderr as Reader & Closer).close();
2019-03-28 16:09:46 -04:00
}
}
close(): void {
close(this.rid);
}
kill(signo: number): void {
kill(this.pid, signo);
}
}
export type ProcessStatus =
2020-07-06 21:45:39 -04:00
| { success: true; code: 0; signal?: undefined }
| { success: false; code: number; signal?: number };
function isRid(arg: unknown): arg is number {
return !isNaN(arg as number);
}
2019-08-26 08:50:21 -04:00
interface RunResponse {
rid: number;
pid: number;
stdinRid: number | null;
stdoutRid: number | null;
stderrRid: number | null;
}
export function run<T extends RunOptions = RunOptions>({
cmd,
cwd = undefined,
env = {},
stdout = "inherit",
stderr = "inherit",
stdin = "inherit",
}: T): Process<T> {
const res = runOp({
cmd: cmd.map(String),
cwd,
env: Object.entries(env),
stdin: isRid(stdin) ? "" : stdin,
stdout: isRid(stdout) ? "" : stdout,
stderr: isRid(stderr) ? "" : stderr,
stdinRid: isRid(stdin) ? stdin : 0,
stdoutRid: isRid(stdout) ? stdout : 0,
stderrRid: isRid(stderr) ? stderr : 0,
}) as RunResponse;
return new Process<T>(res);
}