NodeからDenoへ

# はじめに
現在[Node.js](https://nodejs.org/ja/)はさまざまな用途で多く使われている。しかし、Node.jsの製作者Ryan Dahl(ライアン・ダール)によるとNode.jsには[10の反省点](https://www.youtube.com/watch?v=M3BM9TB-8yA&t=1319s)があると言う。そして、Ryan Dahlはそれらを改善した言語である[Deno](https://deno.land/)を開発した。この記事ではDenoの紹介と導入を記述する。Denoのインストール方法などはMacOSでのみ動作確認した。他OSの場合で動かない場合は公式ドキュメントを参照していただきたい。

# Deno
> Denoは、V8を使用し、Rustで構築された、シンプルでモダンかつセキュアなJavaScriptおよびTypeScript用ランタイムです。
公式サイトより引用(DeepLより翻訳)

DenoはNodeでの開発経験を生かしたものとなっている。DenoはV8、Rust、Tokioに基づいたランタイムである。また、セキュリティと生産性に重きを置いており、大きく5つの特徴がある。

- 明示的に有効にしない限り、ファイル、ネットワーク、環境変数へのアクセスはできず、デフォルトでも安全
- TypeScriptをそのまま扱える
- 単一の実行形式ファイル
- 依存関係インスペクタ(deno info)やフォーマッター(deno fmt)などの便利な機能が組み込まれている
- Denoで動作保証されたレビュー済みの標準ライブラリーがある

## Nodeの反省点
詳しくは説明しないが、以下の反省点を省みてDenoは作成された。

- 非同期処理にpromiseを使用しなかったこと(2009年に一時的に追加されたが)
- ビルドシステムにGYPを使用したこと
- node_modulesを採用したこと
- package.jsonを採用したこと
- 拡張子を除外したこと
- index.jsによる依存関係の解決を採用したこと
- V8のサンドボックス環境の破壊するような実装をしたこと

## Nodeとの比較
- TypeScriptをデフォルトサポート
- npmを使用しない(node_modulesやpackage.jsonなどを使わない)
- 非同期処理にpromissを採用し、APIを再設計
- モジュールの扱いをES Module方式に統一
- ファイル、ネットワーク、環境変数へのアクセスに許可が必要
- ブラウザとの互換性向上

これらの変更によって、Nodeの反省点を改善や生産的で安全なスクリプト環境を達成できたが、Nodeで培われた多くの資産が使えなくなるという欠点もある。

## Denoのインストール
インストールは好みのOSで好みの方法で行うと良い。ここでは公式に書かれている方法を紹介する。[リリースページ](https://github.com/denoland/deno/releases)からのダウンドードも可能だ。
### MacOS

- Shell

```shell
$ curl -fsSL https://deno.land/x/install/install.sh | sh
```
- Homebrew

```shell
$ brew install deno
```

### Windows
- PowewShell

```shell
$ iwr https://deno.land/x/install/install.ps1 -useb | iex
```
- Chocolatey

```shell
$ choco install deno
```

- Scoop

```shell
scoop install deno
```
### Linux

- Shell

```shell
$ curl -fsSL https://deno.land/x/install/install.sh | sh
```

### インストールのテスト(バージョン確認)
インストールをテストするために`deno --version`または`deno -V`を実行し、バージョンが出力されることを確認する。

```shell:例
deno 1.14.1
```

### アップデート
denoのバージョンは`deno upgrade`によって最新のバージョンに更新される。また、`deno upgrade --version x.y.z`でバージョンx.y.zにアップデートされる。

### 対話型
`deno`を実行すると対話型でDenoが使える。簡単な計算や構文の確認時などに便利。

## Hello World
次にDenoを実際に動かしてみる。ここでは公式で紹介している[https://deno.land/std/examples/welcome.ts](https://deno.land/std/examples/welcome.ts)を実行してみる。ファイルの内容はアクセスすれば分かるが、`console.log("Welcome to Deno 🦕")`だ。

```shell
deno run https://deno.land/std/examples/welcome.ts
```
で実行することができ、`Welocome to Deno 🦕`と出力される。このように細かい設定などをしなくても息をするかのようにtsファイルを実行することができる。

## パーミッション
Denoの特徴として「明示的に有効にしない限り、ファイル、ネットワーク、環境変数へのアクセスはできず、デフォルトでも安全」がある。ここではファイル、ネットワーク、環境変数などにアクセスする方法を紹介する。アクセスの許可はファイル実行時に与えることができる。
通常の実行はこのように行う。

```shell
deno run https://deno.land/std/examples/welcome.ts
```
全てのパーミッションを許可するときはこのように行う。

```shell
deno run --allow-all https://deno.land/std/examples/welcome.ts
```
このように`--allow-hoge`のようにhogeに指定したもののアクセスを許可することができる(全て許可は-Aでも可)。

### パーミッションリスト
- `--allow-all`または`-A`で全て
- `--allow-read={許可するファイル}`でファイルの読み込み、`--allow-write{許可するファイル}`でファイルの書き込み(=以降無しで全てのファイル)
- `--allow-net={許可するサイト}`でネットワークアクセス(=以降無しで全てのサイト)
- `--allow-env`で環境アクセス
- `--allow-plugin`でプラグインの読み込み
- `--allow-run`でサブプロセスの実行
- `--allow-hrtime`で高性能の時間測定

### 実例
ネットワークアクセスを許可しなければサイトにアクセスできないことを確認後、許可することでアクセスできることを確認する。
準備のため以下のような適当なファイルを用意した。

```ts:fetch.ts
const result = await fetch("https://deno.land/");
console.log(result);
```
これを通常通り実行すると以下のようなエラーが出る。

```shell
$ deno run fetch.ts
Check file:fetch.ts
error: Uncaught PermissionDenied: Requires net access to "deno.land", run again with the --allow-net flag
const result = await fetch("https://deno.land/");
                     ^
    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 opFetch (deno:ext/fetch/26_fetch.js:65:17)
    at mainFetch (deno:ext/fetch/26_fetch.js:220:61)
    at deno:ext/fetch/26_fetch.js:443:11
    at new Promise (<anonymous>)
    at fetch (deno:ext/fetch/26_fetch.js:412:15)
    at file:fetch.ts:1:22
```
次に、エラー文にも書かれているように`--allow-net`をつけてネットワークアクセスを許可した状態で実行する。

```shell
$ deno run --allow-net fetch.ts
Response {
  body: ReadableStream { locked: false },
  bodyUsed: false,
  headers: Headers {
  "access-control-allow-origin": "*",
  age: "5369",
  "cache-control": "public, max-age=0, must-revalidate",
  connection: "keep-alive",
  "content-disposition": 'inline; filename="index"',
  "content-type": "text/html; charset=utf-8",
  date: "EEE, dd mmm yyyy HH:mm:ss GMT",
  etag: 'W/"7d0c68ff2b382b4f36bd27cfd9c447a2df012a6e7183c896295567b4a4e6a102"',
  server: "deploy/asia-northeast1-a",
  "strict-transport-security": "max-age=63072000; includeSubDomains; preload",
  "transfer-encoding": "chunked",
  "x-vercel-cache": "HIT",
  "x-vercel-id": "hnd1::9t84z-1633395860304-259194a942bb"
},
  ok: true,
  redirected: false,
  status: 200,
  statusText: "OK",
  url: "https://deno.land/"
}
```
上記の結果から`--allow-net`をつけることでアクセス可能であることが確認できた。もちろんfetch先の[https://deno.land/](https://deno.land/)だけを指定して`--allow-net=deno.land`としても実行可能だ。


# 参考
https://deno.land/manual