Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
/ fsss Public

ファイル構造がそのままコマンド構造になり、スキーマを一回書けば CLI フラグ・環境変数・設定ファイルのどこから値が来ても同じように型付きで受け取れる CLI フレームワーク

License

Notifications You must be signed in to change notification settings

miyaoka/fsss

Repository files navigation

fsss

npm version npm downloads license

fsss — "File Structure, Single Schema" — bun + TypeScript + Zod

ファイル構造がそのままコマンド構造になり、スキーマを一回書けば CLI フラグ・環境変数・設定ファイルのどこから値が来ても同じように型付きで受け取れる CLI フレームワーク

A CLI framework where your file structure becomes your command structure, and a single schema gives you typed values whether they come from flags, env vars, or config files.

File Structure — ファイルを置くだけでコマンドが生える

commands/
 serve.ts → my-app serve
 config/
 set.ts → my-app config set
 get.ts → my-app config get
 remote/
 [name]/
 push.ts → my-app remote origin push

ファイルを置くだけでコマンドが生える。ディレクトリのネストがサブコマンドの階層になる。[name] は動的セグメントで、params.name として値を受け取れる。

Single Schema — 1つの定義で CLI / env / config を統合

// commands/serve.ts
export default defineCommand({
 description: "サーバーを起動する",
 args: {
 port: {
 type: z.coerce.number().min(1).max(65535),
 description: "ポート番号",
 alias: "p",
 default: 3000,
 },
 host: {
 type: z.string(),
 description: "ホスト名",
 default: "localhost",
 },
 },
 run({ args }) {
 // args.port: number, args.host: string — 型推論される
 console.log(`${args.host}:${args.port}`);
 },
});

この1つの定義だけで、CLI フラグ・環境変数・設定ファイル・デフォルト値のすべてが統合される。

CLI flag my-app serve --port 8080 ← 最優先
env MYAPP_SERVE_PORT=5000 ← prefix + コマンドパス + arg 名で自動導出
config file { "serve": { "port": 4000 } } ← コマンドツリーと同じ構造
default 3000

どのソースから来た値も、最終的に同じ Zod スキーマでバリデーションされる。

環境変数の自動マッピング

autoEnv を指定すると、コマンドパス + arg 名から環境変数名を自動導出する。

const cli = createCLI({
 name: "my-app",
 autoEnv: { prefix: "MYAPP" },
});
コマンド arg 自動導出される env 名
serve port MYAPP_SERVE_PORT
remote push force MYAPP_REMOTE_PUSH_FORCE

設定ファイルの自動マッピング

config ファイルの JSON 構造はコマンドツリーと一致する。

{
 "serve": { "port": 5000, "host": "0.0.0.0" },
 "remote": { "push": { "force": true } }
}
my-app --config app.json serve

ヘルプ自動生成

$ my-app serve --help
サーバーを起動する
Usage: my-app serve [options]
Options:
 -p, --port <port> ポート番号 (env: MYAPP_SERVE_PORT, default: 3000)
 --host <host> ホスト名 (env: MYAPP_SERVE_HOST, default: localhost)
 -h, --help ヘルプを表示する

Plugin — _plugins/ を置くだけで横断的処理を追加

commands/
 _plugins/
 logger.ts ← 全コマンドに適用
 serve.ts
 remote/
 _plugins/
 auth.ts ← remote 配下にのみ適用
 [name]/
 push.ts

commands/ ツリーの任意の階層に _plugins/ を配置するだけで、その階層以下のコマンドにプラグインが自動適用される。

// commands/_plugins/logger.ts
export default definePlugin(({ cliName }) => ({
 provide: {
 logger: {
 info: (msg: string) => console.log(`[${cliName}] ${msg}`),
 },
 },
 middleware: async (_ctx, next) => {
 const start = performance.now();
 await next();
 console.log(`${(performance.now() - start).toFixed(0)}ms`);
 },
}));
  • provide で注入した値はコマンドの run()extensions として型付きで受け取れる
  • middleware はコマンドの実行を包む onion model(root 側が外側、leaf 側が内側)
  • Extensions の型は fsss-codegen で自動生成される
run({ extensions }) {
 extensions.logger.info("hello");
}

About

ファイル構造がそのままコマンド構造になり、スキーマを一回書けば CLI フラグ・環境変数・設定ファイルのどこから値が来ても同じように型付きで受け取れる CLI フレームワーク

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

AltStyle によって変換されたページ (->オリジナル) /