モノレポの手癖を deno で CLI ツールを作って楽にしたい
deno で CLI ツールを作っていたら楽しくなって色々作っていた。
課題: モノレポの諸々の操作がだるい
npm/pnpm/yarn の workspace を使っていると、次のようなディレクトリ移動が段々面倒になってくる。
foo を build して bar を build してルートから bar のテストを流す、みたいなことをするとこういう感じになる。
$ cd packages/foo
$ pnpm build
$ cd ../bar
$ pnpm build
$ cd ../..
## コマンドの中身を確認
$ cat package.json | jq ".scripts"
{
"test": "pnpm test:foo && pnpm test:bar",
"test:foo": "cd packages/foo && pnpm test",
"test:bar": "cd packages/bar && pnpm test"
}
$ pnpm test:bar
もちろん pnpm run --filter foo
や npm -w packages/foo
でもいいが、頻度が多いのでだるい。
-
npm run -w
が pnpm だと非互換でpnpm run --filter
とする必要があり、使い分けが面倒 - 普段 pnpm を使っているのだが、単純に
pnpm run --filter=pkg
はタイプ数が多くでだるい - package.json の中身を見てコマンドの中身を確認するのが面倒臭いので、さっと確認したい
という自分の手癖を解決するための CLI ツールを deno でさっと作った。
wsr
(WorkSpace Run のつもり)
$ deno install -Af https://raw.githubusercontent.com/mizchi/wsr/main/wsr.ts
# 実行
$ wsr foo build
$ wsr bar build
$ wsr test:bar
# タスク名を省略した場合 npm scripts の一覧を確認
$ wsr
📦 bar [@pkg/bar] <root>/packages/bar
test $ exit 0
📦 foo [@pkg/foo] <root>/packages/foo
test $ exit 0
📦 root [example] <root>/
test $ pnpm test:foo && pnpm test:bar
test:foo $ cd packages/foo && pnpm test
test:bar $ cd packages/bar && pnpm test
# foo だけ確認
$ wsr foo
📦 foo [@pkg/foo] <root>/packages/foo
test $ exit 0
(TODO: 気が向いたら deno.land/x に投げる)
モジュール指定は、次のいずれかのパターン。
- パッケージ名(eg.
@mizchi/foo
) - モジュールのユニークなディレクトリ名(eg.
foo
)。ディレクトリ名が重複している場合は指定不可 -
.
からはじまる相対パス(eg../packages/foo
) - ルートからの相対パス(
packages/foo
) - 上記のいずれのモジュール名も満たさないルートの npm-scripts (eg.
test
) のコマンド名の場合、それを実行
これはモジュール側からも実行できる。
$ cd packages/bar # 移動
$ wsr foo build # bar から foo を実行
$ wsr bar test # ws ../bar test も可
$ wsr test:bar # bar にコマンドがない場合は root の npm-scripts を実行
$ wsr root test:bar # 明示的に root を指定して実行
せっかく deno で作ったので、モジュール内に deno.jsonc を含むディレクトリがあるとき、 deno task も同じコマンド体系で実行できるようにした。
$$ tree .
.
├── package.json
├── packages
│ └── foo
│ └── package.json
└── tasks
└── deno.jsonc
$$ wsr
📦 foo [@pkg/foo] <root>/packages/foo
test $ exit 0
📦 root <root>/
test $ echo 1
🦕 tasks <root>/tasks
test $ echo deno
$$ wsr tasks test
$ cd /Users/kotaro.chikuba/mizchi/wsr/test/mixed-fixture/tasks
$ deno task test
Task test echo deno
deno
これでリポジトリ内で部分的に deno+zx でタスクを書くのが簡単になる。
repo-fetcher
次に簡易 ghq + degit 的なものを作った。何も考えずに git url を叩くと、手元に clone してくれるやつ。
$ deno install -Af https://raw.githubusercontent.com/mizchi/repo-fetcher/main/repo.ts
$ repo github/SpoonKnife # ~/repo/github/SpoonKnife
そのままだと repo
という主語が大きいコマンドが入る。気に食わない場合は deno install -Af --name repo-fetcher ...
でコマンド名を変更可。
次の設定で自分がオーナーのリポジトリは ~/mizchi/<repo>
に clone するようにできる。。
export REPO_FETCHER_ROOT=$HOME/repo # default
export REPO_FETCHER_OWNER=mizchi
export REPO_FETCHER_OWNER_ROOT=$HOME/mizchi
ついでに degit 的な部分チェックアウトも実装した。
$ repo https://github.com/mizchi/monorepo/tree/main/packages/lib-base packages/mylib
一旦 tmpdir に突っ込んで git sparse-checkout して該当パスをローカルに mv するという実装なので、 .git
を持てないのだが、雑に手元にもってきたい用なので気にならない(はず)。
テンプレート集
repo-fetcher 作ったし、せっかくなので部分チェックアウトして使う自分用のモノレポ用テンプレートも作った。
node library, CLI, React component library, React Application のテンプレートを置いている。
おまけ: deno で雑にコマンドをでっちあげる
自分は ~/bin
にパスを通して雑なコマンドを置いている。ここに zx の shebang を設定したスクリプトを置く。
#!/usr/bin/env -S deno run -A --ext ts
import { $ } from "npm:zx@7.1.1";
await $`ls`;
拡張子を省略する場合、 --ext ts
を付けないと js としてパースされてしまい、TS の構文がパースエラーになってしまう。
bash で制御構文を書きたくないので、最近はちょっとしたものは deno + zx で書くようになった。 deno ならインストール作業不要で動くので取り回しがいい。
実行権限を付けて実行。
$ code ~/bin/mycli # ↑ を編集
$ chmod +x ~/bin/mycli
$ mycli
deno install -Af mycli.ts
で ~/.deno/bin
の下に mycli
をインストールすることもできるが、これだと修正するたびにインストールが必要でちょっと面倒。雑に手元でいじりながら処理を書く場合はこっちのが楽。
感想
bash は deno + zx は CLI 作るのに取り回しがよくて最高。
Discussion