虎の穴開発室ブログ

虎の穴ラボ株式会社所属のエンジニアが書く技術ブログです

MENU

Deno 用 の zx?「dzx」を紹介します

こんにちは、おっくんです。2021の最後の大きなお買い物は CSM ゼロノスベルト でした。

先日公開された2021 JavaScript ライジングスター で、 「zx」総合一位になりました。

今回はこのzxの Deno 向けの実装、「dzx」 を紹介します。

github.com

dzx は、zx インスパイアの Deno のシェルツールです。 dzx の紹介の前にこの zx を紹介します。

zx」は、Google が公開している Node.js 環境で動作するシェルツールです。

一番最初のコミットが 2021 年 5 月と最近公開されたツールになっています。
この、zx をインスパイアした Deno 環境で操作するシェルツールが dzx です。 (提供しているのは Google ではありません。)

特徴的なのは、いわゆるシェル操作を JavaScriptとTypeScript で代替できる点です。
さらに、マークダウンのコードブロック中のスクリプトを実行対象として実行できるのも魅力です。 特に、標準でTypescript が動作する Deno向けの zx ということもあり、 TypeScript で使うための環境構築などが不要です。

インストール方法から順を追って紹介します。

導入

実行環境:Deno 1.17.3

インストール

以下コマンドでインストールします。

$ deno install --allow-all --unstable -f https://deno.land/x/dzx@0.3.0/dzx.ts
$ which dzx
/usr/local/bin/dzx

最初のスクリプト

shell-1.ts と名前をつけて、手始めに現在のディレクトリで ls するだけのスクリプトとして下記の通り実装します。 [shell-1.ts]

#!/usr/bin/env dzx

// $.verbose = true;  を入れると、コンソールに$ ls のように表示されます。

const output = await $`ls`;
const files = output.stdout.split("\n").map((f) => console.log(f));

下記の通り実行できます。

$ dzx shell-1.ts
Check file:///usr/src/app/shell-1.ts
Dockerfile
docker-compose.yml
shell-1.ts

JavaScript(TypeScript)資産を活用する

続けて、JavaScript を使えるという利点を生かして以下のスクリプトを作成します。 内容は、

  • 気象庁が公開しているJSONデータから東京都の天気予報を取得する天気を取得する
  • ./output のディレクトリを作成
  • ./output/[予報の日付-時刻] のディレクトリを作成
  • ./output/[予報の日付-時刻]/[予報の日付-時刻] のファイルを作成し、予報を書き込む

となります。

この気象庁の提供する気象情報は、政府標準利用規約に基づいて使用可能なものです。

実装は、以下の通りです。

#!/usr/bin/env dzx

import { $, cd, fs, io, path } from "https://deno.land/x/dzx@0.3.0/mod.ts";
import { datetime } from "https://deno.land/x/ptera/mod.ts";

interface Area {
  area: { name: string, code: string };
  weatherCodes: string[];
  weathers: string[];
  winds: string[];
  waves: string[];
}

interface ForcastRecord {
  time: string;
  forcast: string;
}

// データを取得
const forcast = await fetch(
  "https://www.jma.go.jp/bosai/forecast/data/forecast/130000.json"
);

// 東京のデータを抽出
const forcastJson = await forcast.json();
const timeDefines = forcastJson[0].timeSeries[0].timeDefines;
const tokyoForcast = forcastJson[0].timeSeries[0].areas.filter(
  (a: Area) => a.area.name === "東京地方"
)[0];

// 時刻と予報の組み合わせを作成
const records: ForcastRecord[] = [];
timeDefines.forEach((timeDefine: string, index: number) => {
  records.push({
    time: timeDefine,
    forcast: tokyoForcast.weathers[index],
  });
});

// 書き込み処理
const basename = path.fromFileUrl(path.dirname(import.meta.url));
await fs.ensureDir("./output");

records.forEach(async (r) => {
  const dirname = datetime(r.time).format("YYYYMMdd-HHmm");
  await fs.ensureDir(`./output/${dirname}`);
  cd(`${basename}/output/${dirname}`);
  Deno.writeTextFileSync(`${dirname}`, `${r.forcast}\n`);
});

./output ディレクトリの内容を確認すると次のようになっています。

$ tree output/
output/
|-- 20211115-1700
|   `-- 20211115-1700
|-- 20211116-0000
|   `-- 20211116-0000
`-- 20211117-0000
    `-- 20211117-0000

$  cat output/20211115-1700/20211115-1700
晴れ

dzxにより、各種APIなどのネットワーク資産を容易に利用したり、シェルの実行環境を JavaScript/TypeScript ライブラリ資産で強化していくことができます。
具体的には、シェルでシステムのログを取得して、JavaScript で整理と通知するシステムなど、双方のブリッジにするような使い方が可能です。

マークダウンを実行ファイルとして取り扱う

本家の zx にもある機能ですが、マークダウンの中のコードブロックを実行対象にすることができます。 最初に出した、ls するだけのコードをマークダウンの中で書いてみます。

[shell-3.md]

# 現在のディレクトリで `ls` をするディレクトリ

 実行するときは `dzx shell-3.md`

 ```typescript
 const output = await $`ls`;
 const files = output.stdout.split("\n").map((f) => console.log(f));
 ```

実際に実行すると以下のようになります。

$ dzx shell-3.ts
Check file:///usr/src/app/shell-1.ts
Dockerfile
docker-compose.yml
output
shell-1.ts
shell-2.ts
shell-3.ts

ドキュメントと実行スクリプトが分かれているので、改修内容にドキュメントの修正がついていっていないというのは、よくある話かと思います。 ドキュメント上のスクリプトを実行できれば、ある程度そういったことを招く原因を潰せるはずです。

ちなみに、マークダウンファイル中にコードブロックが複数ある場合には、まとめて実行されます。 例えば以下のようなファイルがあったとします。

[shell-4.md]

 # コードブロックが複数あったら?

 ```typescript
 console.log("code block 1");
 ```

 ```typescript
 console.log("code block 2");
 ```

実行すると次のようになります。

$ dzx shell-4.md
Check data:application/typescript,%0A%20%20%20%20%20%20%20%20%20%20%2F%2F%2F%20%3Creference%20path%3D%22https%3A%2F%2Fdeno.land%2Fx%2Fdzx%400.3.0%2Ftypes.d.ts%22%20%2F%3E%0A%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24.verbose%20%3D%20true%3B%0A%0A%24%60echo%20code%20block%201%60%0A%24%60echo%20code%20block%202%60%0A%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20
code block 1
code block 2

二つのコードブロックに、書かれたecho code block 1echo code block 2 が両方実行されているのがわかります。

dzx バンドル

dzx 用のスクリプトを Deno 環境向けの JavaScript にバンドルします。 先に紹介した shell-1.ts をバンドルして、実行してみます。

$ dzx bundle shell-1.ts > shell-1.js
$ deno run --allow-read --allow-env --allow-run=/bin/bash shell-1.js
Dockerfile
docker-compose.yml
shell-1.js
shell-1.ts
shell-2.ts
shell-3.md
shell-4.md

dzx コンパイル

dzx 用スクリプトをスタンドアロンバイナリにコンパイルすることもできます。 こちらも shell-1.ts を元にコンパイルして、実行してみます。

$ deno compile --allow-read --allow-env --allow-run=/bin/bash shell-1.js
$ ./shell-1
Dockerfile
docker-compose.yml
shell-1
shell-1.js
shell-1.ts
shell-2.ts
shell-3.md
shell-4.md

スタンドアロンバイナリを作成できることが、Node.js向け zxと Deno向けである dzxの利点の1つです。

まとめ

今回は、Google 製 OSS の「zx」インスパイアの Deno 向け シェルツール 「dzx」を紹介しました。 zx の良いところが生かされながら、さらに Deno のパーミッション管理や、標準で TypeScript を使えるといった魅力が上乗せされています。 その上で コンパイルでスタンドアロンバイナリを作成し、必ずしもDeno導入環境でなくても動作できるのもまた魅力です。

興味を持っていただけたら、ぜひ触ってみてください。

P.S.

採用情報
■募集職種
yumenosora.co.jp

カジュアル面談も随時開催中です
■お申し込みはこちら!
news.toranoana.jp

■ToraLab.fmスタートしました!
メンバーによるPodcastを配信中!
是非スキマ時間に聞いて頂けると嬉しいです。
anchor.fm
■Twitterもフォローしてくださいね!
ツイッターでも随時情報発信をしています
twitter.com