monolithic kernel

google/zx と VSCode で快適にスクリプトを書く

September 01, 2022

前回 Kotlin Scripting を使おうとしてみて、導入はよい気がしたものの、実際使おうとすると、HTTP クライアントどうするのとか、JSON はどうやってパースするのとか問題が山積みなことに気付いた。結局ライブラリが全部 Java ないし Kotlin なので、スクリプトとして使うための手軽さがなかったり、やたら識別子が長かったりする。

ということで、今度は他の選択肢として気になっていた zx を試した。zx は JavaScript でシェルスクリプトを置き換えるようなところを狙ったもの。

Homebrew にパッケージがあるので導入は簡単。npm のパッケージを npx で使ってもよい。公式だと GitHub Actions の中で使うなら npx がおすすめとされていた。

main.mjs
await $`echo Hello, ${os.type()}!`
# Use Homebrew
brew install zx
zx main.mjs

# Use npx
npx zx main.mjs

特に何も import しなくても最初からファイル操作できるようになっていたり、外部コマンドの呼び出しを$`command` で実行できて、しかも Promise を返してくれたりと、モダンな感じ。

fetch とか JSON.parse とかももちろん問題なく使える。

const response = await fetch(
  "https://www.jma.go.jp/bosai/forecast/data/forecast/130000.json"
)
const body = JSON.parse(await response.text())

console.log(body)

とはいえ、個人的には JavaScript で書きたいと思ったのは、VSCode で JavaScript を書いているときの補完の優秀さをちょっとしたスクリプトにも持ち込みたいというのが最大の理由。これがなかったら Ruby とかのほうが便利なんじゃないかと思う。特に、コレクションの操作でいうと JavaScript は貧弱な印象。

補完を使えるようにしようとすると、意外と素直には行かなくなってくる。zx コマンドで実行するスクリプトとして書く場合、zx の提供する機能は暗黙のうちに import 済みの状態なので、VSCode でソースを書いているときに認識してくれない。何も import しなくても最初から便利に使えることが完全に仇になっている。

zx はコマンドだけではなくライブラリとしても提供されているため、import して使うこともできる。この場合、import を明示的に記述しているので VSCode は zx を認識してくれる一方で、package.json を用意して npm install しないとスクリプトを実行できず、手軽さが損なわれる。

package.json
{
  "devDependencies": {
    "zx": "^7.0.8"
  }
}
main.mjs
import "zx/globals"

await $`echo Hello, ${os.type()}!`
npm install
npm exec -- zx main.mjs

いろいろ調べて辿り着いたのが、Triple-slash directive を用いる方法。この方法でも package.json を用意するのは変わらないが、import する代わりに Triple-slash directive のコメントで zx を使用していることを表明する。これにより、VSCode は zx を認識してくれて補完が効きつつ、import はしていないことになる。結果として、zx をライブラリとして使う場合とは異なり、開発環境とは別の環境で実行する際に npm install なしで zx コマンドや npx 経由で使える。

package.json
{
  "devDependencies": {
    "zx": "^7.0.8"
  }
}
main.mjs
/// <reference types="zx/build/globals" />

await $`echo Hello, ${os.type()}!`
zx main.mjs # or npx zx main.mjs

しばらくこれで試してみようと思う。

ちなみに、調べていく中で Deno もなかなかおもしろいなと思った。Deno は設定なしでいきなり TypeScript が使えて、package.json なしの単一ファイルのみで外部のライブラリを import できるので、ちょっとしたスクリプトとの相性が非常によい。

# ついでに標準の機能でスタンドアロンのバイナリを作れるのもよさげ。

さらに、zx-denobazxdxdeno-dzx などなど、Inspired by zx な Deno 向けのライブラリが多数存在する。ただ、いずれも本家 zx と比べると GitHub の Star 数はまったく桁が違うので、継続して使うなら zx なのかなぁとなる。Deno で zx が動いてくれるのが最高とも思ったが、サポートするつもりはないらしい。残念。