{\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}你这混蛋
Ex articis, viritas" (外语:外语:外语:外语:外语:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相:外相相相相相相相相:外相相相:外相:外相相:外相相相相相相相相相相相相相相相相相相相
以 @ boneskul
安装
npm install @boneskull/bargs
为什么是酒?
大多数参数解析器会使您选择:或者一个简单的API,有弱类型,或者一个复杂和被过度设计的DSL. 条子 提供了一种组合器式的API,用于构建类型安全的 CLI —— 组合式计划定义,并带有完整的类型推论.
还有,这是我唯一知道 与frickin'的争论 主题。 。 。 . 主题道格,你好吗.
快速启动
具有可选命令和若干选项的 CLI :
import { bargs, opt, pos } from '@boneskull/bargs';
await bargs('greet', { version: '1.0.0' })
.globals(
opt.options({
name: opt.string({ default: 'world' }),
loud: opt.boolean({ aliases: ['l'] }),
}),
)
.command(
'say',
pos.positionals(pos.string({ name: 'message', required: true })),
({ positionals, values }) => {
const [message] = positionals;
const greeting = `${message}, ${values.name}!`;
console.log(values.loud ? greeting.toUpperCase() : greeting);
},
'Say a greeting',
)
.defaultCommand('say')
.parseAsync();
$ greet Hello --name Alice --loud
HELLO, ALICE!
使用量
建筑型安全
每个助手返回一个完整类型的定义 :
import { opt, pos } from '@boneskull/bargs';
const verbose = opt.boolean({ aliases: ['v'] });
// Type: BooleanOption & { aliases: ['v'] }
const level = opt.enum(['low', 'medium', 'high'], { default: 'medium' });
// Type: EnumOption<'low' | 'medium' | 'high'> & { default: 'medium' }
const file = pos.string({ name: 'file', required: true });
// Type: StringPositional & { name: 'file', required: true }
当您用这些建立 CLI 时, 结果类型会自动流过默认的选项或 required: true 是不可撤销的.
可编曲
选项和位置可以使用可调用解析器合并 :
import { opt, pos } from '@boneskull/bargs';
// Create separate parsers
const options = opt.options({
verbose: opt.boolean({ aliases: ['v'] }),
output: opt.string({ aliases: ['o'], default: 'stdout' }),
});
const positionals = pos.positionals(
pos.string({ name: 'input', required: true }),
);
// Merge them: positionals(options) combines both
const parser = positionals(options);
// Type: Parser<{ verbose: boolean | undefined, output: string }, [string]>
简单 CLI
对于没有子命令的 CLI , 请使用 .globals() 包含合并的选项和位置,然后自己处理结果:
import { bargs, opt, pos } from '@boneskull/bargs';
// Merge options and positionals into one parser
// when a positional is variadic, it becomes an array within the result
const parser = pos.positionals(pos.variadic('string', { name: 'text' }))(
opt.options({
uppercase: opt.boolean({ aliases: ['u'], default: false }),
}),
);
const { values, positionals } = await bargs('echo', {
description: 'Echo text to stdout',
version: '1.0.0',
})
.globals(parser)
.parseAsync();
const [words] = positionals;
const text = words.join(' ');
console.log(values.uppercase ? text.toUpperCase() : text);
基于命令的CLI
对于具有多个子命令的 CLI :
import { bargs, merge, opt, pos } from '@boneskull/bargs';
await bargs('tasks', {
description: 'A task manager',
version: '1.0.0',
})
.globals(
opt.options({
verbose: opt.boolean({ aliases: ['v'], default: false }),
}),
)
.command(
'add',
// Use merge() to combine positionals with command-specific options
merge(
opt.options({
priority: opt.enum(['low', 'medium', 'high'], { default: 'medium' }),
}),
pos.positionals(pos.string({ name: 'text', required: true })),
),
({ positionals, values }) => {
const [text] = positionals;
console.log(`Adding ${values.priority} priority task: ${text}`);
if (values.verbose) console.log('Verbose mode enabled');
},
'Add a task',
)
.command(
'list',
opt.options({
all: opt.boolean({ default: false }),
}),
({ values }) => {
console.log(values.all ? 'All tasks' : 'Pending tasks');
},
'List tasks',
)
.defaultCommand('list')
.parseAsync();
$ tasks add "Buy groceries" --priority high --verbose
Adding high priority task: Buy groceries
Verbose mode enabled
$ tasks list --all
All tasks
嵌入式命令( 子命令)
命令可以被嵌入任意的深度. 唯一的限制就是你对用户的蔑视. 使用 工厂图案数字 :
import { bargs, opt, pos } from '@boneskull/bargs';
await bargs('git')
.globals(opt.options({ verbose: opt.boolean({ aliases: ['v'] }) }))
// Factory pattern: receives a builder with parent globals already typed
.command(
'remote',
(remote) =>
remote
.command(
'add',
pos.positionals(
pos.string({ name: 'name', required: true }),
pos.string({ name: 'url', required: true }),
),
({ positionals, values }) => {
const [name, url] = positionals;
// values.verbose is fully typed! (from parent globals)
if (values.verbose) console.log(`Adding ${name}: ${url}`);
},
'Add a remote',
)
.command('remove' /* ... */)
.defaultCommand('add'),
'Manage remotes',
)
.command('commit', commitParser, commitHandler) // Regular command
.parseAsync();
$ git --verbose remote add origin https://github.com/...
Adding origin: https://github.com/...
$ git remote remove origin
工厂的功能收到一个 CliBuilder 已经输入了父全局, 因此所有嵌入式命令处理器都会获得合并的完整类型推断 global + command 选项。 见 examples/nested-commands.ts 一个完整的例子.
API 密码
bargs( 名称, 选项 )
创建 CLI 构建器 .
| 选项 | 类型 | 说明 |
|---|---|---|
description | string | 在帮助中显示描述 |
version | string | 启用 --version 旗帜 |
epilog | string 或者说 false | 帮助页脚文本(参见 图片(中文(简体) ) |
theme | Theme | 帮助色彩主题(参见 他们(中文(简体) ) |
. globals( 参数)
设置全局选项并转换适用于所有命令.
bargs('my-cli').globals(opt.options({ verbose: opt.boolean() }));
// ...
命令( 名称、 解析器、 处理器、 描述 )
注册一个命令 。 处理器接收被合并的全球+命令类型.
.command(
'build',
opt.options({ watch: opt.boolean() }),
({ values }) => {
// values has both global options AND { watch: boolean }
console.log(values.verbose, values.watch);
},
'Build the project',
)
命令(名称、工厂、描述?)
使用工厂函数注册嵌入式指令组 。 工厂接收一个已经拥有母公司Globals键入的构建器,在被巢接的处理器中给出了完整的类型推论.
bargs('main')
.globals(opt.options({ verbose: opt.boolean() }))
.command(
'nested',
(nested) =>
nested
.command('foo', fooParser, ({ values }) => {
// values.verbose is typed correctly!
})
.command('bar', barParser, barHandler),
'Nested commands',
)
.parseAsync();
默认命令(名称)
或者说
.defaultCommand(parser, handler)
设置未指定命令时运行的命令 .
// Reference an existing command by name
.defaultCommand('list')
// Or define an inline default
.defaultCommand(
pos.positionals(pos.string({ name: 'file' })),
({ positionals }) => console.log(positionals[0]),
)
.parse(args?) /.parseAsync(args?) 维基语录链接:名人名言
解析参数并执行处理器 .
.parse()- 同步 如果有变换者或操作者返回承诺,则投出..parseAsync()- 一个同步的。 支持合成变换和处理器.
// Async (supports async transforms/handlers)
const result = await bargs('my-cli').globals(...).parseAsync();
console.log(result.values, result.positionals, result.command);
// Sync (no async transforms/handlers)
const result = bargs('my-cli').globals(...).parse();
选项助手
import { opt } from '@boneskull/bargs';
opt.string({ default: 'value' }); // --name value
opt.number({ default: 42 }); // --count 42
opt.boolean({ aliases: ['v'] }); // --verbose, -v
opt.boolean({ aliases: ['v', 'verb'] }); // --verbose, --verb, -v
opt.enum(['a', 'b', 'c']); // --level a
opt.array('string'); // --file x --file y
opt.array(['low', 'medium', 'high']); // --priority low --priority high
opt.count(); // -vvv → 3
选项属性
| 财产 | 类型 | 说明 |
|---|---|---|
aliases | string[] | 短 (['v'] 页:1 -v)或长相别名(['verb'] 页:1 --verb(中文(简体) ) |
default | 变化 | 默认值( 使选项不可更改) |
description | string | 帮助文本描述 |
group | string | 自定义区域标题下的分组选项 |
hidden | boolean | 隐藏从 --help 产出 |
required | boolean | 按要求标记( 使选项不可更改) |
别名
选项既可以有短(单字符)又可以有长(多字符)别名:
opt.options({
verbose: opt.boolean({ aliases: ['v', 'verb'] }),
output: opt.string({ aliases: ['o', 'out'] }),
});
所有这些都相当于:
$ my-cli -v # verbose: true
$ my-cli --verb # verbose: true
$ my-cli --verbose # verbose: true
$ my-cli -o file.txt # output: "file.txt"
$ my-cli --out file.txt # output: "file.txt"
$ my-cli --output file.txt # output: "file.txt"
对于非阵列选项,既使用别名,又使用克尼克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克
$ my-cli --verb --verbose
Error: Conflicting options: --verb and --verbose cannot both be specified
对于数组选项,所有别名的值被合并. 单字符别名和单词名先被处理(以命令行顺序),再由多字符别名后被后附:
opt.options({
files: opt.array('string', { aliases: ['f', 'file'] }),
});
$ my-cli --file a.txt -f b.txt --files c.txt
# files: ["b.txt", "c.txt", "a.txt"]
# (-f and --files first, then --file appended)
布尔 Negation (- 否- )
所有布尔选项自动支持否定的形式 --no-<flag> 明确设定选择 false数字 :
$ my-cli --verbose # verbose: true
$ my-cli --no-verbose # verbose: false
$ my-cli # verbose: undefined (or default)
如果两者兼有 --flag 财务报告和审定财务报表 --no-flag 指定,bargs丢出一个错误:
$ my-cli --verbose --no-verbose
Error: Conflicting options: --verbose and --no-verbose cannot both be specified
在帮助输出时,布尔以 default: true 显示为 --no-<flag> (因为这样用户才会关机):
opt.options({
colors: opt.boolean({ default: true, description: 'Use colors' }),
});
// Help output shows: --no-colors Use colors [boolean] default: true
选项( schema)
从选项方案创建解析器 :
const parser = opt.options({
verbose: opt.boolean({ aliases: ['v'] }),
output: opt.string({ default: 'out.txt' }),
});
// Type: Parser<{ verbose: boolean | undefined, output: string }, []>
定位辅助工具
import { pos } from '@boneskull/bargs';
pos.string({ required: true }); // <file>
pos.number({ default: 8080 }); // [port]
pos.enum(['dev', 'prod']); // [env]
pos.variadic('string'); // [files...]
位置属性
| 财产 | 类型 | 说明 |
|---|---|---|
default | 变化 | 默认值 |
description | string | 帮助文本描述 |
name | string | 在帮助中显示名称( 默认为 arg0, (中文(简体) ), arg1, (...) (中文(简体) ) |
required | boolean | 按要求标出( 显示为 <name> 对 [name](中文(简体) ) |
pos.posals (. defs) (... defs) (英语)
从位置定义创建解析器 :
const parser = pos.positionals(
pos.string({ name: 'source', required: true }),
pos.string({ name: 'dest', required: true }),
);
// Type: Parser<{}, [string, string]>
使用 variadic 剩余参数( 必须是最后一个) :
const parser = pos.positionals(pos.variadic('string', { name: 'files' }));
// Type: Parser<{}, [string[]]>
合并解析器
使用 merge() 将多个解析器合并为一个:
import { merge, opt, pos } from '@boneskull/bargs';
const combined = merge(
opt.options({
priority: opt.enum(['low', 'medium', 'high'], { default: 'medium' }),
}),
pos.positionals(pos.string({ name: 'task', required: true })),
);
// Type: Parser<{ priority: 'low' | 'medium' | 'high' }, [string]>
您可以根据需要合并尽可能多的解析器- 选项被合并( 后被提前覆盖) , 位置被压缩 .
或者,可以通过调用一个和另一个来合并解析器:
const options = opt.options({ priority: opt.enum(['low', 'medium', 'high']) });
const positionals = pos.positionals(
pos.string({ name: 'task', required: true }),
);
// These are equivalent:
const combined1 = positionals(options);
const combined2 = options(positionals);
使用您认为更可读的样式.
变换
使用 map() 在解析值到达您的处理器前进行转换 :
import { bargs, map, opt } from '@boneskull/bargs';
const globals = map(
opt.options({
config: opt.string(),
verbose: opt.boolean({ default: false }),
}),
({ values, positionals }) => ({
positionals,
values: {
...values,
// Add computed properties
timestamp: new Date().toISOString(),
configLoaded: !!values.config,
},
}),
);
await bargs('my-cli')
.globals(globals)
.command(
'info',
opt.options({}),
({ values }) => {
// values.timestamp and values.configLoaded are available
console.log(values.timestamp);
},
'Show info',
)
.parseAsync();
变换是完全安全的——返回类型成为处理器中可用的类型.
同步变换
变形可以是同位素 :
const globals = map(
opt.options({ url: opt.string({ required: true }) }),
async ({ values, positionals }) => {
const response = await fetch(values.url);
return {
positionals,
values: {
...values,
data: await response.json(),
},
};
},
);
骆驼箱选项密钥
如果您更喜欢骆驼Case属性名称而不是kebab- case, 请使用 camelCaseValues 变换 :
import { bargs, map, opt, camelCaseValues } from '@boneskull/bargs';
const { values } = await bargs('my-cli')
.globals(
map(
opt.options({
'output-dir': opt.string({ default: '/tmp' }),
'dry-run': opt.boolean(),
}),
camelCaseValues,
),
)
.parseAsync(['--output-dir', './dist', '--dry-run']);
console.log(values.outputDir); // './dist'
console.log(values.dryRun); // true
这个 camelCaseValues 变换 :
- 将所有 kebab 大小写密钥转换为骆驼Case(
output-dir→outputDir(中文(简体) ) - 保存已经是骆驼的密钥Case或没有连字符的密钥
- 完全类型安全- TypeScript 知道已转换的密钥名称
图片
默认, 条子 显示您的软件包主页和仓库 URL(来自 package.json)在帮助输出结尾. URL在所支持的终端中成为可点击的超链接.
// Custom epilog
bargs('my-cli', {
epilog: 'For more info, visit https://example.com',
});
// Disable epilog entirely
bargs('my-cli', { epilog: false });
他们
自定义帮助输出带有内置主题或您自己的颜色 :
// Use a built-in theme: 'default', 'mono', 'ocean', 'warm'
bargs('my-cli', { theme: 'ocean' });
// Disable colors entirely
bargs('my-cli', { theme: 'mono' });
这个 ansi 导出为样式化终端输出提供通用的 ANSI 逃脱代码 :
import { ansi } from '@boneskull/bargs';
bargs('my-cli', {
theme: {
colors: {
command: ansi.bold,
flag: ansi.brightCyan,
positional: ansi.magenta,
// ...
},
},
});
可用的主题颜色槽 :
| 槽 | 它的风格 |
|---|---|
command | 命令名称(例如, init, (中文(简体) ), build(中文(简体) ) |
defaultText | 这个 default: 标签 |
defaultValue | 默认值( 例如 ), false, (中文(简体) ), "hello"(中文(简体) ) |
description | 选项和命令描述文本 |
epilog | 页脚文本( 主页,仓库) |
example | 示例代码/命令 |
flag | 旗名( 如 ), --verbose, (中文(简体) ), -v(中文(简体) ) |
positional | 位置参数名称( 例如 ), <file>(中文(简体) ) |
scriptName | 在页眉中显示的 CLI 名称 |
sectionHeader | 节标题(例如, USAGE, (中文(简体) ), OPTIONS(中文(简体) ) |
type | 类型说明(例如, [string], (中文(简体) ), [number](中文(简体) ) |
url | URL( 可点击超链接) |
usage | 使用行文字 |
[TIP] 您不需要指定所有颜色槽 。 缺失的颜色会回到默认主题 .
贝壳补全
条子 可以生成shash,zsh,和fish的外壳补全脚本. 启用它与 completion 选项 :
bargs('my-cli', {
completion: true,
version: '1.0.0',
});
然后生成并安装您 shell 的完成脚本 :
巴许
# Add to ~/.bashrc (or ~/.bash_profile on macOS)
my-cli --completion-script bash >> ~/.bashrc
source ~/.bashrc
日语
# Add to ~/.zshrc
my-cli --completion-script zsh >> ~/.zshrc
source ~/.zshrc
# Or save to a file in your $fpath
my-cli --completion-script zsh > ~/.zsh/completions/_my-cli
鱼
# Save to completions directory
my-cli --completion-script fish > ~/.config/fish/completions/my-cli.fish
完成什么
一旦安装, 按 Tab 将完成:
- 命令和子命令 (包括巢的命令和别名)
- 选项 (单位:千美元)
--verbose, (中文(简体) ),-v, (中文(简体) ),--no-verbose布尔语) - 数字 选项和职位
- 全球备选方案 在任何指挥级别
$ my-cli <TAB>
build test lint
$ my-cli build --target <TAB>
dev staging prod
$ my-cli --<TAB>
--verbose --config --help --version
见 examples/completion.ts 一个完整的例子.
高级使用
处理错误
条子 出口部分 Error 子类 :
import {
bargs,
BargsError,
HelpError,
ValidationError,
} from '@boneskull/bargs';
try {
await bargs('my-cli').parseAsync();
} catch (error) {
if (error instanceof ValidationError) {
// Config validation failed (e.g., invalid schema)
// i.e., "you screwed up"
console.error(`Config error at "${error.path}": ${error.message}`);
} else if (error instanceof HelpError) {
// Likely invalid options, command or positionals;
// re-throw to trigger help display
throw error;
} else if (error instanceof BargsError) {
// General bargs error
console.error(error.message);
}
}
程序辅助
程序生成帮助文本 :
import { generateHelp, generateCommandHelp } from '@boneskull/bargs';
// These require the internal config structure—see source for details
const helpText = generateHelp(config);
const commandHelp = generateCommandHelp(config, 'migrate');
超链接公用事业
创建可点击的终端超链接( C)OSC 8 (中文(简体) ):
import { link, linkifyUrls, supportsHyperlinks } from '@boneskull/bargs';
// Check if terminal supports hyperlinks
if (supportsHyperlinks()) {
// Create a hyperlink
console.log(link('Click me', 'https://example.com'));
// Auto-linkify URLs in text
console.log(linkifyUrls('Visit https://example.com for more info'));
}
[TIP] 条子 已自动链接 URL
--help如果终端支持超链接,则输出.
其他主题公用事业
import {
ansi, // ANSI escape codes
createStyler, // Create a styler from a theme
defaultTheme, // The default theme object
stripAnsi, // Remove ANSI codes from string
themes, // All built-in themes
} from '@boneskull/bargs';
// Create a custom styler
const styler = createStyler({ colors: { flag: ansi.green } });
console.log(styler.flag('--verbose'));
// Strip ANSI codes for plain text output
const plain = stripAnsi('\x1b[32m--verbose\x1b[0m'); // '--verbose'
低级公用事业
这个 handle(parser, fn) 函数导出用于高级使用时需要创建 Command 在流畅的构建器之外的对象。 大部分被替换 .command(name, parser, handler)。 。 。 .
依赖关系
条子 有 0 个依赖关系。 仅有Node.js v20+.
动机
我总是来找 rg 在我的CLI项目。 然而,我发现自己屡次做同样的事情;我脑子里有某种锅炉板,准备去(requiresArg: true 财务报告和审定财务报表 nargs: 1美人鱼? 我不希望锅炉板在我的头上。 我想把我所选择的雅格士行为 分解成可调和的API 这样 条子 是生出来的.
许可证
版权2025 克里斯托弗·"波内斯库尔"·希勒. 根据 蓝橡树型号许可证 1.0.0。 。 。 .
