[2019年2月版] deno の使い方

# Deno ってのがあるらしい

今日のQiitaのトレンドみてたらRustってキーワードで釣られました。

- [Rustのメモリ管理って面白い](https://qiita.com/ksato9700/items/312be99d8264b553b193)

そして出てきたDeno。Denoってなんぞや??

- [Deno について知っていることと、今後への期待 v1811](https://qiita.com/euxn23/items/f3b45e3c093e448acf9b)

node.js本家の人がいろいろアレなんでdenoでどうでしょう？ってことかな？
-[Node.js における設計ミス By Ryan Dahl](https://yosuke-furukawa.hatenablog.com/entry/2018/06/07/080335)

TypeScript がそのまま動く実行環境ってことでしょうか？
そして見つけた本家サイトはこちら。

- 本家サイト: https://deno.land/
- ドキュメント: https://github.com/denoland/deno/blob/master/Docs.md
- GitHub: https://github.com/denoland/deno
- API doc: https://deno.land/typedoc/index.html

## さっそく install
対象の環境は Mac(macOs High Sierra)でございます。
[本家サイト](https://deno.land/)にならってインストールしてみます。

```shell
$ curl -fL https://deno.land/x/install/install.sh | sh
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100  1049  100  1049    0     0    878      0  0:00:01  0:00:01 --:--:--   878
######################################################################## 100.0%
Deno was installed successfully to /Users/xxxxxxx/.deno/bin/deno
Manually add the directory to your $HOME/.bash_profile (or similar)
  export PATH="/Users/xxxxxx/.deno/bin:$PATH"
Run '/Users/xxxxxxx/.deno/bin/deno --help' to get started
```
とあっさりインストール完了。さらに表示されたご案内に従ってパスを通し、```deno```コマンドを叩いてみます。

```shell
$ vi .bash_profile #--> export PATH="~/.deno/bin:$PATH" を追加
$ source .bash_profile
$ deno -v
deno: 0.2.10
v8: 7.2.502.16
$ deno --help
Usage: deno script.ts 

Options:
        --allow-write   Allow file system write access.
        --allow-net     Allow network access.
        --allow-env     Allow environment access.
        --allow-run     Allow running subprocesses.
    -A, --allow-all     Allow all permissions.
        --recompile     Force recompilation of TypeScript code.
    -h, --help          Print this message.
    -D, --log-debug     Log debug output.
    -v, --version       Print the version.
    -r, --reload        Reload cached remote resources.
        --v8-options    Print V8 command line options.
        --types         Print runtime TypeScript declarations.
        --prefetch      Prefetch the dependencies.
        --info          Show source file related info
        --fmt           Format code.

Environment variables:
        DENO_DIR        Set deno's base directory.
```

わーい！~~クララ~~deno が動いた〜！

## VS code から hello world
hello worldくらいならコマンドラインから動かしてもいいんですが、今後のお付き合いも考えるとVS Codeでどんくらいイケるんだ？というのが気になります。

とりあえずVS codeを立ち上げて以下のようなファイルを作成します。

```typescript:sample.ts
console.log("hello world");
```
いや、面目無い。Typescriptならではっていう気の利いたコードがさっと出てこなくて。
でもプログラムはプログラム。ターミナルから以下のようにdenoを動かす。

```shell
$ deno sample.ts
Compiling /Users/xxxxxxx/xxxxxx/xxxxx/xxxxxx/sample.ts
hello world
```
あぁなんかそれっぽい。
でもconsoleとかモロ手打ちなのはいただけないのでコード補完パワーが欲しくなります。

VSCodeのextension で ```deno``` と打つと ```deno-vscode```というモロそれっぽいpluginがヒットしました。これを入れてみますか。
インストール後、```Command + P```でコマンドパレットが開くので、```>deno init```と打ちます。
数秒後、"準備ができましたので再起動します"的なメッセージが一瞬流れて VSCode再起動・・・おいおいおw
ちょっと勝手に再起動はどうなんだろう？と思いつつ、吐き出されたファイルを見てみると、結構、出てますね。。。

```shell
$ ls -1
node_modules
package-lock.json
package.json
sample.ts
tsconfig.json
```
えっと、package.jsonとかnode_modulesとかnode.jsの悪い文化だからここから脱却したかったんじゃなかったっけdeno？ nodeでいいの？ no! なにがいいの？! deno!!って訳でコイツはちょっとuninstall。

そんなわけで以下のツイート見つけました。エッセンシャルなのはこちらですね！

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/denots?src=hash&amp;ref_src=twsrc%5Etfw">#denots</a>、こういうtsconfig.jsonを書くと、IntelliJ、VSCodeで補完されるぞ！！ <a href="https://t.co/sEbwb71f6y">pic.twitter.com/sEbwb71f6y</a></p>&mdash; keroxp (@keroxp) <a href="https://twitter.com/keroxp/status/1078904266918526977?ref_src=twsrc%5Etfw">2018年12月29日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


教えに従って以下のような tsconfig.jsonファイルを作成いたしました。相対パスの深さはコードのある位置から~/.denoまでの深さです。各自、チェックしてくださいね！

```javascript:tsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "deno": [
        "../../../.deno/deno.d.ts"
      ],
      "https://*": [
        "../../../.deno/deps/https/*"
      ],
      "http://*": [
        "../../../.deno/deps/http/*"
      ]
    }
  }
}
```

これでdenoのハッピーコードインテリジェンスライフが送れるかも？！

# 単純にHTTPをエコーするサーバーをこさえてみます。

本家サイトのMini-tutorialに```https://deno.land/x/http/file_server.ts```を使用しているのを発見しました。
これに倣ってもっと簡単な、POSTされた文字列をJSONで返すエコーなHTTPサーバーを作ってみようかと思います。
とりあえず上記ファイルをブラウザでダイレクトに表示し、使えそうな頭の部分(import宣言やらいろいろしてるとこ)だけ持ってきます。

```typescript:sample_http_echo.ts
#!/usr/bin/env deno --allow-net

import {
    listenAndServe,
    ServerRequest,
    setContentLength,
    Response
  } from "https://deno.land/x/http/server.ts";
  import { cwd, DenoError, ErrorKind, args, stat, readDir, open } from "deno";
```
1行目のシバンでdenoで呼ぶ時は--allow-netを付与してくれってことがわかりますね。さすがサンプル、わかりやすい！

次のimportからdenoの本領発揮ですね。インターネット上のtsファイルを直接importするのでpackage.json不要！
次の"deno"からimportしている箇所は、tsconfig.jsonでのパス指定が正しければちゃんと認識してくれるはずです。

## 宣言しただけではダメ

import 宣言でいろいろモジュールからクラスやメソッドなど取ってきますがtsconfig.jsonで指定した```https://*```などは```~/.deno/```を参照してますのでこのままではコード補完も効きません。
初めて使うモジュールに遭遇した場合は依存モジュールだけまずはじめに取得したいところです。
そんな時には```deno```の```--prefetch```オプションを使います。

```shell
$ deno sample_http_echo.ts --prefetch
Compiling /Users/yoshinori/Develop/github/deno_sample/sample_http_echo.ts
Downloading https://deno.land/x/http/server.ts...
Downloading https://deno.land/x/io/bufio.ts...
Downloading https://deno.land/x/textproto/mod.ts...
Downloading https://deno.land/x/http/http_status.ts...
Downloading https://deno.land/x/testing/mod.ts...
Downloading https://deno.land/x/io/util.ts...
Compiling https://deno.land/x/http/server.ts
Compiling https://deno.land/x/io/bufio.ts
Compiling https://deno.land/x/io/util.ts
Compiling https://deno.land/x/testing/mod.ts
Compiling https://deno.land/x/textproto/mod.ts
Compiling https://deno.land/x/http/http_status.ts
$ 
```
ソースコードの実行はせずに依存関係の解決とダウンロードだけを行なってくれます。
コンパイルはされるのでコンパイルエラーがある状態ではダメでしょうけれども・・・

VS Codeのエディタにはちょっと遅れて反映されます。
エディタ閉じて開くとそれなりにさっと認識されましたが・・・これはVS Codeでなんかあるのかな？再解釈的なコマンド・・・？

## とりあえずlistenしてみる

続いて、空っぽのhttp待ち受けでlistenAndServeを呼び出しだけしてみましょうか。

```typescript:sample_http_echo.ts
  const serverArgs = args.slice();
  const addr = `0.0.0.0:${serverArgs[1] || 4500}`;

  listenAndServe(addr, req =>{
    
  });

  console.log(`HTTP server listening on http://${addr}/`);
```
さっそく、ターミナルで叩いてみます。

```shell
$ deno sample_http_echo.ts
Compiling /Users/xxxxxx/xxxx/xxx/xxxxxxx/sample_http_echo.ts
Deno requests network access to "listen". Grant? [yN] y
HTTP server listening on http://0.0.0.0:4500/
```
お？！networkをlistenするのにGrantしていいか？と聞いてきました！ちゃんとガードされてるっ！
```control+c```でいったん終わらせて、```--allow-net```つけてみましょう。

```shell
$ deno sample_http_echo.ts 1899 --allow-net
HTTP server listening on http://0.0.0.0:1899/
```
ついでに違うポートを指定してみましたがちゃんと効いてます。

(いや、テストケース書けよwと思いますが、deno単体で.test.tsを叩けるようなコマンド？がパッと見当たらなかったので後日にします。)

## POSTをエコーする処理
POSTされたbodyをjsonにしてエコーを返す処理を書いてみます。

```typescript:sample_http_echo.ts
  listenAndServe(addr, async req =>{
    const body  = new TextDecoder().decode(await req.body());
    const headers = new Headers();
    headers.set("content-type", "application/json");
    let res : Response = {
      status : 200,
      body: new TextEncoder().encode('{"postBody":"'+body+'"}'),
      headers
    };
    setContentLength(res);

    req.respond(res);
  });
```
なんかJSONの文字列、ベタ書きなのが気になるけど・・・(笑)

RequestやResponseのbodyがUint8Arrayなのでそのまま文字列として扱えませんが、そこはドンマイ。

## 動かしてみる！
以下のコマンドでサーバーを起動します。

```shell
$ deno sample_http_echo.ts 1899 --allow-net
HTTP server listening on http://0.0.0.0:1899/
```
ターミナルをもう一つ開き、```curl```でPOSTしてみます。

```shell
$ curl -X POST -d "aaaaaa" http://localhost:1899/
{"postBody":"aaaaaa"}
```

ちゃんとJSON形式の文字列で返ってきました！！！
とりあえず、サーバーとしての動きが確認できたのでよかった。

# 作成したコード

Gistにあげました。以下のリンクからどうぞ！
https://gist.github.com/Yoshinori-Koide-PRO/e45c93e532974d8defd25b2ac82c0cff

# まとめ
とりあえず動いてよかった〜
denoはまだまだ0.2ですがTypescriptがそのまま動かせるのは面白いです。
今後のバージョンアップにも期待したいと思います！