コマンド
Zeltは @zeltjs/core を通じて、依存性の注入付きのCLIコマンドサポートを提供します。
Commandの作成
型安全なCLIコマンドのために、@Command デコレータを cliSchema() と args() とともに使います:
import { Command, cliSchema, args } from '@zeltjs/core';
@Command({
name: 'greet',
description: 'Greet a user',
})
export class GreetCommand {
static schema = cliSchema({
args: [{ name: 'name', type: 'string' }],
});
run(ctx = args(GreetCommand)) {
console.log(`Hello, ${ctx.name}!`);
}
}
設定
CLI用の src/cli.ts エントリポイントを作成します:
const app = createApp([command([GreetCommand])]);
const nodeApp = await onNode(app);
await nodeApp.commands.execCommand([...nodeApp.args]);
次に、zelt.config.ts で cli.entry を設定します:
// @filename: src/app.ts
import { createApp, command } from '@zeltjs/core';
export const app = createApp([command([])]);
// @filename: zelt.config.ts
import { defineConfig } from '@zeltjs/cli';
export default defineConfig({
app: () => import('./src/app').then((m) => m.app),
cli: { entry: './src/cli.ts' },
});
Commandの実行
コマンドを実行するには zelt run を使います:
# コマンドを実行する
zelt run greet Alice
# カスタムconfigを使う
zelt run -c ./config/zelt.config.ts greet Alice
スキーマ定義
cliSchema() 関数は、型付きの引数とオプションを定義します:
位置引数
@Command({ name: 'copy' })
export class CopyCommand {
static schema = cliSchema({
args: [
{ name: 'source', type: 'string' },
{ name: 'destination', type: 'string' },
],
});
run(ctx = args(CopyCommand)) {
console.log(`Copying ${ctx.source} to ${ctx.destination}`);
}
}
オプション(フラグ)
@Command({ name: 'build' })
export class BuildCommand {
static schema = cliSchema({
options: [
{ name: 'watch', type: 'boolean', alias: 'w' },
{ name: 'outDir', type: 'string', alias: 'o', default: 'dist' },
],
});
run(ctx = args(BuildCommand)) {
if (ctx.watch) {
console.log('Watching for changes...');
}
console.log(`Output directory: ${ctx.outDir}`);
}
}
# 使い方
zelt run build --watch --outDir=out
zelt run build -w -o out
引数とオプションの組み合わせ
@Command({ name: 'deploy' })
export class DeployCommand {
static schema = cliSchema({
args: [
{ name: 'environment', type: 'string' },
],
options: [
{ name: 'dryRun', type: 'boolean' },
{ name: 'tag', type: 'string' },
],
});
run(ctx = args(DeployCommand)) {
const { environment, dryRun, tag } = ctx;
if (dryRun) {
console.log(`[DRY RUN] Would deploy to ${environment}`);
} else {
console.log(`Deploying ${tag ?? 'latest'} to ${environment}`);
}
}
}
スキーマの型
引数の型
| 型 | 説明 |
|---|---|
string | 文字列値 |
number | 数値(自動的にパースされる) |
引数はoptionalとしてマークできます:
const schema = cliSchema({
args: [
{ name: 'file', type: 'string' },
{ name: 'count', type: 'number', optional: true },
],
});
オプションの型
| 型 | 説明 |
|---|---|
string | 文字列オプション |
number | 数値オプション(自動的にパースされる) |
boolean | 真偽値フラグ |
オプションにはデフォルト値を設定できます:
const schema = cliSchema({
options: [
{ name: 'port', type: 'number', default: 3000 },
{ name: 'verbose', type: 'boolean' }, // デフォルトはfalse
],
});
Transientスコープ
Commandは transient として登録されます — 実行のたびに新しいインスタンスが作成されます。これにより以下が保証されます:
- コマンド実行ごとにクリーンな状態
- 実行間で共有される可変状態がない
inject()を通じて注入された依存関係はsingletonのまま
@Command({ name: 'process' })
export class ProcessCommand {
private startTime = Date.now(); // 実行のたびに新しくなる
constructor(private db = inject(DatabaseService)) {} // singletonで共有される
run() {
console.log(`Started at: ${this.startTime}`);
}
}
依存性の注入
Commandは依存性の注入をサポートします:
@Command({ name: 'migrate' })
export class MigrateCommand {
static schema = cliSchema({
options: [
{ name: 'force', type: 'boolean' },
],
});
constructor(private readonly db = inject(DatabaseService)) {}
async run(ctx = args(MigrateCommand)) {
if (ctx.force) {
console.log('Force migration enabled');
}
await this.db.runMigrations();
console.log('Migrations completed');
}
}
プログラムからの実行
Commandは onNode() を使ってプログラムから実行できます:
const app = createApp([command([MigrateCommand])]);
const nodeApp = await onNode(app);
const result = await nodeApp.commands.execCommand(['migrate', '--force']);
console.log(`Exit code: ${result.exitCode}`);
Async Command
Commandはasyncにできます:
@Command({ name: 'sync' })
export class SyncCommand {
async run() {
console.log('Starting sync...');
await this.fetchData();
await this.processData();
console.log('Sync completed');
}
private async fetchData() {
// ...
}
private async processData() {
// ...
}
}