From 45e626d5d04b3c65096c332e0c5da342b0d2931b Mon Sep 17 00:00:00 2001 From: zhangfuxing Date: Wed, 30 Oct 2024 15:01:30 +0800 Subject: [PATCH] feat: allow to configure the writer to stderr (#30) --- README.md | 54 +++++++++++++++++++++++++--------------------- changelog.md | 3 +++ examples/stderr.ts | 23 ++++++++++++++++++++ mod.ts | 24 ++++++--------------- multi.ts | 21 ++++++------------ 5 files changed, 69 insertions(+), 56 deletions(-) create mode 100644 examples/stderr.ts diff --git a/README.md b/README.md index f181289..45b983c 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ ProgressBar in terminal for deno ![logo](screenshots/logo.png) +## Changelog + +[changelog](./changelog.md) + ## Usage ### Multiple progress bars @@ -11,21 +15,21 @@ ProgressBar in terminal for deno #### example ```ts -import { MultiProgressBar } from "https://deno.land/x/progress@v1.4.9/mod.ts"; -import { delay } from "https://deno.land/std@0.220.1/async/delay.ts"; - -// or JSR -// import { MultiProgressBar } from "jsr:@deno-library/progress"; -// import { delay } from "jsr:@std/async"; +import { MultiProgressBar } from "jsr:@deno-library/progress"; +import { delay } from "jsr:@std/async"; // or JSR (with version) -// import { MultiProgressBar } from "jsr:@deno-library/progress@1.4.9"; +// import { MultiProgressBar } from "jsr:@deno-library/progress@1.5.0"; // import { delay } from "jsr:@std/async@0.221.0"; // or JSR (no prefix, run `deno add @deno-library/progress` and `deno add @std/async`) // import { MultiProgressBar } from "@deno-library/progress"; // import { delay } from "@std/async"; +// or +// import { MultiProgressBar } from "https://deno.land/x/progress@v1.5.0/mod.ts"; +// import { delay } from "https://deno.land/std@0.220.1/async/delay.ts"; + const title = "download files"; const total = 100; @@ -74,6 +78,7 @@ interface constructorOptions { interval?: number; display?: string; prettyTime?: boolean; + output?: typeof Deno.stdout | typeof Deno.stderr; } interface renderOptions { @@ -109,6 +114,7 @@ class MultiProgressBar { * @param interval minimum time between updates in milliseconds, default: 16 * @param display What is displayed and display order, default: ':bar :text :percent :time :completed/:total' * @param prettyTime Whether to pretty print time and eta + * @param output Output stream, can be Deno.stdout or Deno.stderr, default is Deno.stdout */ constructor(options: ConstructorOptions); @@ -158,21 +164,21 @@ What is displayed and display order, default: ':bar :text :percent :time #### simple example ```ts -import ProgressBar from "https://deno.land/x/progress@v1.4.9/mod.ts"; -import { delay } from "https://deno.land/std@0.220.1/async/delay.ts"; - -// or JSR -// import ProgressBar from "jsr:@deno-library/progress"; -// import { delay } from "jsr:@std/async"; +import ProgressBar from "jsr:@deno-library/progress"; +import { delay } from "jsr:@std/async"; // or JSR (with version) -// import ProgressBar from "jsr:@deno-library/progress@1.4.9"; +// import ProgressBar from "jsr:@deno-library/progress@1.5.0"; // import { delay } from "jsr:@std/async@0.221.0"; // or JSR (no prefix, run `deno add @deno-library/progress` and `deno add @std/async`) // import ProgressBar from "@deno-library/progress"; // import { delay } from "@std/async"; +// or +// import ProgressBar from "https://deno.land/x/progress@v1.5.0/mod.ts"; +// import { delay } from "@std/async/delay"; + const title = "downloading:"; const total = 100; const progress = new ProgressBar({ @@ -193,21 +199,21 @@ await download(); #### complex example ```ts -import ProgressBar from "https://deno.land/x/progress@v1.4.9/mod.ts"; -import { delay } from "https://deno.land/std@0.220.1/async/delay.ts"; - -// or JSR -// import ProgressBar from "jsr:@deno-library/progress"; -// import { delay } from "jsr:@std/async"; +import ProgressBar from "jsr:@deno-library/progress"; +import { delay } from "jsr:@std/async"; // or JSR (with version) -// import ProgressBar from "jsr:@deno-library/progress@1.4.9"; +// import ProgressBar from "jsr:@deno-library/progress@1.5.0"; // import { delay } from "jsr:@std/async@0.221.0"; // or JSR (no prefix, run `deno add @deno-library/progress` and `deno add @std/async`) // import ProgressBar from "@deno-library/progress"; // import { delay } from "@std/async"; +// or +// import ProgressBar from "https://deno.land/x/progress@v1.5.0/mod.ts"; +// import { delay } from "@std/async/delay"; + const total = 100; const progress = new ProgressBar({ total, @@ -248,6 +254,7 @@ interface ConstructorOptions { interval?: number, display?: string prettyTime?: boolean; + output?: typeof Deno.stdout | typeof Deno.stderr; } interface renderOptions { @@ -286,6 +293,7 @@ class ProgressBar { * @param interval minimum time between updates in milliseconds, default: 16 * @param display What is displayed and display order, default: ':title :percent :bar :time :completed/:total' * @param prettyTime Whether to pretty print time and eta + * @param output Output stream, can be Deno.stdout or Deno.stderr, default is Deno.stdout */ constructor(options: ConstructorOptions): void; @@ -382,7 +390,3 @@ Log some messages next to the bar ![console](./screenshots/info.gif) More screenshots in the `screenshots` folder. - -## Changelog - -[changelog](./changelog.md) diff --git a/changelog.md b/changelog.md index 2371fba..591c173 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,8 @@ ## Changelog +### v1.5.0 - 2024.04.02 +[Allow to configure the writer to something other than stdout](https://github.com/deno-library/progress/issues/30) + ### v1.4.9 - 2024.04.02 [Both JSR and HTTPS are supported](https://github.com/deno-library/progress/issues/29) diff --git a/examples/stderr.ts b/examples/stderr.ts new file mode 100644 index 0000000..5792b48 --- /dev/null +++ b/examples/stderr.ts @@ -0,0 +1,23 @@ +import ProgressBar from "../mod.ts"; +import { delay } from "../deps.example.ts"; + +const total = 100; + +const progress = new ProgressBar({ + total, + // ==> here + output: Deno.stderr, + // <== here +}); + +let completed = 0; + +async function download() { + while (completed <= total) { + await progress.render(completed++); + + await delay(50); + } +} + +await download(); diff --git a/mod.ts b/mod.ts index 34b67cd..08e00ed 100644 --- a/mod.ts +++ b/mod.ts @@ -2,7 +2,7 @@ import { bgGreen, bgWhite, stripAnsiCode } from "./deps.ts"; import { prettyTime, type prettyTimeOptions } from "./time.ts"; export { MultiProgressBar } from "./multi.ts"; -const hasStdout = Deno.stdout; +const isTerminal = Deno.stdout.isTerminal; const enum Direction { left, @@ -21,6 +21,7 @@ interface constructorOptions { interval?: number; display?: string; prettyTime?: boolean; + output?: typeof Deno.stdout | typeof Deno.stderr; } interface renderOptions { @@ -54,16 +55,7 @@ export default class ProgressBar { private start = Date.now(); private lastRenderTime = 0; private encoder = new TextEncoder(); - private writer = Deno.stdout.writable.getWriter(); - - // Deno Version 1.39.1 no longer reports errors - // Note from @bjesuiter: This MUST be a Lamda function compared to a class member function, - // otherwise it will leak async ops in `deno test` - // Deno Version: 1.27.1 - // private signalListener = () => { - // this.end(); - // Deno.exit(); - // }; + private writer: WritableStreamDefaultWriter; /** * Title, total, complete, incomplete, can also be set or changed in the render method @@ -77,6 +69,7 @@ export default class ProgressBar { * - interval minimum time between updates in milliseconds, default: 16 * - display What is displayed and display order, default: ':title :percent :bar :time :completed/:total' * - prettyTime Whether to pretty print time and eta + * - output Output stream, can be Deno.stdout or Deno.stderr, default is Deno.stdout */ constructor({ title = "", @@ -89,6 +82,7 @@ export default class ProgressBar { interval = 16, display, prettyTime = false, + output = Deno.stdout, }: constructorOptions = {}) { this.title = title; this.total = total; @@ -101,7 +95,7 @@ export default class ProgressBar { this.display = display ?? ":title :percent :bar :time :completed/:total :text"; this.prettyTime = prettyTime; - // Deno.addSignalListener("SIGINT", this.signalListener); + this.writer = output.writable.getWriter(); } /** @@ -117,7 +111,7 @@ export default class ProgressBar { * - `prettyTimeOptions` prettyTime options */ async render(completed: number, options: renderOptions = {}): Promise { - if (this.#end || !hasStdout) return; + if (this.#end || !isTerminal) return; if (completed < 0) { throw new Error(`completed must greater than or equal to 0`); @@ -206,7 +200,6 @@ export default class ProgressBar { * No need to call in most cases, unless you want to end before 100% */ async end(): Promise { - // Deno.removeSignalListener("SIGINT", this.signalListener); if (this.#end) return; this.#end = true; if (this.clear) { @@ -252,13 +245,10 @@ export default class ProgressBar { switch (direction) { case Direction.all: return this.stdoutWrite("\x1b[2K"); - // break; case Direction.left: return this.stdoutWrite("\x1b[1K"); - // break; case Direction.right: return this.stdoutWrite("\x1b[0K"); - // break; } } diff --git a/multi.ts b/multi.ts index 402910e..e914d30 100644 --- a/multi.ts +++ b/multi.ts @@ -1,7 +1,7 @@ import { bgGreen, bgWhite, stripAnsiCode } from "./deps.ts"; import { prettyTime, type prettyTimeOptions } from "./time.ts"; -const hasStdout = Deno.stdout; +const isTerminal = Deno.stdout.isTerminal; interface constructorOptions { title?: string; @@ -12,6 +12,7 @@ interface constructorOptions { interval?: number; display?: string; prettyTime?: boolean; + output?: typeof Deno.stdout | typeof Deno.stderr; } interface renderOptions { @@ -49,16 +50,7 @@ export class MultiProgressBar { private start = Date.now(); private lastRenderTime = 0; private encoder = new TextEncoder(); - private writer = Deno.stdout.writable.getWriter(); - - // Deno Version 1.39.1 no longer reports errors - // Note from @bjesuiter: This MUST be a Lamda function compared to a class member function, - // otherwise it will leak async ops in `deno test` - // Deno Version: 1.27.1 - // private signalListener = () => { - // this.end(); - // Deno.exit(); - // }; + private writer: WritableStreamDefaultWriter; /** * Title, total, complete, incomplete, can also be set or changed in the render method @@ -71,6 +63,7 @@ export class MultiProgressBar { * - interval minimum time between updates in milliseconds, default: 16 * - display What is displayed and display order, default: ':bar :text :percent :time :completed/:total' * - prettyTime Whether to pretty print time and eta + * - output Output stream, can be Deno.stdout or Deno.stderr, default is Deno.stdout */ constructor({ title = "", @@ -81,6 +74,7 @@ export class MultiProgressBar { interval, display, prettyTime = false, + output = Deno.stdout, }: constructorOptions = {}) { if (title != "") { this.#bars.push({ str: title }); @@ -93,7 +87,7 @@ export class MultiProgressBar { this.interval = interval ?? 16; this.display = display ?? ":bar :text :percent :time :completed/:total"; this.prettyTime = prettyTime; - // Deno.addSignalListener("SIGINT", this.signalListener); + this.writer = output.writable.getWriter(); } /** @@ -108,7 +102,7 @@ export class MultiProgressBar { * - `prettyTimeOptions` - prettyTime options */ async render(bars: Array): Promise { - if (this.#end || !hasStdout) return; + if (this.#end || !isTerminal) return; const now = Date.now(); const ms = now - this.lastRenderTime; @@ -197,7 +191,6 @@ export class MultiProgressBar { * No need to call in most cases, unless you want to end before 100% */ async end(): Promise { - // Deno.removeSignalListener("SIGINT", this.signalListener); if (this.#end) return; this.#end = true; if (this.clear) {