diff --git a/cli/unstable_spinner.ts b/cli/unstable_spinner.ts index 178a5b14965c..1c9979d395fe 100644 --- a/cli/unstable_spinner.ts +++ b/cli/unstable_spinner.ts @@ -75,6 +75,12 @@ export interface SpinnerOptions { * This can be changed while the spinner is active. */ color?: Color; + /** + * The stream to write the spinner to. + * + * @default {Deno.stdout} + */ + stream?: typeof Deno.stderr | typeof Deno.stdout; } /** @@ -126,6 +132,7 @@ export class Spinner { #color: Color | undefined; #intervalId: number | undefined; #active = false; + #stream: typeof Deno.stdout | typeof Deno.stderr; /** * Creates a new spinner. @@ -142,6 +149,7 @@ export class Spinner { this.#spinner = spinner; this.message = message; this.#interval = interval; + this.#stream = options?.stream ?? Deno.stdout; this.color = color; } @@ -199,7 +207,7 @@ export class Spinner { * ``` */ start() { - if (this.#active || Deno.stdout.writable.locked) { + if (this.#active || this.#stream.writable.locked) { return; } @@ -219,7 +227,7 @@ export class Spinner { const writeData = new Uint8Array(LINE_CLEAR.length + frame.length); writeData.set(LINE_CLEAR); writeData.set(frame, LINE_CLEAR.length); - Deno.stdout.writeSync(writeData); + this.#stream.writeSync(writeData); i = (i + 1) % this.#spinner.length; }; @@ -245,7 +253,7 @@ export class Spinner { stop() { if (this.#intervalId && this.#active) { clearInterval(this.#intervalId); - Deno.stdout.writeSync(LINE_CLEAR); // Clear the current line + this.#stream.writeSync(LINE_CLEAR); // Clear the current line this.#active = false; } } diff --git a/cli/unstable_spinner_test.ts b/cli/unstable_spinner_test.ts index a1833c2cbef7..4ae1bd15bf9f 100644 --- a/cli/unstable_spinner_test.ts +++ b/cli/unstable_spinner_test.ts @@ -166,6 +166,39 @@ Deno.test("Spinner constructor accepts interval", async () => { } }); +Deno.test("Spinner constructor accepts stream", async () => { + try { + stub(Deno.stdin, "setRaw"); + + const expectedOutput = [ + "\r\x1b[K⠋\x1b[0m ", + "\r\x1b[K⠙\x1b[0m ", + "\r\x1b[K⠹\x1b[0m ", + "\r\x1b[K", + ]; + + const actualOutput: string[] = []; + + stub( + Deno.stderr, + "writeSync", + (data: Uint8Array) => { + const output = decoder.decode(data); + actualOutput.push(output); + return data.length; + }, + ); + + const spinner = new Spinner({ interval: 300, stream: Deno.stderr }); + spinner.start(); + await delay(1000); // 100ms buffer + spinner.stop(); + assertEquals(actualOutput, expectedOutput); + } finally { + restore(); + } +}); + Deno.test("Spinner constructor accepts each color", async (t) => { await t.step("black", async () => { try {