みどりのさるのエンジニア

Denoを触ってみよう

2020年04月06日

インストール

denoland/deno: A secure JavaScript and TypeScript runtime

$ curl -fsSL https://deno.land/x/install/install.sh | sh
$ vi ~/.zshrc
# Denoのパス設定
export DENO_INSTALL="/Users/t-yng/.local"
export PATH="$DENO_INSTALL/bin:$PATH"

$ source ~/.zshrc

インストール直後はパスが通っていない状態なので ~/.zshrc にDenoのパス設定を追加して再読み込みします。
※ 個人の環境に合わせて適宜読み替えてください

HomeblewやCargoを利用してもインストールできます。
詳細は https://deno.land/ を確認してください。

VsCodeの拡張

今の状態でVSCodeでコードを書くとTypeScriptとして認識されるので、Denoの構文でエラーが発生します。
そこで Deno - Visual Studio Marketplace をインストールしておきます。

デフォルトでは設定が false になっているので .vscode/setting.json で拡張を有効化します。
自分は Node.js 環境で TypeScript を書く場合に競合する事を避けるために、プロジェクト単位で有効化しています。

{
  "deno.enable": true
}

Hello World を表示する

最初に「Hello World」を表示するサンプルコードを実装してみます。

$ mkdir deno-sample
$ cd deno-sample
$ touch server.ts

server.ts

deno-sample/server.ts に次のサンプルコードを記述します。

import { serve } from "https://deno.land/std@v0.39.0/http/server.ts";

const s = serve({ port: 8000 });
console.log("access to http://localhost:8000/");

for await (const req of s) {
  req.respond({ body: "Hello World\n" });
}

最初のimport文はモジュール読み込みです。
Denoは npm を利用せずにURLやファイルパスでモジュールを読み込む仕組みになっており、サンプルコードでは deno.land にホスティングされた標準APIのモジュールを読み込んでいます。

次に serve({port: 8000}) でサーバーインスタンスを生成しています。

ここで生成されるサーバーインスタンスはPromiseのイテレーターとなっているので、 for await でループ処理にてリクエストのインスタンスを取得して、 Hello World の文字列をレスポンスとして返しています。

実行

エラーが発生しました。

$ deno server.ts

Compile file:///Users/xxxxxx/workspace/deno/deno-sample/server.ts
error: Uncaught PermissionDenied: network access to "127.0.0.1:8000", run again with the --allow-net flag

Denoではネットワークアクセスやファイルアクセスには許可が必要なため、今回はネットワークアクセスを許可するために --allow-net フラグを指定します。

$ deno --allow-net server.ts
access to http://localhost:8000/

今度は成功しました。
この状態で http://localhost:8000/ にアクセスすれば、「Hello World」がブラウザに表示されます。🎉

HTMLを返す

次にHTMLを返すWebサーバーを実装してみます。

ディレクトリ構造

public/index.html は適当に作成してください。

.
├── public
│   └── index.html
└── server.ts

server.ts

fs モジュールの readFileStr でHTMLファイルの文字列を読み込みをしています。

正しく実装するなら、File Serverとして実装した方が良さそうですが、ファイル読み込みをを試してみたかったので、この書き方をしています。

import { serve } from "https://deno.land/std/http/server.ts";
import { readFileStr } from "https://deno.land/std/fs/mod.ts";

// HTMLファイルを読み込んで文字列を取得
const html = await readFileStr("./public/index.html", {
    encoding: "utf8",
}).catch((error) => {
    console.log(error);
    return "読み込みに失敗したよ";
});

const s = serve({ port: 8000 });
console.log("access to http://localhost:8000");
for await (const req of s) {
    req.respond({ body: html }); // HTML文字列をレスポンスBodyにセット
}

実行

ファイル読み込みをしているので --allow-read フラグを新たに指定しています。

$ deno --allow-net --allow-read server.ts
access to http://localhost:8000/

パッケージ管理

問題

現状では npm のようなパッケージ管理ツールがなく、ソース単位で外部モジュールの読み込みを書いている状態です。
依存している外部ライブラリを把握するためには、ソースコードを全て見る必要があり管理が困難になってしまいます。

deps.ts

deps.ts ファイルを新しく作り、モジュール読み込みの記述をまとめることで簡易的なパッケージ管理が実現できます。
deps.ts はマニュアルで紹介されいているファイル名で名前は何でもOKです。
ライブラリを見ていると、deps.ts か package.ts をファイル名として利用しているパターンが多かったです。

先ほどのHTMLを返すサンプルコードの一部を deps.ts を利用して書き直してみるとこんな感じです。

deps.ts

export { serve } from "https://deno.land/std/http/server.ts";
export { readFileStr } from "https://deno.land/std/fs/mod.ts";

server.ts

import { serve, readFileStr } from "./deps.ts";

// 後は同じ
...

ドキュメント