aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar dave caruso <me@paperdave.net> 2023-04-05 22:15:06 -0400
committerGravatar GitHub <noreply@github.com> 2023-04-05 19:15:06 -0700
commit569d4940bb68cb47fa546b5505f5a326073d6c5c (patch)
tree03089fd034a0420619bd0d044c412559d780bbb4
parent4af78c7d5a88378fb5912d160a936f79a30f46be (diff)
downloadbun-569d4940bb68cb47fa546b5505f5a326073d6c5c.tar.gz
bun-569d4940bb68cb47fa546b5505f5a326073d6c5c.tar.zst
bun-569d4940bb68cb47fa546b5505f5a326073d6c5c.zip
rebase (#1501)
-rw-r--r--packages/bun-types/bun.d.ts385
-rw-r--r--packages/bun-types/tests/spawn.test-d.ts64
2 files changed, 305 insertions, 144 deletions
diff --git a/packages/bun-types/bun.d.ts b/packages/bun-types/bun.d.ts
index 1768432df..0c7116d40 100644
--- a/packages/bun-types/bun.d.ts
+++ b/packages/bun-types/bun.d.ts
@@ -2964,31 +2964,41 @@ declare module "bun" {
): UnixSocketListener<Data>;
namespace SpawnOptions {
+ /**
+ * Option for stdout/stderr
+ */
type Readable =
+ | "pipe"
| "inherit"
| "ignore"
- | "pipe"
- | null
- | undefined
+ | null // equivalent to "ignore"
+ | undefined // use default
| FileBlob
| ArrayBufferView
| number;
+ /**
+ * Option for stdin
+ */
type Writable =
+ | "pipe"
| "inherit"
| "ignore"
- | "pipe"
- | null
- | ReadableStream // supported by stdin
- | undefined
+ | null // equivalent to "ignore"
+ | undefined // use default
| FileBlob
| ArrayBufferView
- | Blob
| number
+ | ReadableStream
+ | Blob
| Response
| Request;
- interface OptionsObject {
+ interface OptionsObject<
+ In extends Writable = Writable,
+ Out extends Readable = Readable,
+ Err extends Readable = Readable,
+ > {
/**
* The current working directory of the process
*
@@ -3004,25 +3014,69 @@ declare module "bun" {
* Changes to `process.env` at runtime won't automatically be reflected in the default value. For that, you can pass `process.env` explicitly.
*
*/
- env?: Record<string, string | number>;
+ env?: Record<string, string>;
/**
- * The standard file descriptors of the process
- * - `inherit`: The process will inherit the standard input of the current process
- * - `pipe`: The process will have a new pipe for standard input
- * - `null`: The process will have no standard input
+ * The standard file descriptors of the process, in the form [stdin, stdout, stderr].
+ * This overrides the `stdin`, `stdout`, and `stderr` properties.
+ *
+ * For stdin you may pass:
+ *
+ * - `"ignore"`, `null`, `undefined`: The process will have no standard input (default)
+ * - `"pipe"`: The process will have a new {@link FileSink} for standard input
+ * - `"inherit"`: The process will inherit the standard input of the current process
+ * - `ArrayBufferView`, `Blob`, `Bun.file()`, `Response`, `Request`: The process will read from buffer/stream.
+ * - `number`: The process will read from the file descriptor
+ *
+ * For stdout and stdin you may pass:
+ *
+ * - `"pipe"`, `undefined`: The process will have a {@link ReadableStream} for standard output/error
+ * - `"ignore"`, `null`: The process will have no standard output/error
+ * - `"inherit"`: The process will inherit the standard output/error of the current process
+ * - `ArrayBufferView`: The process write to the preallocated buffer. Not implemented.
+ * - `number`: The process will write to the file descriptor
+ *
+ * @default ["ignore", "pipe", "inherit"] for `spawn`
+ * ["ignore", "pipe", "pipe"] for `spawnSync`
+ */
+ stdio?: [In, Out, Err];
+ /**
+ * The file descriptor for the standard input. It may be:
+ *
+ * - `"ignore"`, `null`, `undefined`: The process will have no standard input
+ * - `"pipe"`: The process will have a new {@link FileSink} for standard input
+ * - `"inherit"`: The process will inherit the standard input of the current process
* - `ArrayBufferView`, `Blob`: The process will read from the buffer
* - `number`: The process will read from the file descriptor
- * - `undefined`: The default value
+ *
+ * @default "ignore"
+ */
+ stdin?: In;
+ /**
+ * The file descriptor for the standard output. It may be:
+ *
+ * - `"pipe"`, `undefined`: The process will have a {@link ReadableStream} for standard output/error
+ * - `"ignore"`, `null`: The process will have no standard output/error
+ * - `"inherit"`: The process will inherit the standard output/error of the current process
+ * - `ArrayBufferView`: The process write to the preallocated buffer. Not implemented.
+ * - `number`: The process will write to the file descriptor
+ *
+ * @default "pipe"
+ */
+ stdout?: Out;
+ /**
+ * The file descriptor for the standard error. It may be:
+ *
+ * - `"pipe"`, `undefined`: The process will have a {@link ReadableStream} for standard output/error
+ * - `"ignore"`, `null`: The process will have no standard output/error
+ * - `"inherit"`: The process will inherit the standard output/error of the current process
+ * - `ArrayBufferView`: The process write to the preallocated buffer. Not implemented.
+ * - `number`: The process will write to the file descriptor
+ *
+ * @default "inherit" for `spawn`
+ * "pipe" for `spawnSync`
*/
- stdio?: [
- SpawnOptions.Writable,
- SpawnOptions.Readable,
- SpawnOptions.Readable,
- ];
- stdin?: SpawnOptions.Writable;
- stdout?: SpawnOptions.Readable;
- stderr?: SpawnOptions.Readable;
+ stderr?: Err;
/**
* Callback that runs when the {@link Subprocess} exits
@@ -3045,7 +3099,7 @@ declare module "bun" {
* ```
*/
onExit?(
- subprocess: Subprocess,
+ subprocess: Subprocess<In, Out, Err>,
exitCode: number | null,
signalCode: number | null,
/**
@@ -3054,24 +3108,64 @@ declare module "bun" {
error?: Errorlike,
): void | Promise<void>;
}
- }
- interface SubprocessIO {
- readonly stdin?: undefined | number | ReadableStream | FileSink;
- readonly stdout?: undefined | number | ReadableStream;
- readonly stderr?: undefined | number | ReadableStream;
+ type OptionsToSubprocess<Opts extends OptionsObject> =
+ Opts extends OptionsObject<infer In, infer Out, infer Err>
+ ? Subprocess<
+ // "Writable extends In" means "if In === Writable",
+ // aka if true that means the user didn't specify anything
+ Writable extends In ? "ignore" : In,
+ Readable extends Out ? "pipe" : Out,
+ Readable extends Err ? "pipe" : Err
+ >
+ : Subprocess<Writable, Readable, Readable>;
+
+ type OptionsToSyncSubprocess<Opts extends OptionsObject> =
+ Opts extends OptionsObject<any, infer Out, infer Err>
+ ? SyncSubprocess<
+ Readable extends Out ? "pipe" : Out,
+ Readable extends Err ? "pipe" : Err
+ >
+ : SyncSubprocess<Readable, Readable>;
+
+ type ReadableToIO<X extends Readable> = X extends "pipe" | undefined
+ ? ReadableStream<Buffer>
+ : X extends FileBlob | ArrayBufferView | number
+ ? number
+ : undefined;
+
+ type ReadableToSyncIO<X extends Readable> = X extends "pipe" | undefined
+ ? Buffer
+ : undefined;
+
+ type WritableToIO<X extends Writable> = X extends "pipe"
+ ? FileSink
+ : X extends
+ | FileBlob
+ | ArrayBufferView
+ | Blob
+ | Request
+ | Response
+ | number
+ ? number
+ : undefined;
}
- interface Subprocess<T extends SubprocessIO = SubprocessIO> {
- readonly stdin: T["stdin"] | undefined;
- readonly stdout: T["stdout"] | undefined;
- readonly stderr: T["stderr"] | undefined;
+
+ interface Subprocess<
+ In extends SpawnOptions.Writable = SpawnOptions.Writable,
+ Out extends SpawnOptions.Readable = SpawnOptions.Readable,
+ Err extends SpawnOptions.Readable = SpawnOptions.Readable,
+ > {
+ readonly stdin: SpawnOptions.WritableToIO<In>;
+ readonly stdout: SpawnOptions.ReadableToIO<Out>;
+ readonly stderr: SpawnOptions.ReadableToIO<Err>;
/**
* This returns the same value as {@link Subprocess.stdout}
*
* It exists for compatibility with {@link ReadableStream.pipeThrough}
*/
- readonly readable: T["stdout"] | undefined;
+ readonly readable: SpawnOptions.ReadableToIO<Out>;
/**
* The process ID of the child process
@@ -3135,86 +3229,12 @@ declare module "bun" {
unref(): void;
}
- export class FileSystemRouter {
- /**
- * Create a new {@link FileSystemRouter}.
- *
- * @example
- * ```ts
- *const router = new FileSystemRouter({
- * dir: process.cwd() + "/pages",
- * style: "nextjs",
- *});
- *
- * const {params} = router.match("/blog/2020/01/01/hello-world");
- * console.log(params); // {year: "2020", month: "01", day: "01", slug: "hello-world"}
- * ```
- * @param options The options to use when creating the router
- * @param options.dir The root directory containing the files to route
- * @param options.style The style of router to use (only "nextjs" supported
- * for now)
- */
- constructor(options: {
- /**
- * The root directory containing the files to route
- *
- * There is no default value for this option.
- *
- * @example
- * ```ts
- * const router = new FileSystemRouter({
- * dir:
- */
- dir: string;
- style: "nextjs";
-
- /** The base path to use when routing */
- assetPrefix?: string;
- origin?: string;
- /** Limit the pages to those with particular file extensions. */
- fileExtensions?: string[];
- });
-
- // todo: URL
- match(input: string | Request | Response): MatchedRoute | null;
-
- readonly assetPrefix: string;
- readonly origin: string;
- readonly style: string;
- readonly routes: Record<string, string>;
-
- reload(): void;
- }
-
- export interface MatchedRoute {
- /**
- * A map of the parameters from the route
- *
- * @example
- * ```ts
- * const router = new FileSystemRouter({
- * dir: "/path/to/files",
- * style: "nextjs",
- * });
- * const {params} = router.match("/blog/2020/01/01/hello-world");
- * console.log(params.year); // "2020"
- * console.log(params.month); // "01"
- * console.log(params.day); // "01"
- * console.log(params.slug); // "hello-world"
- * ```
- */
- readonly params: Record<string, string>;
- readonly filePath: string;
- readonly pathname: string;
- readonly query: Record<string, string>;
- readonly name: string;
- readonly kind: "exact" | "catch-all" | "optional-catch-all" | "dynamic";
- readonly src: string;
- }
-
- interface SyncSubprocess {
- stdout?: Buffer;
- stderr?: Buffer;
+ interface SyncSubprocess<
+ Out extends SpawnOptions.Readable = SpawnOptions.Readable,
+ Err extends SpawnOptions.Readable = SpawnOptions.Readable,
+ > {
+ stdout: SpawnOptions.ReadableToSyncIO<Out>;
+ stderr: SpawnOptions.ReadableToSyncIO<Err>;
exitCode: number;
success: boolean;
}
@@ -3244,10 +3264,14 @@ declare module "bun" {
*
* To check if the command exists before running it, use `Bun.which(bin)`.
*
+ * @example
+ * ```ts
+ * const subprocess = Bun.spawn(["echo", "hello"]);
+ * ```
*/
cmd: string[]; // to support dynamically constructed commands
},
- ): Subprocess<OptionsToSubprocessIO<Opts>>;
+ ): SpawnOptions.OptionsToSubprocess<Opts>;
/**
* Spawn a new process
@@ -3263,22 +3287,21 @@ declare module "bun" {
function spawn<Opts extends SpawnOptions.OptionsObject>(
/**
* The command to run
+ *
+ * The first argument will be resolved to an absolute executable path. It must be a file, not a directory.
+ *
+ * If you explicitly set `PATH` in `env`, that `PATH` will be used to resolve the executable instead of the default `PATH`.
+ *
+ * To check if the command exists before running it, use `Bun.which(bin)`.
+ *
* @example
* ```ts
* const subprocess = Bun.spawn(["echo", "hello"]);
+ * ```
*/
cmds: string[],
options?: Opts,
- ): Subprocess<OptionsToSubprocessIO<Opts>>;
- type OptionsToSubprocessIO<Opts extends SpawnOptions.OptionsObject> = {
- stdin?: Opts["stdin"] extends number
- ? number
- : Opts["stdin"] extends "pipe"
- ? FileSink
- : ReadableStream;
- stdout?: Opts["stdout"] extends number ? number : ReadableStream;
- stderr?: Opts["stderr"] extends number ? number : ReadableStream;
- };
+ ): SpawnOptions.OptionsToSubprocess<Opts>;
/**
* Spawn a new process
@@ -3292,8 +3315,8 @@ declare module "bun" {
*
* Internally, this uses [posix_spawn(2)](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/posix_spawn.2.html)
*/
- function spawnSync(
- options: SpawnOptions.OptionsObject & {
+ function spawnSync<Opts extends SpawnOptions.OptionsObject>(
+ options: Opts & {
/**
* The command to run
*
@@ -3303,10 +3326,14 @@ declare module "bun" {
*
* To check if the command exists before running it, use `Bun.which(bin)`.
*
+ * @example
+ * ```ts
+ * const subprocess = Bun.spawnSync({ cmd: ["echo", "hello"] });
+ * ```
*/
- cmd: [string, ...string[]];
+ cmd: string[];
},
- ): SyncSubprocess;
+ ): SpawnOptions.OptionsToSyncSubprocess<Opts>;
/**
* Synchronously spawn a new process
@@ -3318,21 +3345,101 @@ declare module "bun" {
*
* Internally, this uses [posix_spawn(2)](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/posix_spawn.2.html)
*/
- function spawnSync(
+ function spawnSync<Opts extends SpawnOptions.OptionsObject>(
/**
* The command to run
+ *
+ * The first argument will be resolved to an absolute executable path. It must be a file, not a directory.
+ *
+ * If you explicitly set `PATH` in `env`, that `PATH` will be used to resolve the executable instead of the default `PATH`.
+ *
+ * To check if the command exists before running it, use `Bun.which(bin)`.
+ *
* @example
* ```ts
- * const subprocess = Bun.spawn(["echo", "hello"]);
+ * const subprocess = Bun.spawnSync(["echo", "hello"]);
+ * ```
*/
- cmds: [
- /** One command is required */
- string,
- /** Additional arguments */
- ...string[],
- ],
- options?: SpawnOptions.OptionsObject,
- ): SyncSubprocess;
+ cmds: string[],
+ options?: Opts,
+ ): SpawnOptions.OptionsToSyncSubprocess<Opts>;
+
+ export class FileSystemRouter {
+ /**
+ * Create a new {@link FileSystemRouter}.
+ *
+ * @example
+ * ```ts
+ *const router = new FileSystemRouter({
+ * dir: process.cwd() + "/pages",
+ * style: "nextjs",
+ *});
+ *
+ * const {params} = router.match("/blog/2020/01/01/hello-world");
+ * console.log(params); // {year: "2020", month: "01", day: "01", slug: "hello-world"}
+ * ```
+ * @param options The options to use when creating the router
+ * @param options.dir The root directory containing the files to route
+ * @param options.style The style of router to use (only "nextjs" supported
+ * for now)
+ */
+ constructor(options: {
+ /**
+ * The root directory containing the files to route
+ *
+ * There is no default value for this option.
+ *
+ * @example
+ * ```ts
+ * const router = new FileSystemRouter({
+ * dir:
+ */
+ dir: string;
+ style: "nextjs";
+
+ /** The base path to use when routing */
+ assetPrefix?: string;
+ origin?: string;
+ /** Limit the pages to those with particular file extensions. */
+ fileExtensions?: string[];
+ });
+
+ // todo: URL
+ match(input: string | Request | Response): MatchedRoute | null;
+
+ readonly assetPrefix: string;
+ readonly origin: string;
+ readonly style: string;
+ readonly routes: Record<string, string>;
+
+ reload(): void;
+ }
+
+ export interface MatchedRoute {
+ /**
+ * A map of the parameters from the route
+ *
+ * @example
+ * ```ts
+ * const router = new FileSystemRouter({
+ * dir: "/path/to/files",
+ * style: "nextjs",
+ * });
+ * const {params} = router.match("/blog/2020/01/01/hello-world");
+ * console.log(params.year); // "2020"
+ * console.log(params.month); // "01"
+ * console.log(params.day); // "01"
+ * console.log(params.slug); // "hello-world"
+ * ```
+ */
+ readonly params: Record<string, string>;
+ readonly filePath: string;
+ readonly pathname: string;
+ readonly query: Record<string, string>;
+ readonly name: string;
+ readonly kind: "exact" | "catch-all" | "optional-catch-all" | "dynamic";
+ readonly src: string;
+ }
/**
* The current version of Bun
diff --git a/packages/bun-types/tests/spawn.test-d.ts b/packages/bun-types/tests/spawn.test-d.ts
index cd776e43f..f5ef01277 100644
--- a/packages/bun-types/tests/spawn.test-d.ts
+++ b/packages/bun-types/tests/spawn.test-d.ts
@@ -1,3 +1,6 @@
+import { FileSink } from "bun";
+import * as tsd from "tsd";
+
Bun.spawn(["echo", "hello"]);
{
const proc = Bun.spawn(["echo", "hello"], {
@@ -9,6 +12,10 @@ Bun.spawn(["echo", "hello"]);
});
proc.pid; // process ID of subprocess
+
+ tsd.expectType<ReadableStream<Uint8Array>>(proc.stdout);
+ tsd.expectType<undefined>(proc.stderr);
+ tsd.expectType<undefined>(proc.stdin);
}
{
@@ -28,17 +35,17 @@ Bun.spawn(["echo", "hello"]);
});
// enqueue string data
- proc.stdin!.write("hello");
+ proc.stdin.write("hello");
// enqueue binary data
const enc = new TextEncoder();
- proc.stdin!.write(enc.encode(" world!"));
+ proc.stdin.write(enc.encode(" world!"));
// send buffered data
- proc.stdin!.flush();
+ proc.stdin.flush();
// close the input stream
- proc.stdin!.end();
+ proc.stdin.end();
}
{
@@ -66,5 +73,52 @@ Bun.spawn(["echo", "hello"]);
}
{
+ const proc = Bun.spawn(["echo", "hello"], {
+ stdio: ["pipe", "pipe", "pipe"],
+ });
+ tsd.expectType<FileSink>(proc.stdin);
+ tsd.expectType<ReadableStream<Uint8Array>>(proc.stdout);
+ tsd.expectType<ReadableStream<Uint8Array>>(proc.stderr);
+}
+{
+ const proc = Bun.spawn(["echo", "hello"], {
+ stdio: ["inherit", "inherit", "inherit"],
+ });
+ tsd.expectType<undefined>(proc.stdin);
+ tsd.expectType<undefined>(proc.stdout);
+ tsd.expectType<undefined>(proc.stderr);
+}
+{
+ const proc = Bun.spawn(["echo", "hello"], {
+ stdio: ["ignore", "ignore", "ignore"],
+ });
+ tsd.expectType<undefined>(proc.stdin);
+ tsd.expectType<undefined>(proc.stdout);
+ tsd.expectType<undefined>(proc.stderr);
+}
+{
+ const proc = Bun.spawn(["echo", "hello"], {
+ stdio: [null, null, null],
+ });
+ tsd.expectType<undefined>(proc.stdin);
+ tsd.expectType<undefined>(proc.stdout);
+ tsd.expectType<undefined>(proc.stderr);
+}
+{
+ const proc = Bun.spawn(["echo", "hello"], {
+ stdio: [new Request("1"), null, null],
+ });
+ tsd.expectType<number>(proc.stdin);
+}
+{
+ const proc = Bun.spawn(["echo", "hello"], {
+ stdio: [new Response("1"), null, null],
+ });
+ tsd.expectType<number>(proc.stdin);
+}
+{
+ const proc = Bun.spawn(["echo", "hello"], {
+ stdio: [new Uint8Array([]), null, null],
+ });
+ tsd.expectType<number>(proc.stdin);
}
-export {};