Optique 0.3.0:依赖选项和灵活组合

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub
我们发布了 Optique 0.3.0,其中包含多项改进,使构建复杂的 CLI 应用程序变得更加直观。此版本专注于扩展解析器的灵活性并改进帮助系统,这些改进基于社区反馈,特别是来自 Fedify 项目从 Cliffy 迁移到 Optique 的反馈。特别感谢 @z9mb1wwj 在此过程中提供的宝贵见解。
新特性
- 使用新的
flag()
解析器实现必需的布尔标志,用于依赖选项模式 withDefault()
中的灵活类型默认值,支持联合类型以实现条件 CLI 结构- 扩展的
or()
容量,现在支持多达 10 个解析器(之前为 5 个) - 增强的
merge()
组合器,可与任何生成对象的解析器配合使用,而不仅限于object()
- 使用新的
longestMatch()
组合器实现上下文感知帮助 - 在
@optique/core
和@optique/run
中支持版本显示 - 结构化输出函数,用于一致的终端格式化
使用 flag()
实现必需的布尔标志
新的 flag()
解析器创建必须明确提供的布尔标志。虽然 option()
在缺失时默认为 false
,但 flag()
在不存在时会完全失败解析。这种微妙的差异使依赖选项的模式更加清晰。
考虑一个场景,某些选项只有在明确启用模式时才有意义:
import { flag, object, option, withDefault } from "@optique/core/parser";
import { integer } from "@optique/core/valueparser";
// 没有 --advanced 标志,这些选项不可用
const parser = withDefault(
object({
advanced: flag("--advanced"),
maxThreads: option("--threads", integer()),
cacheSize: option("--cache-size", integer())
}),
{ advanced: false as const }
);
// 用法:
// myapp → { advanced: false }
// myapp --advanced → 错误:需要 --threads 和 --cache-size
// myapp --advanced --threads 4 --cache-size 100 → 成功
这种模式对于确认标志(--yes-i-am-sure
)或从根本上改变 CLI 行为的模式开关特别有用。
withDefault()
中的联合类型
以前,withDefault()
要求默认值与解析器的类型完全匹配。现在它支持不同的类型,创建联合类型,从而实现条件 CLI 结构:
const conditionalParser = withDefault(
object({
server: flag("-s", "--server"),
port: option("-p", "--port", integer()),
host: option("-h", "--host", string())
}),
{ server: false as const }
);
// 结果类型现在是一个联合类型:
// | { server: false }
// | { server: true, port: number, host: string }
这一变化使构建 CLI 变得更加容易,不同的标志可以启用不同的选项集,而无需使用复杂的 or()
链。
更灵活的 merge()
组合器
merge()
组合器现在接受任何产生类对象值的解析器。以前仅限于 object()
解析器,现在它可以与 withDefault()
、map()
和其他转换解析器一起工作:
const transformedConfig = map(
object({
host: option("--host", string()),
port: option("--port", integer())
}),
({ host, port }) => ({ endpoint: `${host}:${port}` })
);
const conditionalFeatures = withDefault(
object({
experimental: flag("--experimental"),
debugLevel: option("--debug-level", integer())
}),
{ experimental: false as const }
);
// 现在可以合并不同的解析器类型
const appConfig = merge(
transformedConfig, // map() 结果
conditionalFeatures, // withDefault() 解析器
object({ // 传统的 object()
verbose: option("-v", "--verbose")
})
);
这一改进源于认识到许多解析器最终都会产生对象,而人为地将 merge()
限制为仅使用 object()
解析器会限制组合模式。
使用 longestMatch()
实现上下文感知帮助
新的 longestMatch()
组合器选择消耗最多输入标记的解析器。这使得复杂的帮助系统成为可能,其中 command --help
显示该特定命令的帮助,而不是全局帮助:
const normalParser = object({
help: constant(false),
command: or(
command("list", listOptions),
command("add", addOptions)
)
});
const contextualHelp = object({
help: constant(true),
commands: multiple(argument(string())),
helpFlag: flag("--help")
});
const cli = longestMatch(normalParser, contextualHelp);
// myapp --help → 显示全局帮助
// myapp list --help → 显示 'list' 命令的帮助
// myapp add --help → 显示 'add' 命令的帮助
@optique/core/facade
和 @optique/run
中的 run()
函数现在自动使用这种模式,因此您的 CLI 无需任何额外配置即可获得上下文感知帮助。
版本显示支持
@optique/core/facade
和 @optique/run
现在都通过 --version
标志和 version
命令支持版本显示。详情请参阅 runners 文档:
// @optique/run - 简单 API
run(parser, {
version: "1.0.0", // 添加 --version 标志
help: "both"
});
// @optique/core/facade - 详细控制
run(parser, "myapp", args, {
version: {
mode: "both", // --version 标志和 version 命令
value: "1.0.0",
onShow: process.exit
}
});
API 遵循与帮助配置相同的模式,保持一致性和可预测性。
结构化输出函数
@optique/run
中的新输出函数提供了一致的终端格式化,具有自动功能检测。在 messages 文档 中了解更多信息:
import { print, printError, createPrinter } from "@optique/run";
import { message } from "@optique/core/message";
// 带有自动格式化的标准输出
print(message`Processing ${filename}...`);
// 错误输出到 stderr,可选择退出
printError(message`File ${filename} not found`, { exitCode: 1 });
// 针对特定需求的自定义打印器
const debugPrint = createPrinter({
stream: "stderr",
colors: true,
maxWidth: 80
});
debugPrint(message`Debug: ${details}`);
这些函数自动检测终端功能并应用适当的格式化,使您的 CLI 输出在不同环境中保持一致。
破坏性变更
虽然我们尽量保持向后兼容性,但有几个变化需要注意:
@optique/run
中的help
选项不再接受"none"
。要禁用帮助,只需省略该选项。- 实现
getDocFragments()
的自定义解析器需要更新其签名,使用DocState<TState>
而不是直接的状态值。 object()
解析器现在使用贪婪解析,尝试在一次传递中消耗所有匹配的字段。这不应影响大多数用例,但可能会在复杂场景中改变解析顺序。
升级到 0.3.0
要升级到 Optique 0.3.0,请更新两个包:
# Deno (JSR)
deno add @optique/core@^0.3.0 @optique/run@^0.3.0
# npm
npm update @optique/core @optique/run
# pnpm
pnpm update @optique/core @optique/run
# Yarn
yarn upgrade @optique/core @optique/run
# Bun
bun update @optique/core @optique/run
如果您只使用核心包:
# Deno (JSR)
deno add @optique/core@^0.3.0
# npm
npm update @optique/core
展望未来
这些改进来自实际使用和社区反馈。我们特别希望听到新的依赖选项模式如何适用于您的用例,以及上下文感知帮助系统是否满足您的需求。
一如既往,您可以在 optique.dev 找到完整文档,并在 GitHub 上提交问题或建议。