npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

murasame

v0.1.1

Published

Implementation of a command line interfaces decorators.

Readme

murasame

Murasame 是一个基于 Bun 开发的 CLI 装饰器(Decorator)实现,你可以轻松通过 ECMAScript 最新装饰器语法,来创建自己的命令行工具。

使用其他语言阅读:English | 简体中文

介绍

我们都知道,在传统开发中,如果想使用 JS 开发一个命令行工具,目前 npm 上所有的依赖包都是通过链式调用实现的:

const cli = require('other package');

function reverseString(text) {
  return text.split('').reverse().join('');
}

cli
  .name('my-tools')
  .version('1.1.4')
  .description('Some of my command line tools.');

cli
  .command('echo')
  .description('Output a text to the terminal.')
  .argument('<text>')
  .option('-r, --reverse', 'Reverse of the string.', false)
  .action(({ args, options }) => {
    const { text } = args;
    const { reverse } = options;

    console.log(reverse ? reverseString(text) : text);
  });

cli.parse();

在早期,JS 并没有装饰器的概念,TS 中的装饰器也是通过 reflect-metadata 这个依赖库实现的,但它并不是 ECMAScript 的官方规范。现在,我们可以直接通过 Bun 来运行原生装饰器:

import { Program, Command, Option, execute } from 'murasame';

@Command({
  name: 'echo',
  args: '<text>',
  description: 'Output a text to the terminal.',
})
class EchoCommand {
  @Option({ short: 'r', description: 'Reverse of the string.' })
  static reverse: boolean = false;

  constructor(text: string) {
    console.log(EchoCommand.reverse ? this.reverseString(text) : text);
  }
  reverseString(text: string) {
    return text.split('').reverse().join('');
  }
}

@Program({
  name: 'my-tools',
  version: '1.1.4',
  description: 'Some of my command line tools.',
  commands: [EchoCommand],
})
class Tools {}

execute(Tools);

这很酷,不是么?代码的可读性更高,也更利于模块化管理。

安装

由于当前装饰器处于 stage 3 状态,所以该项目目前是一个纯 TypeScript 包,你可以使用 Bun 来直接运行。

什么是 Bun?

Bun 是一个 JavaScript runtime,与 Node.js、Deno 一样,但它可以原生运行 TypeScript 代码,并在 v1.3.10 添加了对新版装饰器的支持。

你可以在终端执行对应的脚本进行安装:

# macOS & Linux
curl -fsSL https://bun.com/install | bash
# Windows
powershell -c "irm bun.sh/install.ps1|iex"

除此之外,也还能访问 Bun 官方文档 查看更多安装方式。

依赖

在做好准备工作后,我们便可以通过 bun add 来添加依赖项:

bun add murasame

使用

以下是一个完整的单文件示例,展示了如何使用 Murasame 快速构建一个具有参数与选项的命令行工具:

import { Program, Command, Option, execute } from 'murasame';

@Command({
  name: 'echo',
  args: '<text>',
  description: 'Output a text to the terminal.',
})
class EchoCommand {
  @Option({ short: 'r', description: 'Reverse of the string.' })
  static reverse: boolean = false;

  constructor(message: string) {
    console.log(EchoCommand.reverse ? this.reverseString(message) : message);
  }
  reverseString(text: string) {
    return text.split('').reverse().join('');
  }
}

@Program({
  name: 'my-tools',
  version: '1.1.4',
  description: 'Some of my command line tools.',
  commands: [EchoCommand],
})
class Tools {}

execute(Tools);

如果你在 tsconfig.jsoncompilerOptions 中,手动配置lib 选项,那么在使用 TypeScript 原生装饰器时,你需要追加配置 "ESNext.Decorators" 来获取最新的装饰器语法支持:

{
  "compilerOptions": {
    "lib": ["ESNext", "ESNext.Decorators"]
  }
}

保存 cli.ts 代码后,即可通过 Bun 直接运行。Murasame 会在应用启动时自动收集定义的装饰器元数据(Metadata),并最终输出 CLI 面板:

$ bun cli.ts
Some of my command line tools. (1.1.4)

Usage: my-tools <command> [...flags] [...args]

Commands:
  echo      <text>               Output a text to the terminal.

  <command> --help               Print help text for command.

如果不知道命令支持哪些选项,也可以追加 -h 获得该命令的说明文档:

$ bun cli.ts echo -h
Usage: my-tools echo [flags] <text>

  Output a text to the terminal.

Flags:
  -h, --help             Print help text for command.
  -r, --reverse          Reverse of the string.

输入对应的命令,即可运行程序,参数将被按顺序注入到构造函数中:

$ bun cli.ts echo ciallo
ciallo

追加选项短名 -r 输出翻转后的字符串:

$ bun cli.ts echo ciallo -r
ollaic

API 与说明

Murasame 深度复刻了 Bun 原生 CLI 的默认行为,旨在为使用 Bun 的开发者带来一致的感观体验。

程序入口:@Program(options?: ProgramOptions)

@Program 是应用程序的主入口装饰器。它用于定义 CLI 的全局配置,并作为所有命令的挂载点:

@Program({
  name: 'my-tools',
  version: '1.1.4',
  description: 'Some of my command line tools.',
  commands: [CdCommand, EchoCommand, LsCommand, PingCommand, RmCommand],
  categories: {},
  details: {
    Blog: 'https://blog.yuki.sh',
    GitHub: 'https://github.com/xueelf',
  },
})
class Tools {}
  • name:程序名称。
  • version:当前版本号,可以使用 --version-v 查询。
  • description:程序的说明文本,会显示在最顶部。
  • commands:配置可用的命令。以数组形式传入(使用 @Command 装饰器修饰的类)。
  • categories (可选): 传入对应的类别名与颜色样式,为命令进行分类排列。
  • details (可选): 在 CLI 面板的最底部添加额外信息。通常被用于放置相关的扩展链接、版权声明或作者信息等。

version

CLI 会默认创建 --version 与 -v 选项。

categories

若你的命令随项目的迭代逐渐增多,可传递此对象将命令在终端中进一步分类展示。

如果不配置 categories,所有命令会被堆叠在 Commands: 标签下按照 commands 的传入顺序排列展示。例如上方的示例代码,未指定分类时在终端的布局排版是这样的:

Some of my command line tools. (1.1.4)

Usage: my-tools <command> [...flags] [...args]

Commands:
  cd        <path>               Change the current working directory.
  echo      <text>               Output a text to the terminal.
  ls
  ping      <host>               Check the network connectivity to a host.
  rm        <path>               Remove a file or directory.

  <command> --help               Print help text for command.

Blog                             https://blog.yuki.sh
GitHub                           https://github.com/xueelf

如果像上方示例代码那样配置,并在对应的命令装饰器(@Command)中指定 category 属性,CLI 排版会发生如下变化:

Some of my command line tools. (1.1.4)

Usage: my-tools <command> [...flags] [...args]

Commands:
  cd        <path>               Change the current working directory.
  ls
  rm        <path>               Remove a file or directory.

  ping      <host>               Check the network connectivity to a host.

  echo      <text>               Output a text to the terminal.

  <command> --help               Print help text for command.

Blog                             https://blog.yuki.sh
GitHub                           https://github.com/xueelf

不同分类之间会有换行符进行分隔,并按照字母顺序进行排列。

默认情况下,Commands: 标签内的命令名称会输出黑色加粗样式文本,我们可以通过 categories 自定义配置文本样式:

@Program({
  categories: {
    file: 'blue',
    network: ['magenta', 'underline'],
    system: '#114514',
  },
})
class Tools {}

定义命令:@Command(options: CommandOptions | string)

@Command 用于声明单个可执行的命令。当该命令被调用时,Murasame 会将终端中输入的参数(Arguments)进行解析,并按顺序向下注入至该类的实例对象中:

@Command({
  name: 'echo',
  aliases: ['print'],
  args: '<text>',
  description: 'Output a text to the terminal.',
  examples: [
    {
      description: 'Print a text',
      syntax: 'my-tools echo ciallo',
    },
    {
      description: 'Print a text in reverse',
      syntax: 'my-tools echo ciallo -r',
    },
  ],
})
class EchoCommand {
  @Option({ short: 'r', description: 'Reverse of the string.' })
  static reverse: boolean = false;

  constructor(message: string) {
    console.log(EchoCommand.reverse ? this.reverseString(message) : message);
  }
  reverseString(text: string) {
    return text.split('').reverse().join('');
  }
}
  • name:命令名称。
  • args:参数信息。
  • aliases:调用别名。
  • category:命令分类,对应 @Programcategories 键名。
  • description:命令的说明文本,会自动对齐并显示在命令右侧。
  • examples:为当前命令添加使用示例。接收一个对象数组,包含 syntax(具体语法)与 description(说明文本)。
  • epilog:在当前命令帮助面板的最底部追加额外的补充说明或提示文本,接受一个字符串数组,每个元素独占一行。

args

args 用于定义并在终端帮助提示中展示当前命令所需的位置参数(如 <text>[options] 等)。当命令被执行时,底层包引擎会将读取到的连续位置参数(Arguments),按照原始的传入顺序依次注入到该命令类的 constructor 构造函数中。

aliases

命令别名在 CLI 中很常见,例如我们经常使用的 npm install 就可以简写为 npm i,那么在这里 i 就是 install 的别名,你还可以指定多个:

@Command({
  name: 'install',
  aliases: ['i', 'add'],
})
class InstallCommand {}

这样不管是 my-tools installmy-tools i 还是 my-tools add,都可以调用该命令。

定义选项:@Option(options?: OptionOptions)

选项装饰器必须作用于类的静态属性(static。装饰器会根据你的初始 JavaScript 类型(stringboolean)来自动决定这个选项该如何被控制:

@Command('echo')
class EchoCommand {
  @Option({ short: 'r', description: 'Reverse of the string.' })
  static reverse: boolean = false;

  constructor() {
    console.log(EchoCommand.reverse);
  }
}

例如,布尔值将被当作简单的开关 flag 处理(有输入则视为 true):

$ my-tools echo
$ my-tools echo --reverse
$ my-tools echo -r
false
true
true
@Command('echo')
class EchoCommand {
  @Option({ short: 'o', description: '' })
  static outfile: string = 'out.txt';

  constructor() {
    console.log(EchoCommand.outfile);
  }
}

字符串类型则意味着必须且安全地接收一个值,你可以在调用时使用空格传入,或者使用等号相连

$ my-tools echo --outfile index.txt
$ my-tools echo --outfile=index.txt
$ my-tools echo -o index.txt
$ my-tools echo -oindex.txt
index.txt
index.txt
index.txt
index.txt

在 CLI 的业界规范(如 POSIX Utility Syntax Guidelines 和 GNU 命令行标准)中,命令携带参数值是非常标准化且极其常见的做法。关于传参格式,在标准解析器(包括 Bun 内置的解析与 Node util.parseArgs)中的业界共识如下:

  • 长参数
    • --outfile out.txt(空格):POSIX 标准,最广泛的参数传值方式。
    • --outfile=out.txt(等号)
  • 短参数
    • -o out(空格):POSIX 标准,最广泛的短参数传值方式。
    • -oout(紧凑):POSIX 标准,缩写连写,例如 mysql -uroot -ppassword。
    • -o=out(等号,非规范)

等号 = 在规范中通常被设计为专门给长参数(--outfile=out)使用的,短参数使用等号在严格 POSIX 中是不规范的。但现代 CLI 解析工具(包括 parseArgs)为了对用户保持最大程度的宽容,通常也会将 -o=out 正确解析。

执行与运行:execute(ProgramClass)

当所有的命令与配置都定义好后,只需要将使用了 @Program 装饰器的主体类交由 execute 处理即可:

import { execute } from 'murasame';

@Program({
  name: 'my-tools',
  version: '1.1.4',
  description: 'Some of my command line tools.',
  commands: [EchoCommand],
})
class Tools {}

execute(Tools);

自定义外观与交互

除了基础的 CLI 装饰器外,Murasame 底层还封装了一套轻量的 C-FFI 层,这使得我们可以轻松调用内置的高性能终端交互 API。除此之外,还提供了文本美化的工具。

文本着色:colorize(color, text)

如果你想为终端输出的文本加上醒目的颜色或装饰(如粗体、下划线),可以使用内置的 colorize 函数,这能直接将文本处理为 ANSI 控制码序列:

import { colorize } from 'murasame';

// 单一颜色
console.log(colorize('cyan', 'Hello, Murasame!'));

// 颜色组合数组:[前景色, 样式]
console.log(colorize(['#57b497', 'bold'], 'Ciallo~(∠·ω< )⌒★'));

文本输入:input(message, options?)

阻塞当前主进程,等待用户在终端窗口输入一段普通的文本:

import { input } from 'murasame/prompts';

const answer = input('What is your name', { default: 'Yuki' });
console.log(`Hello, ${answer}!`);

多选列表:select(message, choices)

基于终端渲染出一个可使用键盘交互(上下选择与回车确认)的单选列表:

import { select } from 'murasame/prompts';

const framework = select('Choose your favorite framework', [
  { label: 'Vue', value: 'vue' },
  { label: 'React', value: 'react' },
  { label: 'Svelte', value: 'svelte' },
]);

console.log(`Your choice: ${framework?.value}`);

除了使用键盘方向键控制上下选择,你还可以使用 jk 操作。同时,你可以连按两下 ESC 或者直接使用 Ctrl + c 来取消。

细心的你可能已经发现了,不论是 input 还是 select,在终端的排版样式以及逻辑交互上,也都是与 Bun 的 CLI 完全一致的。

FAQ

名字的由来

murasame 是日语「ムラサメ」的罗马音, 汉字通常写作「村雨」,在英语中被翻译为「秋雨」,指的是一种雨,先猛烈,然后轻柔,时断时续。在日本的传统诗歌中,她经常与秋天的冷雨联系在一起。

同时,她也是游戏《千恋*万花》中的角色名,中文译为「丛雨」。她为主人排忧解难,伴其左右处理种种事务。我喜欢下雨,也喜欢丛雨,所以我选择了将其作为这个项目的名字。

感谢

包名「murasame」最初已经被使用了,但很长时间都没有较为活跃的维护。在与作者取得联系后,Kamata 将包权限转交给了我,在这里再次表示感谢!