wukong-progress

npm version downloads license GitHub stars issues

🎨 A Node.js / ESM style CLI progress bar library that supports:


English | įŽ€äŊ“中文


🚀 Installation

yarn add wukong-progress chalk
# or
npm install wukong-progress chalk

âšĄī¸ Basic Usage

Single Progress Bar

import chalk from "chalk";
import { createMultiBar } from "wukong-progress";

const mb = createMultiBar();
const bar = mb.create(100, {
  prefix: chalk.cyan("Build"),
  format: "Build [:bar] :percent :current/:total",
});

async function run() {
  for (let i = 0; i <= 100; i++) {
    await new Promise((r) => setTimeout(r, 20));
    bar.tick();
  }
  mb.stop();
  console.log(chalk.green("\nDone!\n"));
}

run();

Multiple Progress Bars

import chalk from "chalk";
import { createMultiBar } from "wukong-progress";

const mb = createMultiBar();
const build = mb.create(100, {
  prefix: chalk.blue("Build"),
  format: "Build [:bar] :percent",
});
const test = mb.create(50, {
  prefix: chalk.magenta("Test"),
  format: "Test  [:bar] :percent",
});

async function run() {
  for (let i = 0; i <= 100; i++) {
    await new Promise((r) => setTimeout(r, 15));
    if (i <= 50) test.tick();
    build.tick();
  }
  mb.stop();
  console.log(chalk.green("\nAll tasks done!\n"));
}

run();

Step with Payload

Update progress with a descriptive payload message.

import chalk from 'chalk'
import { createMultiBar } from '../src/index.mjs'

const mb = createMultiBar()

const build = mb.create(100, {
  prefix: chalk.blue('Build'),
  format: 'Build [:bar] :percent :payload'
})
const test = mb.create(50, {
  prefix: chalk.magenta('Test'),
  format: 'Test  [:bar] :percent'
})

async function run() {
  for (let i = 0; i <= 100; i++) {
    await new Promise((r) => setTimeout(r, 15))
    if (i <= 50) test.tick()
    build.step(5, 'Extracting Git history...')
    build.step(5, 'Parsing commits...')
    build.step(5, 'Generating Changelog...')
    build.step(5, 'Generating Release Info...')
  }
  mb.stop()
  console.log(chalk.green('\nAll tasks done!\n'))
}

run()

Group / Stage / prefix

import chalk from "chalk";
import { createMultiBar } from "wukong-progress";

const mb = createMultiBar();
const buildGroup = mb.group("Build Group");
buildGroup.create(50, {
  prefix: chalk.blue("Compile"),
  format: "Compile [:bar] :percent",
});
buildGroup.create(30, {
  prefix: chalk.cyan("Bundle"),
  format: "Bundle  [:bar] :percent",
});

const testGroup = mb.group("Test Group");
testGroup.create(20, {
  prefix: chalk.magenta("Unit"),
  format: "Unit [:bar] :percent",
});
testGroup.create(10, {
  prefix: chalk.yellow("E2E"),
  format: "E2E  [:bar] :percent",
});

async function run() {
  const allTasks = [...buildGroup.bars, ...testGroup.bars];

  for (let i = 0; i < 50; i++) {
    await new Promise((r) => setTimeout(r, 20));
    allTasks.forEach((bar) => {
      if (!bar.state.complete) bar.tick();
    });
  }

  mb.stop();
  console.log(chalk.green("\nGroups completed!\n"));
}

run();

JSON Fallback (non-TTY / CI)

import { Writable } from "node:stream";
import { createMultiBar } from "wukong-progress";

let out = "";
const stream = new Writable({
  write(chunk, _, cb) {
    out += chunk;
    cb();
  },
});

const mb = createMultiBar({ stream, json: true });
const bar = mb.create(5, { prefix: "JSON" });

async function run() {
  for (let i = 0; i <= 5; i++) {
    await new Promise((r) => setTimeout(r, 20));
    bar.tick();
  }
  mb.stop();

  console.log("JSON fallback output:");
  console.log(out);
}

run();

🎨 Colored Output (optional)

prefix: chalk.green('Build'),
format: chalk.green('Build [:bar] :percent')

📂 Examples Folder

node examples/index.mjs

âšĄī¸ Testing

# Node.js native test
yarn test:node

# Vitest snapshot test
yarn test:vitest

# Run all tests
yarn test

đŸ’ģ Use Cases