Denoとはなんなのか今更ながらちょっとお触りしてみた

# はじめに

v1.0.0 が出てからだいぶ月日が経ってしまいましたが、今更ながらちょっとお触りしてみました。

## Denoってなんなん？

`Deno` は、V8 JavaScript エンジンを使用した Rust 製の JavaScript および TypeScript 用のシンプルでモダンで安全なランタイムです！
って[公式](https://deno.land/)が謳っています😊

## なんでDeno作ったん？
`Deno` は JSConf EU 2018 での Ryan Dahl 氏による講演「[Node.js に関する 10 の反省点](https://www.youtube.com/watch?v=M3BM9TB-8yA)」で発表されました。
その講演にて、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](https://www.youtube.com/watch?v=M3BM9TB-8yA)

※ "ディーノ"だと思ってましたけど発音的には"ディノ"っぽいですね

# とりあえずお触りしてみる

色々あるんですね〜と思いつつ、触ってみないことにはわからないのでとりあえずお触りしてみます。

## インストール

まずは肝心な `Deno` を[インストール](https://deno.land/#installation)します。
私は `Mac` なので `brew` で入れてゆきます。

```bash
% brew install deno
% deno -V
deno 1.14.2
```

## 実行してみる

サクッと[サンプルコード](https://deno.land/#getting-started)を実行してみます。

```bash
% 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 回目以降は挙動が少々変わります。

```bash
% deno run https://deno.land/std/examples/welcome.ts
Welcome to Deno!
```

あれ？ダウンロードがない！

そうなんです。ダウンロードは初回実行時のみ行われローカルにキャッシュされて、2 回目以降はそのキャッシュを利用して実行されるのです。だから早い！

ちなみに、キャッシュディレクトリについては以下のコマンドで確認できます。

```bash
% deno info
```

また、`Node.js`とは異なり、`TypeScript` のコード（拡張子が ts）がそのまま実行されています。
`Node.js`の場合は `npm` で `babel` や `tsc` といったトランスパイラをインストールしてなんやらかんやら用意してトランスパイルを行う〜という必要がありましたが、`Deno`では `TypeScript` が標準サポートされているのでそういった面倒もないのです！素晴らしい！😊

## サンプルコードを試してみる

これだけではちょっとわからないので、同じく公式サイトのローカルにサーバーを立てる[サンプルコード](https://deno.land/#getting-started)を試してみます。

```typescript:server.ts
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が独自のパッケージマネージャーとして機能するというものです。補助的なツールの必要性を明確に避けたいと思います。

うーん、とはいえバージョン管理とか面倒になりそうな気が・・・。どうなるんだろう？手動？
それはあとで考えるとします。

一旦実行してみます。

```bash
% 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 はデフォルトではネットワークやシステムへのアクセスといったことができません。アクセスを許可するには実行時に明示的に対応するフラグを指定する必要があります。
（[パーミッション一覧はこちら](https://deno.land/manual/getting_started/permissions#permissions-list)）

ちなみに、アクセス権限が付与されていない状態のことを `Deno` 的には`サンドボックスモード`と言うらしいです。

今回の場合は、エラーでも言われている通り `--allow-net`（ネットワークのアクセス権限）を指定する必要があります。

では気を取り直してもう一度。

```bash
% deno run --allow-net server.ts
http://localhost:8000/
```

はい動きました！Hello World！！🙌

# その他

## テストのお話
`Deno` ではテストモジュールも標準搭載してくれています。
[公式](https://deno.land/manual/testing#writing-tests)のサンプルコードで試してみます。

```typescript:demo_test.ts
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` コマンドで実施します。

```bash
% 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 インポートなんてしてたら管理も煩雑になるから、依存する外部ファイルはどこかに集約しておくのがいいのかな？
と思ったら、[公式](https://deno.land/manual/examples/manage_dependencies)にありました。ですよねー、思ってた通りでした。

公式的には `deps.ts` ファイルを配置する感じだそうです（開発のみの依存の場合は `dev_deps.ts`）

```typescript:deps.ts
export * as _ from 'https://deno.land/x/lodash@4.17.15-es/lodash.js';
```

とはいえ、バージョンアップは手動でやらないといけないのか…？
と思ったら、`import maps` が v.1.8 からサポートされてるみたいです😊

## スタイルガイドのお話

公式に[スタイルガイド](https://deno.land/manual/contributing/style_guide)があるので Deno らしいコードを書くためにも読んでおいた方が良さそう。
ここにもありますが、`index.ts/index.js` 的なファイル名は使うな！と言ってます。

「Node.js に関する 10 の反省点」で挙げられていた

> * index.js による魔法のようなモジュールの依存関係の解決を採用したこと

これですね。
もしディレクトリにデフォルトのエントリーポイントが必要な場合は、`mod.ts` ファイルを使用するとのことです。

# さいごに

サンプルコードを動かした程度ですが、運用面も含めてなんとなく見えてきた感じはありました。
自前で簡単なスクリプトを書く度に `npm init` やら `nmp i` やらして、ディレクトリ配下に `node_modules` を量産しなくて済むのは非常に良い！
対応されているモジュールがどれだけあるのかわかりませんが、これからどんどん `Deno` と向き合っていこうと思います🤟