Deno ちょっと触ってみたメモ

2000年以前から「JavaScript」というモノに触れてきて、2015年ぐらいから Node.js というサーバサイド JS にも触れ始め、2017年ぐらいから TypeScript もかじってきた。JS は僕が一番得意な言語で、フロントエンドとバックエンドを同じ言語で書ける効率の良さは、他ではなかなか味わえない。Node.js は環境構築も容易で、Java や Python なんかと比べるとつまづきポイントがないと思っている。

さて、そんな Node.js だが、開発者のライアン・ダールさんはいくつか後悔があるとして、最近は Deno (ディノ) と呼ばれる新しいランタイムを開発している。コレがなかなか良かったので、概要だけ紹介しておく。

目次

インストールがめちゃ楽

Deno 本体はシングルバイナリで配信されており、極端にいえばシングルバイナリを curl で落としてきて PATH の通っているところに配置すればインストールが完了する。公式サイトにもインストール用のワンライナーが各 OS 向けに用意されており、Homebrew や Chocolatey にも対応している。

複数バージョンの管理も「asdf」で簡単だ。

Deno 自体が Rust 製なので、Cargo (Rust のパッケージ管理ツール) からもインストールできる。

TypeScript が設定なしで書ける

$ npm i typescripttsconfig.json も要らない。ただ .ts ファイルを書いて $ deno run example.ts と実行すれば、TypeScript が実行できる。イメージは ts-nodets-node-dev が内蔵されている感じだと思うが、tsconfig.json すらなくても動かせるのが素晴らしい。

package.json はなくなった

package.jsonnode_modules/ はなくなった。外部パッケージのバージョン情報などは、全て import 時に指定する URL で表現する。

import { listenAndServe } from 'https://deno.land/std@0.113.0/http/server.ts';

await listenAndServe(':8080', (request: Request): Response => {
  return new Response('Hello World', { status: 200 });
});

例えば、↑ こんなコードが動く。サラッと「トップレベル await」が使えているのも素敵だが、注目は1行目。std@0.113.0 という風に、パッケージ名とバージョン番号込みで URL インポートしている。

依存パッケージは $ deno run で実行する際に、マシン内にキャッシュがなければ初回ダウンロードする動き。プロジェクトローカルに node_modules/ のようなディレクトリは生成されず、マシン全体で依存パッケージのキャッシュが共有されている。

「実行時に初めてダウンロードするの?$ npm install で予めローカルにダウンロードしておかないと何か不安…」って思ってたのだが、npm もよくよく考えれば npmjs.com が配信するパッケージをダウンロードしているだけだし、npmjs.com というドメインや URL がそこに存在することを前提に成り立っていた仕組みではあった。さらに考えれば、Yum とか APT とか、各種言語のパッケージ管理ツールなんかも、内部的にはレジストリの URL を管理しているし、やっていることは変わらないんだなと。権威的なサーバやサイトドメインがずっと存在すること前提に作られてたんだよなぁ、って思って、なんとなく理解した。

サードパーティライブラリを配信する、いわば npmjs.com 的な立ち位置にある deno.land/x/ も、Deno 製のサーバで元気に稼動しているそうで、まぁこの配信方法がすぐさま潰れてなくなることは考えにくいだろうし、npmjs.com が安心できて deno.land/x/ は安心できないと思う差はないだろうな、と思い込むことにした。w

ちなみに、この std というのは Deno が提供する「標準ライブラリ群」なのだが、実際は Deno 本体が内蔵する機能が充実していて、やろうと思えば一つの import 文もなしに HTTP サーバを実装することもできる。

開発支援ツールが内蔵されている

Deno のサブコマンドを見ると、色んな機能が標準で内包されていて素晴らしかった。

ホットリロードできるから BrowserSync とか要らないし、フォーマッタ内蔵で Prettier も要らないし、Linter 内蔵で ESLint のつらみも消える。ドキュメンテーション生成も簡単だし、ユニットテストツールが内蔵されていて Jest だ Mocha だで揉めることもない。lcov 形式のカバレッジレポートも吐ける。

ビルドの仕組みも当然内包されているうえに、Go 言語や Rust なんかでもよくある「シングルバイナリ」へのビルドもできる。これらがサードパーティライブラリの npm install なしに、Deno 本体だけで実現されているのが素晴らしい。今までの苦労はなんだったんだ感。

多少自分の思う流儀と違うところがあっても基本は Deno 任せで扱うことで、開発に注力できるというモノだ。

先程 tsconfig.json も不要、と紹介したが、諸々の設定をしたくなったら deno.json という設定ファイルを書くことで対応できる。

動作は安定しているが、絶賛開発中で落ち着きがない

現状、Deno 製の Web サーバが deno.land/x/ で元気に動いていたり、Lambda など FaaS 界隈でも Deno のサポートが始まったりして、プロダクションレベルの運用も対応はできそうな感じ。import 文中にバージョン番号込みで残るので、最新版では破壊的な変更があったとしても、後方互換性はしばらく保たれそう。

ただ、Deno 本体が現在絶賛開発進展中で、std の API もコロコロ変わっていたりするので、「落ち着きがない」印象。ドキュメントが少ない中で、公式ドキュメントから拾い上げるスキルがないと、運用を見越した利用は厳しそう。

内部は Node.js と別モノで、require() を使って npm パッケージを利用していたような既存のコードは、基本的に流用できない。一応、「Node.js 互換モード」の開発が進んではいるが、まだ完璧ではない模様。というか Node.js 時代のつらみを捨てるために Deno が生まれたワケだし、Node.js と同じことをやろうと思わない方が良いだろうな。

最後に、「スタイルガイド」のページで「ファイル名はアンダースコア区切りにしような」と書かれてて、deno.land/x/ に公開できるパッケージ名なんかもスネークケースしか許容されていなくて、なんで?と思ったので質問してみたのだが、「まぁなんかそういうガイドラインがあった方が良いだろっていうだけだから気にすんな」って回答しかなかった。

個人的には Node.js 時代にハイフンケースでファイルを扱ってきたし、例えば Angular CLI なんかのようなツールも、ハイフンケースのファイル名を出力することが多かった。それに、ドメインや URL の世界ではアンダースコアを許容しないような RFC も存在したりして、import 文に URL を載せるなら、アンダースコアが出てくるよりはハイフンの方がしっくりくるだろ、とか思っていた。だからなんでスネークケースがオススメ~的なガイドになったのかな?と気になったのだが、理由は定かではない。

…こうした諸々の状況を加味すると、Deno の中で「パッケージ名」や「ファイル名」って、「URL」として登場することが多いので、スネークケースよりもハイフンケースの方が推奨されるべきでは?と思うのだが、なんか違うらしい。

ということで以上。今すぐ Node.js を完全に代替する存在にはならないかもしれないが、Functions 単位とかで小さく使い始めるのは結構アリかもしれない。