コンテンツにスキップ

Standard Schema

Rune のオプションや引数は、type プロパティで指定するプリミティブ型("string" | "number" | "boolean")と組み込みの enum 型(type: "enum"values)に加えて、schema プロパティで Standard Schema に準拠した任意のスキーマを指定できます。これにより、Zod や Valibot などのスキーマライブラリをそのまま活用して、単純な型チェックを超えたバリデーションと変換をオプション・引数に適用できます。

typeschema は同じフィールドに対していずれか一方のみを使用できます。どちらを選ぶかは、そのオプション・引数に必要な検証・変換のレベルによって決まります。

やりたいこと使うもの
単純な文字列・数値・真偽値の受け取りで十分type: "string" | "number" | "boolean"
固定された文字列/数値の選択肢に制限したいtype: "enum"values
フォーマット検証(UUID、メールアドレスなど)をおこないたいschema
値の範囲制約(最小・最大など)を課したいschema
文字列を特定の型に変換(z.coerce.number() など)したいschema
列挙値に対して追加のバリデーションや変換をおこないたいschema

プリミティブ型や enum 型は宣言が簡潔で、--help 出力にも <string><dev|prod> のような型ヒントとデフォルト値が自動で表示されます。一方、Standard Schema はライブラリの表現力をそのまま使えますが、ヘルプ表示は後述の typeLabel / defaultLabel で補う必要があります。

import { defineCommand } from "@rune-cli/rune";
import { z } from "zod";
export default defineCommand({
description: "Fetch a resource by id",
options: [
{
name: "retries",
schema: z.coerce.number().int().min(0).max(10),
description: "Number of retry attempts",
},
],
args: [
{
name: "id",
schema: z.uuid(),
description: "Resource id (UUID)",
},
],
run({ options, args }) {
// options.retries は number(0〜10 の整数に検証・変換済み)
// args.id は string(UUID 形式として検証済み)
},
});
import { defineCommand } from "@rune-cli/rune";
import * as v from "valibot";
export default defineCommand({
args: [
{
name: "mode",
schema: v.picklist(["dev", "prod"]),
},
],
run({ args }) {
// args.mode は "dev" | "prod"
},
});

ctx.argsctx.options の値の型は、スキーマの出力型から自動的に推論されます。

必須・省略可能とデフォルト値

Section titled “必須・省略可能とデフォルト値”

プリミティブ型では requireddefault プロパティで必須/省略可能を宣言しますが、スキーマフィールドではこれらを指定しません。代わりに、スキーマ自体の定義から必須/省略可能が決まります。

スキーマが undefined を受け入れるかどうかで必須/省略可能が決まります。z.string().optional()z.string().default("dev") のように undefined を受け付けるスキーマは省略可能として、それ以外は必須として扱われます。

import { defineCommand } from "@rune-cli/rune";
import { z } from "zod";
export default defineCommand({
options: [
// 必須: undefined を受け付けないため、省略するとエラー
{ name: "id", schema: z.uuid() },
// 省略可能: undefined が許容される
{ name: "label", schema: z.string().optional() },
// 省略可能 + デフォルト値: 省略時にスキーマが "dev" を返す
{ name: "mode", schema: z.string().default("dev") },
],
run() {
// ...
},
});

スキーマ側で default() を指定した場合、ユーザーがそのオプションを省略すると、スキーマが返すデフォルト値がそのまま ctx.options に入ります。

真偽値フラグとしてのオプション

Section titled “真偽値フラグとしてのオプション”

Standard Schema で定義したオプションを値なしの真偽値フラグとして扱うには、flag: true を指定します。フラグが指定された場合はスキーマに true が、指定されなかった場合は undefined が渡されます。

import { defineCommand } from "@rune-cli/rune";
import { z } from "zod";
export default defineCommand({
options: [
{
name: "force",
schema: z.boolean().optional(),
flag: true,
short: "f",
},
],
run({ options }) {
// options.force は boolean | undefined
},
});

flag: true が必要なのは、スキーマの内部構造から「このオプションが値を取るのか、値なしのフラグなのか」を Rune が判別できないためです。プリミティブ型では type: "boolean" がそのヒントになりますが、スキーマフィールドでは明示的な指定が必要になります。

flag: true の有無によって、引数のパース結果は次のように変わります:

Terminal window
# flag: true なし(値を取るオプションとして扱われる)
$ my-cli --force value # options.force = "value" がスキーマに渡される
$ my-cli --force # エラー: 値が必要
# flag: true あり(値なしのフラグとして扱われる)
$ my-cli --force # options.force = true がスキーマに渡される
$ my-cli --force value # "value" は次の位置引数として扱われる

flag: true を指定したスキーマオプションに対しては、プリミティブの真偽値オプションとは異なり、--no-<name> の自動生成はおこなわれません。否定形が必要な場合は、別オプションとして明示的に定義してください。

スキーマの flag option でも、オプションの env フォールバックを使用できます。env 値は "true" / "false" のみ受け付け、Rune が boolean に変換してからスキーマへ渡します。

typeLabeldefaultLabel によるヘルプ表示

Section titled “typeLabel と defaultLabel によるヘルプ表示”

Standard Schema にはスキーマの型やデフォルト値を外部から取り出すための API がないため、--help 出力にこれらを自動で表示することはできません。読み手に意図を伝えたい場合は、スキーマフィールドに表示専用の typeLabeldefaultLabel を指定します。

import { defineCommand } from "@rune-cli/rune";
import { z } from "zod";
export default defineCommand({
options: [
{
name: "port",
schema: z.coerce.number().int().positive().default(3000),
typeLabel: "number",
defaultLabel: "3000",
description: "Port to listen on",
},
],
run() {
// ...
},
});

--help 出力は次のようになります:

Options:
--port <number> Port to listen on (default: 3000)
-h, --help Show help

typeLabeldefaultLabel はいずれも表示専用であり、バリデーションや型推論、必須/省略可能の判定には一切影響しません。defaultLabel を指定しても、実際にデフォルト値を供給するのはスキーマ側です。ラベルとスキーマ側の定義がずれないよう、両方を更新する際はセットで見直してください。

スキーマの検証に失敗した場合、Rune はそのフィールドの検証エラーを引数パースエラーとしてまとめ、コマンド実行前にエラーメッセージを出力して終了します。メッセージは、スキーマが返す issuesmessage を改行で連結したものになります。

Terminal window
$ my-cli fetch not-a-uuid
Error: Invalid uuid

JSON モード(json: true のコマンドで --json を指定した場合)では、パースエラーも error オブジェクトとして stderr に JSON 形式で出力されます。詳細は JSON 出力ガイドを参照してください。