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 上提交問題或建議。