目錄
- 項目簡介
- 安裝
- 基本用法
- 樣例
- 創建一個簡單的命令行工具
- 使用`archons`上下文創建進度條
- 最后
項目地址: https://github.com/noctisynth/archons
Bug反饋或功能請求:https://github.com/noctisynth/archons/issues
項目簡介
Archons意思是“執政官”,我使用Rust作為Nodejs的Native層并將方法通過napi-rs
導出到Nodejs,這使得我們可以借助Rust來幫助Nodejs使用者創建高效、強大和優雅的終端命令行工具。
創建這個項目首先是由于citty
項目的啟發,但是citty
很多更高級的功能并不完善,個人感覺社區活躍度也不高,因為我的PR至今沒人處理。于是我便想著另起爐灶,但是既然都重寫了,為什么不RIIR(Rewrite it in Rust)呢?Rust的命令行工具已經有了一套完整的生態,我們完全可以讓Nodejs工具在Rust的肩膀上新生。
于是Archons應運而生。
從本質上來講,我只是對Rust很多生態例如clap
、indicatif
等庫的套殼,但是這的確能夠讓我們的Nodejs命令行工具更加優雅和強大。我采用類似citty
的函數式的聲明方式,這能夠更加清晰的進行開發。
安裝
我們將archons
作為開發依賴安裝:
-
使用
npm
安裝:npm install --save-dev archons
-
使用
pnpm
安裝:pnpm add -D archons
-
使用
yarn
安裝:yarn add -D archons
-
使用
bun
安裝:bun add -d archons
基本用法
-
defineCommand
方法構建一個基本的命令(或子命令)。
-
run
方法將一個命令作為主命令并運行。
樣例
所有樣例:https://github.com/noctisynth/archons/tree/main/examples
命令行選項所有可用參數:https://github.com/noctisynth/archons/blob/main/index.d.ts#L66-L210
創建一個簡單的命令行工具
Archons 中參數的行為將盡量與 clap 保持一致,部分參數由于Rust與Nodejs的差異,在代碼文檔中均有標注。
import { defineCommand, run, type Context } from 'archons';// 子命令
const dev = defineCommand({meta: {name: 'dev',about: 'Run development server',},options: {port: {type: 'option',parser: 'number',default: '3000',},},callback: (ctx: Context) => {console.log(ctx); // 這里輸出完整的上下文console.log(ctx.args.port) // 這里port的值,缺省值為3000}
})const main = defineCommand({meta: {name: 'simple',version: '0.0.1',about: 'A simple command line tool',styled: true, // 啟用色彩},options: {name: {type: 'positional', // 位置參數required: true, // 必須傳入help: 'Name of the person to greet',},verbose: {type: 'option',parser: 'boolean',action: 'store',help: 'Enable verbose output',global: true // 全局參數,會被子命令繼承},},// 子命令subcommands: {dev,},callback: (ctx: Context) => {console.log(ctx);}
})run(main) // 執行主命令
使用archons
上下文創建進度條
Archons 中進度條的創建行為與 indicatif 一致。
import { type Context, defineCommand, run } from 'archons'const spinner = defineCommand({meta: {name: 'spinner',},options: {'enable-steady-tick': {type: 'option',action: 'store',},},callback: async (ctx: Context) => {const spinner = ctx.createSpinner()spinner.setMessage('loading')spinner.tick()let i = 100const interval = ctx.args.interval as numberif (ctx.args['enable-steady-tick']) {spinner.println('Enabled steady tick')spinner.enableSteadyTick(interval)while (i--) {if (i < 30) {spinner.setMessage('Disabled steady tick for now')spinner.disableSteadyTick()}await new Promise((resolve) => setTimeout(resolve, interval))}} else {spinner.println('Disabled steady tick')while (i--) {spinner.tick()await new Promise((resolve) => setTimeout(resolve, interval))}}spinner.finishWithMessage('? finished')},
})const bar = defineCommand({meta: {name: 'bar',},options: {clear: {type: 'option',action: 'store',help: 'Clear the progress bar',parser: 'boolean',},},callback: async (ctx: Context) => {const bar = ctx.createProgressBar(ctx.args.total as number)bar.setTemplate('{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {pos:>5}/{len:5} {msg}')bar.setProgressChars('=>-')let i = 100const interval = ctx.args.interval as numberwhile (i--) {bar.inc(1)await new Promise((resolve) => setTimeout(resolve, interval))}if (ctx.args.clear) {bar.finishAndClear()} else {bar.finish()}console.log('? finished')},
})const main = defineCommand({meta: {name: 'progressbar',styled: true,subcommandRequired: true,},options: {interval: {type: 'option',numArgs: '1',default: '100',global: true,help: 'Interval of spinner',parser: 'number',},total: {type: 'option',numArgs: '1',default: '100',global: true,help: 'Total of progress bar',parser: 'number',},},subcommands: {spinner,bar,},
})run(main)
最后
如果你認為這個項目有所幫助,歡迎在GitHub給我一個Star哦!