diff options
author | 2023-04-05 22:15:06 -0400 | |
---|---|---|
committer | 2023-04-05 19:15:06 -0700 | |
commit | 569d4940bb68cb47fa546b5505f5a326073d6c5c (patch) | |
tree | 03089fd034a0420619bd0d044c412559d780bbb4 | |
parent | 4af78c7d5a88378fb5912d160a936f79a30f46be (diff) | |
download | bun-569d4940bb68cb47fa546b5505f5a326073d6c5c.tar.gz bun-569d4940bb68cb47fa546b5505f5a326073d6c5c.tar.zst bun-569d4940bb68cb47fa546b5505f5a326073d6c5c.zip |
rebase (#1501)
-rw-r--r-- | packages/bun-types/bun.d.ts | 385 | ||||
-rw-r--r-- | packages/bun-types/tests/spawn.test-d.ts | 64 |
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 {}; |