@benev/argv

中文文档
0.3.14发布于 2026/02/21MITTypeScriptES Module零依赖构建来源已验证近 30 天下载 2744

command line argument parser

原始使用文档
文档目录14 节
  1. 💁 autogenerated --help pages
  2. 📖 build your cli
  3. 🧑‍🔧 configuring your cli's args and params
  4. validation for args and params
  5. help literally everywhere!
  6. list helper
  7. choice helper
  8. multipleChoice helper
  9. 🌳 tree of multiple commands
  10. tree strategy
  11. command-execution strategy
  12. 🛠️ custom types
  13. 🦚 custom themes
  14. 🌠 give me a github star!

🎛️ @benev/argv

the greatest command line parser for typescript, maybe

🤖 for making node cli programs
🕵️ incredible typescript type inference
🧼 zero dependencies
💖 made free and open source, just for you


💁 autogenerated --help pages

pizza --help

pizza large --pepperoni --slices 3

# or, any way you like it
pizza --slices=9 large
pizza medium -p --slices=5
pizza small --pepperoni="no" --slices="2"

📖 build your cli

  1. install @benev/argv via npm
    npm i @benev/argv
    
  2. import stuff
    import {cli, command, arg, param, string, number} from "@benev/argv"
    
  3. specify your cli, and perform the parsing
    const {args, params} = cli(process.argv, {
      name: "pizza",
      commands: command({
        args: [
          arg("size").required(string),
        ],
        params: {
          slices: param.default(number, "1"),
          pepperoni: param.flag("p"),
        },
      }),
    }).tree
    
  4. now you have your args and params
    args.size // "large"
    params.slices // 5
    params.pepperoni // true
    
    • this is the "flat strategy" for receiving your args and params.
      simple, easy!
  5. all your types automagically work!
    it took me a long time to make it elegant and cool like this.

🧑‍🔧 configuring your cli's args and params

  • let's start by making a command
    command({
      args: [],
      params: {},
    })
    
  • a command can optionally accept a help string
    command({
      help: "what a time to be alive!",
      args: [],
      params: {},
    })
    
  • let's add positional args
    command({
      args: [
        arg("active").required(boolean),
        arg("count").default(number, "101"),
        arg("name").optional(string),
      ],
      params: {},
    })
    
    • args are in an array, so each needs a name, eg "active" above
    • there are three modes, required, default, and optional
    • default requires a fallback value
    • there are three basic types, string, number, and boolean, but you can make your own types
  • now let's talk about params
    command({
      args: [],
      params: {
        active: param.required(boolean),
        count: param.default(number, "101"),
        name: param.optional(string),
        verbose: param.flag("-v"),
      },
    })
    
    • pretty similar. but see the way the names are different?
    • there's a new variety of param called flag, of course, it's automatically a boolean (how could it be otherwise?)

validation for args and params

  • you can set a validate function on any arg or param
    arg("quality").optional(number, {
      validate: n => {
        if (n > 100) throw new Error("to big")
        if (n < 0) throw new Error("to smol")
        return n
      },
    })
    
    • if you throw any error in a validate, it will be printed all nice-like to the user

help literally everywhere!

  • in fact, every arg and param can have its own help
    command({
      help: "it's the best command, nobody makes commands like me",
    
      args: [
        arg("active").required(boolean, {
          help: "all systems go?",
        }),
    
        arg("count").default(number, "101", {
          help: "number of dalmatians",
        }),
    
        arg("name").optional(string, {
          help: `
            see this multi-line string?
            it will be trimmed all nicely on the help page.
          `
        }),
      ],
    
      params: {
        active: param.required(boolean, {
          help: "toggle this carefully!",
        }),
    
        count: param.default(number, "101", {
          help: "classroom i'm late for",
        }),
    
        name: param.optional(string, {
          help: "pick your pseudonym",
        }),
    
        verbose: param.flag("-v", {
          help: "going loud",
        }),
      },
    })
    

list helper

  • okay this is seriously crazy cool, check this out
    param.required(list(string))
    
  • you can just wrap any type in the list helper
    • user inputs comma-separated values mp3,wav,ogg
    • you get an array ["mp3", "wav", "ogg"]
  • is works with any type, like numbers and such
    param.required(list(number))
    
    • now you get a number[] array (not strings)
    • yes, list preserves the type's validation

choice helper

  • user can choose one of the permitted values
    param.required(string, choice(["thick", "thin"]))
    
  • you can add a help to it as well
    param.required(string, choice(["thick", "thin"], {
      help: "made with organic whole-wheat flour",
    }))
    

multipleChoice helper

  • allows the user to choose multiple permitted choices
    param.required(string, multipleChoice(["bacon", "lettuce", "tomatoes"]))
    
  • you can add help, and decide if the user is allowed to make zero choices
    param.required(string, choice(["bacon", "lettuce", "tomatoes"], {
      help: "all available ingredients are gmo'd",
      zeroAllowed: true, // defaults to false
    }))
    

🌳 tree of multiple commands

  • the commands object is a recursive tree with command leaves
    const {tree} = cli(process.argv, {
      name: "converter",
      commands: {
        image: command({
          args: [],
          params: {
            quality: param.required(number),
          },
        }),
        media: {
          audio: command({
            args: [],
            params: {
              mono: param.required(boolean),
            },
          }),
          video: command({
            args: [],
            params: {
              codec: param.required(string),
            },
          })
        },
      },
    })
    

tree strategy

  • you get this tree object that reflects its shape
    tree.image?.params.quality // 9
    tree.media.audio?.mono // false
    tree.media.video?.codec // "av1"
    
    • all the commands are undefined except for the "selected" command
    • and yes, all the typings work

command-execution strategy

  • you can choose to provide each command with an async execute function
    command({
      args: [],
      params: {
        active: param.required(boolean),
        count: param.default(number, "101"),
      },
      async execute({params}) {
        params.active // true
        params.count // 101
      },
    })
    
    • your execute function receives the relevant fully-typed args, params, and some more stuff
  • your execute function can opt-into pretty-printing errors (with colors) by throwing an ExecutionError
    import {ExecutionError, command} from "@benev/argv"
    
    async execute({params}) {
      throw new ExecutionError("scary error printed in red!")
    }
    
  • if you choose to use this command-execution strategy, then you need to call your cli's final execute function
    // 👇 awaiting cli execution
    await cli(process.argv, {
      name: "pizza",
      commands: {
        meatlovers: command({
          args: [],
          params: {
            meatiness: param.required(number),
          },
          async execute({params}) {
            console.log(params.meatiness) // 9
          },
        }),
        hawaiian: command({
          args: [],
          params: {
            pineappleyness: param.required(number),
          },
          async execute({params}) {
            console.log(params.pineappleyness) // 8
          },
        }),
      },
    }).execute()
      // ☝️ calling cli final execute
    

🛠️ custom types

  • i can't believe i got all the types working for everything with custom types
  • it's easy to make your own types
    const date = asType({
      name: "date",
      coerce: string => new Date(string),
    })
    
    • the name is shown in help pages
    • the coerce function takes a string input, and you turn it into anything you like
  • then you can use 'em in your args and params like normal
    param.required(date)
    
  • hey why not make a list of 'em while we're at it
    param.required(list(date))
    
  • feeling spiffy? make a whole group of custom types with this one weird tip
    const myTypes = asTypes({
      date: string => new Date(string),
      integer: string => Math.floor(Number(string)),
    })
    
    • asTypes will use your object's property names as the type name
  • your custom types can throw errors and it works as validation
    const integer = asType({
      name: "integer",
      coerce: string => {
        const n = Number(string)
    
        if (isNaN(n))
          throw new Error("not a number")
    
        if (!Number.isSafeInteger(n))
          throw new Error("not a safe integer")
    
        return n
      },
    })
    

🦚 custom themes

  • you can set the theme for your --help pages
    import {themes} from "@benev/argv"
    
    await cli(process.argv, {
    
      // the default theme
      theme: themes.standard,
    
      ...otherStuff,
    }).execute()
    
    • maybe try themes.seaside for a more chill vibe
    • if you hate fun, use themes.noColor to disable ansi colors
  • make your own theme like this
    import {theme, color} from "@benev/argv"
    
    const seaside = theme({
      plain: [color.white],
      error: [color.brightRed, color.bold],
      program: [color.brightCyan, color.bold],
      command: [color.cyan, color.bold],
      property: [color.blue],
      link: [color.brightBlue, color.underline],
      arg: [color.brightBlue, color.bold],
      param: [color.brightBlue, color.bold],
      flag: [color.brightBlue],
      required: [color.cyan],
      mode: [color.blue],
      type: [color.brightBlue],
      value: [color.cyan],
    })
    

🌠 give me a github star!

  • i worked way too hard on this
  • please submit issues for any problems or questions
  • maybe make a cool help theme and submit a PR for it

版本与发布记录

@benev/argv 共发布 23 个版本,首次发布于 2023/01/06;过去 12 个月发布了 2 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.3.14latest2026/02/218 个月前
0.3.132026/02/198 个月前
0.3.122025/08/071.3 年前
0.3.112025/07/101.3 年前
0.3.102025/04/241.5 年前
0.3.92025/04/111.6 年前

关于 @benev/argv 的常见问题

@benev/argv 是什么?

command line argument parser

如何安装 @benev/argv?

在项目目录运行 npm install @benev/argv,或使用 pnpm add / yarn add / bun add。

@benev/argv 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@benev/argv 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@benev/argv 可以商用吗?

@benev/argv 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@benev/argv 的下载量有多少?

根据 npm 官方下载统计,@benev/argv 在 2026/10/08 – 2026/11/06 这 30 天内被下载 2,744 次,日均约 91 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@benev/argv 还在维护吗?

最新版本 0.3.14 发布于 2026/02/21(8 个月前);至今共发布 23 个版本,其中过去 12 个月发布了 2 个;首次发布于 2023/01/06。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。