🦕

Deno CLI コマンド作成 最速 方法 20231218

2023/12/18に公開

この記事は前に書いた記事の抜粋かつ修正版

https://zenn.dev/mizchi/articles/wsr-monorepo-util

簡単にライブラリを呼び出すコマンドを作るとなったら、やはり deno + zx(or dux) だと思う。権限管理があって、ファイル単体でモジュールを解決して実行できる。

tl;dr

  • ~/bin/mycmd の単体ファイルでコマンドを作成する
  • shebang に deno permissions を与えて chmod +x する
  • node:utilparseArgsDeno.args をパース
  • dax でコマンドを実行

実装

~/bin 等にパスを通してあるとする。

雛形。

~/bin/myls
#!/usr/bin/env -S deno run -A --ext=ts
import { parseArgs } from "node:util";
import $ from "https://deno.land/x/dax@0.36.0/mod.ts";
// コマンド実行時に > ls みたいな形で出力する
$.setPrintCommand(true);

// 引数のパース
const parsed = parseArgs({
  args: Deno.args,
  options: {}
});

// run
await $`ls`;

これを

# $ code ~/bin/myls ファイルを編集
$ chmod +x ~/bin/myls
$ myls # run!
> ls
myls

みたいな感じで実行する。

以下解説

単体ファイルで完結

~/bin の下で実行コマンド以外を散らかしたくないので、 deno スクリプトを一つ置いて完結させる。

chmod +x で実行権限を与えて、拡張子を省略して実行するのを想定しているが、その際に ts shebang で --ext ts を与えておく必要がある(省略した場合は js として実行)。権限の制御が必要なら shebang に -A ではないパーミッションを書く。

テストが必要ならばスクリプト内で Deno.test を書いてテストする。

if (import.meta.main) {
  // run
  await $`ls`;
}
Deno.test('test', () => {
  // test here
});

dax でコマンド実行

前回の記事では npm:zx で作ったが、これはnodeで様々なユーティリティを同梱してるために、色々と余計な権限を要求してくる。

今回は dax を使った。

$ deno eval 'import $ from "https://deno.land/x/dax@0.36.0/mod.ts";$.setPrintCommand(true); await $`ls`;'
> ls
args.ts         cfai-text       cmd

とはいえ dax の要求する --allow-run は細かい工夫を吹き飛ばすほどの強い権限なので、よく考える必要はある。ファイルの読み書き程度なら dax ではなく --allow-read/--allow-write で書き直す。

Deno v1.39 - node:util's parseArgs

Deno の公式の flags 非常に使いづらく困っていたのだが、 Deno v1.39 で node:util の parseArgs がサポートされた。

https://deno.land/std@0.209.0/flags/mod.ts

これは自分が Node.js で愛用していたもので、 Node.js の標準ライブラリとして提供されていて TypeScript でいい感じに型が付く CLI パーサーになっている。

Parsing command line arguments with `util.parseArgs()` in Node.js

というか自分が Issue 立てて実装してもらって、やっとリリースされた。それで嬉しくなってこの記事を書いている。

https://github.com/denoland/deno/issues/20452

npm:zx が minimist 相当の CLI パースを同梱していたが、型がつくインターフェースではないし、parseArgs の方が Node 標準ライブラリの機能で使いやすい。

deno install との違い

今回のハック以外のコマンドを作る方法として、deno install コマンドでも deno スクリプトを CLI 化することはできる。

$ deno install --allow-read -f foo.ts # ~/.deno/bin/foo
$ foo # 拡張子を省略したコマンド名でインストールされる

これでも別に構わないのだが、自分はソースの変更毎に deno install し直すのが面倒だった。

複数のファイルで構成されるぐらいの複雑度のCLIコマンドを作ってるときは deno install で、1ファイルで完結する書き捨てのファイルなら今回の方式でやっている。

利点として、挙動を変更したくなったらその場でエディタで開いて編集できる。

おわり

何人かにこの自己流のやり方を紹介したら好評だったので記事にした。

Discussion