wukong-progress

npm version downloads license GitHub stars issues

🎨 Node.js / ESM 风格的 CLI 进度条库,支持:


中文 | English


🚀 安装

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

⚡️ 基本用法

单条进度条

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()

多条进度条

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 方法可以在更新进度的同时附加描述性文字。

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, '正在提取 Git 提交记录...')
    build.step(5, '正在解析提交记录...')
    build.step(5, '正在生成 Changelog...')
    build.step(5, '正在生成 Release 信息...')
  }
  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(非 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()

🎨 彩色渲染(可选)

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

📂 Examples 文件夹

node examples/index.mjs

⚡️ 测试

# Node.js 原生测试
yarn test:node

# Vitest snapshot 测试
yarn test:vitest

# 全部测试
yarn test

💻 适用场景