はじめに
v1.0.0 が出てからだいぶ月日が経ってしまいましたが、今更ながらちょっとお触りしてみました。
Denoってなんなん?
Deno
は、V8 JavaScript エンジンを使用した Rust 製の JavaScript および TypeScript 用のシンプルでモダンで安全なランタイムです!
って公式が謳っています😊
なんでDeno作ったん?
Deno
は JSConf EU 2018 での Ryan Dahl 氏による講演「Node.js に関する 10 の反省点」で発表されました。
その講演にて、Node.js の初期設計における後悔している点をいくつか挙げています。
- API の設計おいて非同期処理に使う promise を使用しないという選択をしたこと
- 古い GYP ビルドシステムを使用するようにしたこと
- パッケージ管理において設定ファイルの package.json と node_modules を採用したこと
- モジュールのインポートで拡張子を除外したこと
- index.js による魔法のようなモジュールの依存関係の解決を採用したこと
- V8 JavaScript エンジンのサンドボックス環境の破壊するような実装をしたこと
10 Things I Regret About Node.js - Ryan Dahl - JSConf EU - YouTube
※ "ディーノ"だと思ってましたけど発音的には"ディノ"っぽいですね
とりあえずお触りしてみる
色々あるんですね〜と思いつつ、触ってみないことにはわからないのでとりあえずお触りしてみます。
インストール
まずは肝心な Deno
をインストールします。
私は Mac
なので brew
で入れてゆきます。
% brew install deno
% deno -V
deno 1.14.2
実行してみる
サクッとサンプルコードを実行してみます。
% deno run https://deno.land/std/examples/welcome.ts
Download https://deno.land/std/examples/welcome.ts
Warning Implicitly using latest version (0.109.0) for https://deno.land/std/examples/welcome.ts
Download https://deno.land/std@0.109.0/examples/welcome.ts
Check https://deno.land/std/examples/welcome.ts
Welcome to Deno!
やった!歓迎されました!!🙌
で、これ。実は 2 回目以降は挙動が少々変わります。
% deno run https://deno.land/std/examples/welcome.ts
Welcome to Deno!
あれ?ダウンロードがない!
そうなんです。ダウンロードは初回実行時のみ行われローカルにキャッシュされて、2 回目以降はそのキャッシュを利用して実行されるのです。だから早い!
ちなみに、キャッシュディレクトリについては以下のコマンドで確認できます。
% deno info
また、Node.js
とは異なり、TypeScript
のコード(拡張子が ts)がそのまま実行されています。
Node.js
の場合は npm
で babel
や tsc
といったトランスパイラをインストールしてなんやらかんやら用意してトランスパイルを行う〜という必要がありましたが、Deno
では TypeScript
が標準サポートされているのでそういった面倒もないのです!素晴らしい!😊
サンプルコードを試してみる
これだけではちょっとわからないので、同じく公式サイトのローカルにサーバーを立てるサンプルコードを試してみます。
import { listenAndServe } from "https://deno.land/std@0.109.0/http/server.ts";
console.log("http://localhost:8000/");
listenAndServe(":8000", (req) => new Response("Hello World\n"));
あれ?npm init
しないの?パッケージ管理は?
そうなんです。Deno
には npm
がありません。
ということは、node_modules
も package.json
もありません。
「Node.js に関する 10 の反省点」で挙げられていた
- パッケージ管理において設定ファイルの package.json と node_modules を採用したこと
この点ですね。
Ryan Dahl 氏は、以下のコメントで Deno
自体がパッケージマネージャーの役割を担っていると言っています。
https://github.com/denoland/deno/issues/47#issuecomment-395405713
URLインポートの考え方は、denoが独自のパッケージマネージャーとして機能するというものです。補助的なツールの必要性を明確に避けたいと思います。
うーん、とはいえバージョン管理とか面倒になりそうな気が・・・。どうなるんだろう?手動?
それはあとで考えるとします。
一旦実行してみます。
% deno run server.ts
Download https://deno.land/std@0.89.0/http/server.ts
Download https://deno.land/std@0.89.0/io/bufio.ts
Download https://deno.land/std@0.89.0/http/_io.ts
Download https://deno.land/std@0.89.0/async/mod.ts
Download https://deno.land/std@0.89.0/_util/assert.ts
Download https://deno.land/std@0.89.0/bytes/mod.ts
Download https://deno.land/std@0.89.0/async/deferred.ts
Download https://deno.land/std@0.89.0/async/delay.ts
Download https://deno.land/std@0.89.0/async/mux_async_iterator.ts
Download https://deno.land/std@0.89.0/async/pool.ts
Download https://deno.land/std@0.89.0/http/http_status.ts
Download https://deno.land/std@0.89.0/textproto/mod.ts
Check file:///Users/Poo/file/to/path/server.ts
error: Uncaught PermissionDenied: Requires net access to "0.0.0.0:8000", run again with the --allow-net flag
const listener = Deno.listen(addr);
^
at deno:core/01_core.js:106:46
at unwrapOpResult (deno:core/01_core.js:126:13)
at Object.opSync (deno:core/01_core.js:140:12)
at opListen (deno:ext/net/01_net.js:38:17)
at Object.listen (deno:ext/net/01_net.js:204:17)
at serve (https://deno.land/std@0.89.0/http/server.ts:304:25)
at file:///Users/YuKi/scripts/deno/server.ts:2:11
あれ?動かない。なぜ・・・?
そうなんです。Deno はデフォルトではネットワークやシステムへのアクセスといったことができません。アクセスを許可するには実行時に明示的に対応するフラグを指定する必要があります。
(パーミッション一覧はこちら)
ちなみに、アクセス権限が付与されていない状態のことを Deno
的にはサンドボックスモード
と言うらしいです。
今回の場合は、エラーでも言われている通り --allow-net
(ネットワークのアクセス権限)を指定する必要があります。
では気を取り直してもう一度。
% deno run --allow-net server.ts
http://localhost:8000/
はい動きました!Hello World!!🙌
その他
テストのお話
Deno
ではテストモジュールも標準搭載してくれています。
公式のサンプルコードで試してみます。
import { assertEquals } from "https://deno.land/std@0.109.0/testing/asserts.ts";
// Simple name and function, compact form, but not configurable
Deno.test("hello world #1", () => {
const x = 1 + 2;
assertEquals(x, 3);
});
// Fully fledged test definition, longer form, but configurable (see below)
Deno.test({
name: "hello world #2",
fn: () => {
const x = 1 + 2;
assertEquals(x, 3);
},
});
テストは deno test
コマンドで実施します。
% deno test demo_test.ts
Download https://deno.land/std@0.109.0/testing/asserts.ts
Download https://deno.land/std@0.109.0/fmt/colors.ts
Download https://deno.land/std@0.109.0/testing/_diff.ts
Check file:///Users/YuKi/scripts/deno/demo_test.ts
running 2 tests from file:///Users/YuKi/scripts/deno/demo_test.ts
test hello world #1 ... ok (10ms)
test hello world #2 ... ok (5ms)
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out (64ms)
モジュール管理のお話
単一ファイルであればどうとでもなるけど、複数ファイルで URL インポートなんてしてたら管理も煩雑になるから、依存する外部ファイルはどこかに集約しておくのがいいのかな?
と思ったら、公式にありました。ですよねー、思ってた通りでした。
公式的には deps.ts
ファイルを配置する感じだそうです(開発のみの依存の場合は dev_deps.ts
)
export * as _ from 'https://deno.land/x/lodash@4.17.15-es/lodash.js';
とはいえ、バージョンアップは手動でやらないといけないのか…?
と思ったら、import maps
が v.1.8 からサポートされてるみたいです😊
スタイルガイドのお話
公式にスタイルガイドがあるので Deno らしいコードを書くためにも読んでおいた方が良さそう。
ここにもありますが、index.ts/index.js
的なファイル名は使うな!と言ってます。
「Node.js に関する 10 の反省点」で挙げられていた
- index.js による魔法のようなモジュールの依存関係の解決を採用したこと
これですね。
もしディレクトリにデフォルトのエントリーポイントが必要な場合は、mod.ts
ファイルを使用するとのことです。
さいごに
サンプルコードを動かした程度ですが、運用面も含めてなんとなく見えてきた感じはありました。
自前で簡単なスクリプトを書く度に npm init
やら nmp i
やらして、ディレクトリ配下に node_modules
を量産しなくて済むのは非常に良い!
対応されているモジュールがどれだけあるのかわかりませんが、これからどんどん Deno
と向き合っていこうと思います🤟