【2020年】Denoに加わった変更点のまとめ

## はじめに

Deno Advent Calendar 2020 19日目の記事です🦕

この1年でDenoには沢山の変更や改善が行われました。
この記事では、2020年にDenoに加わった変更点をまとめます。

## 要約

* [deno@v1がメジャーリリースされた](#denov1がメジャーリリースされた)
* [denoの本体と`deno_std`が独立してバージョン管理されるようになった](#denoの本体とdeno_stdが独立してバージョン管理されるようになった)
* [`--unstable`オプションが導入された](#--unstableオプションが導入された)
* [`deno doc`コマンドが実装された](#deno-docコマンドが実装された)
* [リンター(`deno lint`コマンド)が実装された](#リンターdeno-lintコマンドが実装された)
* [バンドラ(`deno bundle`コマンド)が大幅に改善された](#バンドラdeno-bundleコマンドが大幅に改善された)
* [テストランナー(`deno test`コマンド)が強化された](#テストランナーdeno-testコマンドが強化された)
* [型チェックがスキップできるようになった(`--no-check`)](#型チェックがスキップできるようになった--no-check)
* [ファイルウォッチャー(`--watch`)が実装された](#ファイルウォッチャー--watchが実装された)
* [アプリケーションをシングルバイナリ形式へパッケージングできるようになった(`deno compile`コマンド)](#アプリケーションをシングルバイナリ形式へパッケージングできるようになったdeno-compileコマンド)
* [Language Serverが組み込まれた(`deno lsp`コマンド)](#language-serverが組み込まれたdeno-lspコマンド)
* [Web標準との互換性が向上した](#web標準との互換性が向上した)
* [`std/wasi`モジュールが追加された](#stdwasiモジュールが追加された)

## deno@v1がメジャーリリースされた

2020/05/13にdenoのv1がメジャーリリースされました🥳

[初回のコミット](http://github.com/denoland/deno/commit/f7c5e19081920f59ce2006d3255a58fb611b6a17)からおよそ2年後の出来事のようです。

このメジャーリリースに合わせて、[公式のブログポスト](https://deno.land/posts/v1)が投稿されたり、[Denoパーカー](https://deno.land/v1/hoodie)(※現在は売り切れ中です🥺)が発売されるなど、様々なことがありました。

## denoの本体と`deno_std`が独立してバージョン管理されるようになった

denoには標準モジュールとして[deno_std](https://deno.land/std)が存在します。

元々、この`deno_std`とdenoの本体(CLI)はバージョンを一致させる方針で管理されていました。(例: `deno@v0.40.0`に対応する`deno_std`のバージョンはv0.40.0)

しかし、deno v1.0.0のメジャーリリースの直前にこの方針が変更され、CLIと`deno_std`は独立したバージョンを持つように変更されました。(例: `deno@v1.6.1`に対応する`deno_std`のバージョンは0.81.0)

### なぜ`deno_std`のバージョン管理方式が変更されたの?

これは`deno_std`の安定性などが理由です。

denoのCLIとは異なり、**deno_stdはまだ安定版がリリースされていません。**

### いつ`deno_std`の安定版がリリースされるの?

現在、[Deno Standard Library Working Group](https://github.com/denoland/deno/discussions/8405)という**deno_stdを安定化させるための取り組み**が開始されています。(詳しくは @kt3kさんの「[Deno Standard Library Working Group について](https://qiita.com/kt3k/items/eb18a34e667395f67b3a)」の記事を参照ください)

そのため、推測ではありますが、2021年には`deno_std`の安定版がリリースされると思います。

## `--unstable`オプションが導入された

Denoのメジャーリリース以降、「安定した機能」と「不安定な機能」を分離するために、`--unstable`オプションが導入されました。

Denoにおいて不安定な機能を使用する際は、`--unstable`オプションを指定する必要があります。

例えば、次のように不安定な機能(`Deno.hostname()`)を使用したコードがあったとします。

```typescript:sample.ts
console.log(Deno.hostname());
```

このファイルを実行する際は、`--unstable`オプションを付与する必要があります。

```shell
# --unstableがないとエラー!!
$ deno run --allow-env sample.ts
error: TS2339 [ERROR]: Property 'hostname' does not exist on type 'typeof Deno'. 'Deno.hostname' is an unstable API. Did you forget to
 run with the '--unstable' flag?
console.log(Deno.hostname());


# --unstableをつけるとうまくいく
$ deno run --unstable --allow-env sample.ts
```

不安定なAPIの一覧については[こちら](https://doc.deno.land/builtin/unstable)から参照できます。

## `deno doc`コマンドが実装された

[Deno@v0.38.0](https://github.com/denoland/deno/releases/tag/v0.38.0)にて`deno doc`コマンドが実装されました。
これにより、**ソースコードの内容からAPIドキュメントを出力できる**ようになりました。
また、このコマンドの実装にともない、[公式のドキュメンテーションサイト](https://doc.deno.land)も作成されました。

### 使い方

#### リモートモジュールのAPIドキュメントを出力

試しに`deno_std`のTOMLモジュールのドキュメントを出力してみます。以下のように実行すると、関数やメソッドのシグネチャ及びJSDocの内容を元に、APIドキュメントが生成されます。

```shell
$ deno doc https://deno.land/std@0.81.0/encoding/toml.ts
Defined in https://deno.land/std@0.81.0/encoding/toml.ts:732:0

function parse(tomlString: string): Record<string, unknown>
  Parse parses TOML string into an object.
  @param tomlString

Defined in https://deno.land/std@0.81.0/encoding/toml.ts:724:0

function stringify(srcObj: Record<string, unknown>): string
  Stringify dumps source object into TOML string and returns it.
  @param srcObj
```

#### ローカルモジュールのAPIドキュメントを出力

ローカルファイルのドキュメントを出力したいときは、そのファイルへのパスを指定します。また、引数としてクラス名または関数名を指定すると、それに関するドキュメントのみを出力することもできます。

```shell
# mod.tsからconnect関数のドキュメントを出力
$ deno doc ./mod.ts connect
Defined in file:///home/uki00a/ghq/github.com/uki00a/redis/redis.ts:2098:0

async function connect(options: RedisConnectOptions): Promise<Redis>
  Connect to Redis server
  @param options
  @example
   const conn = connect({hostname: "127.0.0.1", port: 6379}) // -> TCP, 127.0.0.1:6379
   const conn = connect({hostname: "redis.proxy", port: 443, tls: true}) // -> TLS, redis.proxy:443
```

#### ビルトインAPIのドキュメントを出力

ビルトインAPIのドキュメントを出力したいときは、`--builtin`オプションを付与します。

```shell
$ deno doc --builtin Deno.version
Defined in lib.deno.d.ts:2058:2

const version: { deno: string; v8: string; typescript: string; }
  Version related information.
```

#### APIドキュメントをJSON形式で出力

`--json`オプションを指定すると、JSON形式で出力されます。

```shell
$ deno doc --json io.ts
[
  {
    "kind": "typeAlias",
    "name": "Status",
    "location": {
      "filename": "file:///home/uki00a/ghq/github.com/uki00a/redis/
io.ts",
      "line": 7,
      "col": 0
    },
    "jsDoc": null,
    "typeAliasDef": {
      "tsType": {
        "repr": "string",
        "kind": "keyword",
        "keyword": "string"
      },
      "typeParams": []
    }
  },
  ... 省略 ...
]
```

## リンター(`deno lint`コマンド)が実装された

[Deno@v1.1.0](https://github.com/denoland/deno/releases/tag/v1.1.0)にて`deno lint`コマンドが実装されました
これにより、deno単独でソースコードのlintができるようになりました。

また、この`deno lint`コマンドは**Rustで実装されており、とても速い**のが特徴です。

```shell
$ deno lint --unstable chrome.ts
(no-inferrable-types) inferrable types are not allowed
              let result: string = "";
                  ^^^^^^^^^^^^^^^^^^^
    at /home/uki00a/ghq/github.com/uki00a/carol/chrome.ts:520:18

    hint: Remove the type, it is easily inferrable

(no-inferrable-types) inferrable types are not allowed
              let error: string = "";
                  ^^^^^^^^^^^^^^^^^^
    at /home/uki00a/ghq/github.com/uki00a/carol/chrome.ts:521:18

    hint: Remove the type, it is easily inferrable

Found 2 problems
Checked 1 file
```

`deno lintコマンド`の詳細については、「[Deno の組み込みリンター "deno_lint" の紹介 〜 ESLintの代替としても](https://zenn.dev/magurotuna/articles/66618f26475702)」の記事がよくまとまっていておすすめです。興味があればぜひご覧ください:exclamation:

## バンドラ(`deno bundle`コマンド)が大幅に改善された

Denoはバンドラ(`deno bundle`コマンド)を組み込んでいます。

このバンドラが[v1.5](https://deno.land/posts/v1.5)において、大幅に強化されました。

* ツリーシェイキングに対応
* バンドラが`tsc`から`swc_bundler`(Rust製)に変更された(その結果、型チェックをスキップすれば、v1.4以前と比較してパフォーマンスが**15倍**にまで向上したようです)
* dynamic importに対応

その他にも、[denopack](https://github.com/denofn/denopack)のようなサードパーティのバンドラや[Aleph.js](https://github.com/alephjs/aleph.js)や[Dext.ts](https://github.com/lucacasonato/dext.ts)のようなSSR/SSGフレームワークも登場し、**Denoにおけるフロントエンド周りのエコシステムも徐々に充実しつつある**印象を受けます。

## テストランナー(`deno test`コマンド)が強化された

Denoはテストランナー(`deno test`コマンド)を組み込んでいます。
このテストランナーにも様々な変更や改善が実施されました。

### 簡単な使い方

`deno test`コマンドの使い方について、簡単に説明します。
まず、次のようなテストファイルを用意します。

```typescript:test.ts
import { assertEquals } from "https://deno.land/std@0.81.0/testing/asserts.ts";

function add(a: number, b: number): number {
  return a + b;
}

Deno.test("add", () => {
  assertEquals(add(1, 2), 3);
});
```

そして、コマンドラインから以下のコマンドを実行すると、テストが実行されます。

```shell
$ deno test
running 1 tests
test add ... ok (3ms)

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out (3ms)
```

### 主な変更点

#### カバレッジレポートのサポート

[Deno@v1.4.0](https://github.com/denoland/deno/releases/tag/v1.4.0)からカバレッジレポートの出力がサポートされました。

```shell
$ deno test -A --coverage --unstable
Check file:///home/uki00a/ghq/github.com/uki00a/carol/$deno$test.ts
running 9 tests
test Application#evaluate ... ok (92ms)
test Application#exposeFunction ... ok (106ms)
test Application#onExit ... ok (80ms)
test Application#serveFolder ... ok (200ms)
test Application#serveFolder with prefix ... ok (213ms)
test custom executablePath ... ok (4ms)
test Chrome#evaluate ... ok (118ms)
test Chrome#load ... ok (110ms)
test Chrome#bind ... ok (97ms)

test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out (1021ms)

cover file:///home/uki00a/ghq/github.com/uki00a/carol/deps.ts ... 100.000% (11/11)
cover file:///home/uki00a/ghq/github.com/uki00a/carol/test_util.ts ... 78.261% (54/69)
  29 |                     assert(err instanceof Deno.errors.ConnectionReset);
  30 |                 }
  31 |             }
  32 |         },
-----|-----
  49 |                     assert(err instanceof Deno.errors.ConnectionReset);
  50 |                 }
  51 |             }
  52 |         },
  53 |     });
  54 | }
-----|-----
  64 |                 assert(err instanceof Deno.errors.ConnectionReset);
  65 |             }
  66 |         },
  67 |     });
  68 | }

... 省略 ...

cover file:///home/uki00a/ghq/github.com/uki00a/carol/logger.ts ... 81.250% (13/16)
  10 |         return console;
  11 |     }
  12 |     else {
```

詳細については[Deno v1.4.0で実装された新機能の紹介](https://qiita.com/uki00a/items/91658e83c47b187b59d3)の記事を参照ください。

#### リソースリークを検知するようになった

例えば、次のようにリソースの開放が適切に行われていないコードがあったとします。

```typescript
async function doSomething(): Promise<void> {
  const _file = await Deno.open("mod.ts"); // _fileがクローズされていない!!!
}

Deno.test("...", async () => {
  await doSomething();
});
```

このテストを実行すると、次のようなエラーが発生します。

```shell
test ... ... FAILED (4ms)

failures:

...
AssertionError: Test case is leaking resources.
Before: {
  "0": "stdin",
  "1": "stdout",
  "2": "stderr"
}
After: {
  "0": "stdin",
  "1": "stdout",
  "2": "stderr",
  "3": "fsFile"
}

Make sure to close all open resource handles returned from Deno APIs before
finishing test case.
    at assert (deno:cli/rt/06_util.js:33:13)
    at Object.resourceSanitizer [as fn] (deno:cli/rt/40_testing.js:81:7)
    at async TestRunner.[Symbol.asyncIterator] (deno:cli/rt/40_testing.js:245:13)
    at async Object.runTests (deno:cli/rt/40_testing.js:322:22)
    at async file:///home/uki00a/ghq/github.com/uki00a/sample/$deno$test.ts:3:1

failures:

        ...

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out (4ms)
```

このようなエラーが発生した際は、リソースの開放漏れがないか確認してみましょう。

#### テストケースのフィルタリングがサポートされた(`--filter`オプション)

`--filter`オプションによって、実行するテストケースを絞りこめるようになりました。

```shell
# 名前に「hoge」と含まれるテストのみを実行
$ deno test -A --filter hoge

# 正規表現を使用した例: 名前が「auth」から始まるテストのみを実行
$ deno test -A --filter "/^auth.+$/" ./test.ts
```

## 型チェックがスキップできるようになった(`--no-check`)

[Deno@v1.2.0](https://github.com/denoland/deno/releases/tag/v1.2.0)にて`--no-check`オプションがサポートされました。
これにより、TypeScriptの型チェックをスキップする手段が提供されます。

```shell
# 型チェックをスキップしてスクリプトを実行
$ deno run --no-check script.ts

# 型チェックをスキップしてテストを実行
$ deno test --no-check test.ts
```

## ファイルウォッチャー(`--watch`)が実装された

[Deno@v1.4.0](https://github.com/denoland/deno/releases/tag/v1.4.0)からファイルウォッチャーが実装されました。
これにより、ファイルが変更された際にスクリプトを自動で再実行できるようになります。

```shell
$ deno run --watch --unstable cli.ts
```

ファイルウォッチャーの詳細については「[Deno の組み込み機能、ソースコードの変更を検知してオートリロードする "File Watcher" の紹介](https://zenn.dev/magurotuna/articles/8f0ed7da6e9ab1)」の記事を参照ください:exclamation: 

## アプリケーションをシングルバイナリ形式へパッケージングできるようになった(`deno compile`コマンド)

[Deno@v1.6.0](https://github.com/denoland/deno/releases/tag/v1.6.0)にて、`deno compile`コマンドが実装されました。
これにより、Denoで書かれたアプリケーションをシングルバイナリ形式へパッケージングできるようになりました。(ただし、まだ不安定な機能(`--unstable`)という位置づけです)

```shell
$ deno compile --unstable https://deno.land/std@0.80.0/examples/welcome.ts
Check https://deno.land/std@0.80.0/examples/welcome.ts
Bundle https://deno.land/std@0.80.0/examples/welcome.ts
Compile https://deno.land/std@0.80.0/examples/welcome.ts
Emit welcome

$ ./welcome
Welcome to Deno 🦕
```

`deno compile`コマンドの詳細については「[Deno 1.6.0 がリリースされたので主要機能の紹介](https://zenn.dev/magurotuna/articles/020f6b103937ed)」の記事にて詳しく解説されていますので、興味があればぜひご覧ください:exclamation:

## Language Serverが組み込まれた(`deno lsp`コマンド)

[Deno@v1.6.0](https://github.com/denoland/deno/releases/tag/v1.6.0)から、DenoにLanguage Serverが組み込まれました。

`deno lsp`コマンドを使うと、Language Serverを起動できます。

vscodeやVim/Neovimとの連携方法については、[こちらの記事](https://qiita.com/uki00a/items/86eae971286d2bff515a)を参照ください。

## Web標準との互換性が向上した

この1年で様々なWeb APIがDenoに実装されました。

* `FileReader` ([v1.3.0](https://github.com/denoland/deno/releases/tag/v1.3.0)〜)
* `WebSocket` ([v1.4.0](https://github.com/denoland/deno/releases/tag/v1.4.0)〜)
* [prompt API](https://qiita.com/kt3k/items/2cf8c220c1c5552448ac) ([v1.5.0](https://github.com/denoland/deno/releases/tag/v1.5.0)〜)

等

これにより、Web標準との互換性がさらに向上しました。

## `std/wasi`モジュールが追加された

[caspervonbさん](https://github.com/caspervonb)により、標準モジュールにWASIモジュール(`std/wasi`)が実装されました。

https://github.com/denoland/deno/tree/master/std/wasi

WASIについては、以下の記事を参照ください。

* [コンテナ技術を捨て、 WASIを試す](https://medium.com/nttlabs/wasi-6060b243ac90)

## まとめ

* この1年でDenoには沢山の新機能が実装されました
* メジャーリリース以降、Denoの安定性は着実に向上しつつあります
* 標準モジュール(`deno_std`)の安定化はこれから行われていきます

明日は@kt3kさんの「Denoのディレクトリ構造2020」です:exclamation:

## 参考

* https://github.com/denoland/deno/blob/v1.6.1/Releases.md