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 typescript
も tsconfig.json
も要らない。ただ .ts
ファイルを書いて $ deno run example.ts
と実行すれば、TypeScript が実行できる。イメージは ts-node
や ts-node-dev
が内蔵されている感じだと思うが、tsconfig.json
すらなくても動かせるのが素晴らしい。
package.json
はなくなった
package.json
や node_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 インポートしている。
- oak@v9.0.1 | Deno
- サードパーティライブラリの例
- サードパーティライブラリは
deno.land/x/
で配信されている import { Application } from 'https://deno.land/x/oak/mod.ts';
のようにバージョン指定しなければ、最新版 (本稿執筆時点だと v9.0.1) にリダイレクトしてダウンロードされた
依存パッケージは $ 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 のサブコマンドを見ると、色んな機能が標準で内包されていて素晴らしかった。
deno run --watch
: ホットリロードdeno fmt
: コードフォーマットdeno lint
: Linterdeno doc
: ドキュメンテーション生成deno test
: ユニットテスト実行deno coverage
: カバレッジレポート生成 (deno test
と併用)deno bundle
: シングル JS ファイルにビルドdeno compile
: シングルバイナリ生成
ホットリロードできるから 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/
に公開できるパッケージ名なんかもスネークケースしか許容されていなくて、なんで?と思ったので質問してみたのだが、「まぁなんかそういうガイドラインがあった方が良いだろっていうだけだから気にすんな」って回答しかなかった。
- Why Deno prefers underscores to dash? · Discussion #12415 · denoland/deno
- Restricted characters in package names · Issue #1170 · denoland/deno
- コチラも「
deno.land/x/
はそういうルールにしてるけど、各自ハイフン入りの URL を使ってもいいよ」的な回答のみ
- コチラも「
個人的には Node.js 時代にハイフンケースでファイルを扱ってきたし、例えば Angular CLI なんかのようなツールも、ハイフンケースのファイル名を出力することが多かった。それに、ドメインや URL の世界ではアンダースコアを許容しないような RFC も存在したりして、import
文に URL を載せるなら、アンダースコアが出てくるよりはハイフンの方がしっくりくるだろ、とか思っていた。だからなんでスネークケースがオススメ~的なガイドになったのかな?と気になったのだが、理由は定かではない。
- ちなみに、スネークケースの文字列はダブルクリックすると全体が選択され、ケバブケースの文字列は単語だけが選択される。コレは、アンダースコアが「単語を連結するモノ」、ハイフンが「単語を区切るモノ」と認識されているから、というところが由来らしい
- ドメインや URL については、RFC が複数存在して、現状アンダースコアが問題になることは少ないが、「ハイフン」を使う方がより安全だし、Google や Microsoft のガイドでは、URL についてはハイフン区切りが推奨されている
- HTML 内の ID・Class・
data
属性などもハイフンケースが前提。CSS の部分一致セレクタが効くのはハイフンケースだし、data
属性値は HTML ⇔ JS 間でハイフンケースからキャメルケースへの自動変換が行われる - JS の中では、ハイフンケースは変数として使えず、スネークケースなら変数として認識される。ハイフン記号が算術演算の「マイナス」として扱われるので、プログラミング言語のなかでハイフンケースが出てこないのは理解できる
- こうした挙動を前提にして、RESTful な設計においては、リクエストパラメータやレスポンスボディ (JSON) のプロパティ名はスネークケースで書かれることが多い。JS で JSON パースした後、ドット記法でアクセスしやすいのはスネークケースだからだ
…こうした諸々の状況を加味すると、Deno の中で「パッケージ名」や「ファイル名」って、「URL」として登場することが多いので、スネークケースよりもハイフンケースの方が推奨されるべきでは?と思うのだが、なんか違うらしい。
- Web の世界から離れて、PC 内の純粋な「ファイル名」で考えると、ハイフンが Bash コマンドのオプションと混同されたりしないように、アンダースコア区切りがより好まれた時代もあるみたい。現在でもスペース区切りのファイル名は避けられるように、Linux や Bash 寄りで考えると、アンダースコア区切りの方が多そうな印象は何となく分かる
- Deno は Rust 製。Rust のスタイルガイドを見ると、スネークケースを使うとされている箇所が多い。ちょっと Python っぽい。もしかすると Rust のスタイルに合わせたいとか、内部的に都合が良いとかいう思いがあったのかもしれない
ということで以上。今すぐ Node.js を完全に代替する存在にはならないかもしれないが、Functions 単位とかで小さく使い始めるのは結構アリかもしれない。