虎の穴開発室ブログ

虎の穴ラボ株式会社所属のエンジニアが書く技術ブログです

MENU

Deno 1.11 へのアップデートと変更事項まとめ

皆さんこんにちは、ゴルドバーン抽選先行販売に受かりました。おっくんです。

去る 2021 年 6 月 8 日に Deno 1.11 がリリースされました。 今回も、リリースノートを参考に 変更事項を、紹介したいと思います。

実行環境

  • macOS Big Sur 10.4
  • Dockerイメージ denoland/deno:centos

アップデートのやり方

今回は Deno 1.10.1 から Deno 1.11.0 へのアップデートを行います。

アップデートする Deno を導入した時のコマンドは以下の通りです。

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

アップデートは、以下のコマンドで実施しました。

$ deno upgrade

# バージョン指定する場合は次のコマンドで実行します。

# deno upgrade --version 1.11.0

方法については、こちらに記載があります。

deno.land

Deno 1.11

Deno 1.11 での変更事項をDeno 1.11 リリースノートを元に確認します。

deno.com

公式 Docker イメージが出ました

以下のイメージを dockerhubから取得できるようになりました。

hub.docker.com

deno repl を開始するには、次のようにします。

developernoMacBook:deno4 developer$ docker run -it --init denoland/deno:1.11.0 repl
Deno 1.11.0
exit using ctrl+d or close()
> Deno.version
{ deno: "1.11.0", v8: "9.1.269.35", typescript: "4.3.2" }
>
developernoMacBook:deno4 developer$ docker run -it --init --entrypoint sh denoland/deno:1.11.0
# deno
Deno 1.11.0
exit using ctrl+d or close()
> Deno.version
{ deno: "1.11.0", v8: "9.1.269.35", typescript: "4.3.2" }
>
# exit
$ cat main.ts
console.log(Deno.version)

$ docker run -it --init -p 1993:1993 -v $PWD:/app denoland/deno:1.11.0 run --allow-net /app/main.ts
Check file:///app/main.ts
{ deno: "1.11.0", v8: "9.1.269.35", typescript: "4.3.2" }

以上のように起動が可能でした。
おそらく普段は、docker-compose で起動するのでは?と思っています。

以降の確認は、次の Dockerfile と docker-compose.yml を使用して起動したコンテナ内で確認します。
(使用するDocker イメージは、私が使い慣れていることも有りcentosを使います。)

[Dockerfile]

FROM denoland/deno:centos

RUN mkdir /usr/src/app
WORKDIR /usr/src/app

EXPOSE 8080

[docker-compose.yml]

version: "3"
services:
  app:
    build:
      context: .
    entrypoint:
      - /sbin/init
    ports:
      - "8080:8080"
    volumes:
      - .:/usr/src/app:cached
    tty: true
$ ls
Dockerfile  docker-compose.yml

$ docker-compose build
$ docker-compose up -d
$ docker-compose exec app bash
[root@f811fdc6fbf7 app]#

Web Crypto API がサポートする形式が増えました

今回のリリースから、Deno で使える Web Crypto API のメソッドが追加されます。

developer.mozilla.org

既に、Deno 1.0 からサポートされている crypto.getRandomValues() に加えて、ハッシュや UUID 生成のためのメソッドが追加されます。 これにより、安全なシステムを簡単に構築するための暗号化プリミティブを、外部モジュールへ依存せずに使うことができるようになります。

以下のように、crypto.subtle.digest() を使用することで、sha-1 sha-256 sha-384 sha-512 といったハッシュ生成が行えます。

[crypt_test.ts]

import { encodeToString } from "https://deno.land/std@0.97.0/encoding/hex.ts";

const data = new TextEncoder().encode("Deno 1.11 has been released!");

const digest1 = await crypto.subtle.digest("sha-1", data.buffer);
console.log("Digest(sha-1):", encodeToString(new Uint8Array(digest1)));

const digest256 = await crypto.subtle.digest("sha-256", data.buffer);
console.log("Digest(sha-256):", encodeToString(new Uint8Array(digest256)));

const digest384 = await crypto.subtle.digest("sha-384", data.buffer);
console.log("Digest(sha-384):", encodeToString(new Uint8Array(digest384)));

const digest512 = await crypto.subtle.digest("sha-512", data.buffer);
console.log("Digest(sha-512):", encodeToString(new Uint8Array(digest512)));
[root@f811fdc6fbf7 app]# deno run crypt_test.ts
Digest(sha-1): f1c04454a05805214d8d5d3c0f038d020e886698
Digest(sha-256): 2f3b7a841d4be0ef23c13d9795fd56ebc794aee5f41f1be0796118eaa52237dc
Digest(sha-384): 31b3a40911e37cac917b59f3c1371b06f678f6fd584dd0acec2828b47f9fc46caa26a5e1a768ae60eab6e0493440d54c
Digest(sha-512): c3605912802f61cd43c228491a0e4b15a4bd986f6e8367c7a5a788c63c7e447702f4ee40e6300252aec48e16f6029d5e184acca85b1b9b3ed7c36c028d06024a

また、最近標準化された crypto.randomUUID() もサポートしており、RFC 4122に従い UUID v4 を生成できます。

[root@f811fdc6fbf7 app]# deno
Deno 1.11.0
exit using ctrl+d or close()
> crypto.randomUUID()
"4a11af47-eec3-4ca0-8461-08913ef35ca4"
> crypto.randomUUID()
"3301b4ce-6559-47a7-b049-109f102b8806"
> crypto.randomUUID()
"7221a12f-f192-45ed-b04b-aba18e8dc2fb"
> crypto.randomUUID()
"97be5877-230d-40ac-8a5c-fa5efc6bc302"
>

この機能は、Node.js では実装済みで、Chrome/Edge 92  でリリースされます。 Web コンパチブルな API の導入ということですね。

さらに、7 月 13 日 リリース予定の Deno 1.12 では、Web Crypto API を拡張することを目指しているそうです。

今挙がっているプルリクを見ると、generateKey()sign() の実装が進んでいるようです。

github.com

fetch API の中止に対応しました

進行している fetch の停止できるとうれしいことがあります。 例えば、応答の無いリクエストをタイムアウトさせたい時などです。 今回のリリースでは、fetch を停止するために用いる標準 API AbortSignal に対応しました。

developer.mozilla.org

リリースノートでは、次のように示されています。

[abort_signal_test.ts]

const controller = new AbortController();

const timeoutMSec = 30; // <= abortまでの時間

setTimeout(() => controller.abort(), timeoutMSec);

const response = await fetch("[任意のURL]", {
  signal: controller.signal,
});

const users = await response.json();

timeoutMSec を 30 など、fetch の結果の前に、controller.abort() が実行されるようにすると、次のようになります。

[abort_signal_test.ts]

[root@f811fdc6fbf7 app]# deno run --allow-net abort_signal_test.ts
Check file:///usr/src/app/abort_signal_test.ts
error: Uncaught (in promise) AbortError: Ongoing fetch was aborted.
setTimeout(() => controller.abort(), 30);
                            ^
    at abortFetch (deno:extensions/fetch/26_fetch.js:422:19)
    at onabort (deno:extensions/fetch/26_fetch.js:383:16)
    at AbortSignal.[signalAbort] (deno:extensions/web/03_abort_signal.js:34:9)
    at deno:extensions/web/03_abort_signal.js:134:59
    at AbortSignal.[signalAbort] (deno:extensions/web/03_abort_signal.js:34:9)
    at AbortController.abort (deno:extensions/web/03_abort_signal.js:74:32)
    at file:///usr/src/app/abort_signal_test.ts:3:29
    at fire (deno:extensions/timers/01_timers.js:460:7)
    at handleTimerMacrotask (deno:extensions/timers/01_timers.js:321:7)

AbortError が発生しfetch を停止できました。

実際の実装では、例外を管理するために以下のようにするのでは?と想定しています。

[abort_signal_test_try.ts]

const controller = new AbortController();

setTimeout(() => controller.abort(), 30);

let users = []
try {
  const response = await fetch("[任意のURL]", {
    signal: controller.signal,
  });
  users = await response.json();
} catch (e) {
  console.error(e);
}

console.log(users)

実行すると次のようになります。

[root@f811fdc6fbf7 app]# deno run --allow-net abort_signal_test_try.ts
Check file:///usr/src/app/abort_signal_test_try.ts
AbortError: Ongoing fetch was aborted.
    at abortFetch (deno:extensions/fetch/26_fetch.js:422:19)
    at onabort (deno:extensions/fetch/26_fetch.js:383:16)
    at AbortSignal.[signalAbort] (deno:extensions/web/03_abort_signal.js:34:9)
    at deno:extensions/web/03_abort_signal.js:134:59
    at AbortSignal.[signalAbort] (deno:extensions/web/03_abort_signal.js:34:9)
    at AbortController.abort (deno:extensions/web/03_abort_signal.js:74:32)
    at file:///usr/src/app/abort_signal_test_try.ts:3:29
    at fire (deno:extensions/timers/01_timers.js:460:7)
    at handleTimerMacrotask (deno:extensions/timers/01_timers.js:321:7)
[]

AbortErrorcatch して処理できました。

Deno lint が stable になりました

Deno には、deno lint というサブコマンドで、使用できるリンターが組み込まれています。 Deno 1.1.0 で登場したdeno lintですが、これまではバグの可能性から --unstable フラグが必要でした。 安定性向上の為の、複数回のリファクタリングを行い、ESLint の推奨ルールセットと一致するルールを追加しました。

これまでdeno lint は、不安定なことから、一部ユーザーからまだ使用したくないというフィードバックがありました。 現在deno lint は、安定化したと考えており、--unstable フラグが不要になりました。

ESLint の推奨ルール相当のルールが追加されている現在ですが、推奨ルールセットを変更する可能性があるそうです。

deno lint の改善には、日本人コミッター Yusuke Tanaka さん が貢献されたそうです。

github.com

deno compile がアップデート

deno compile は、Deno 1.6 で登場した、自己完結型のバイナリ生成サブコマンドです。 この Deno compileは提供される機能に制限があり、要望の多かった機能の 1 つが、生成したバイナリで動的インポートを使用することだったそうです。 今回のリリースでは、データ URI を使用した動的インポートがサポートされました。 試してみます。

以下の 2 つのファイルを用意します。

[compile_src.ts]

const sourceCode = await Deno.readTextFile("./some_source_code.js");
const dataUrl = "data:text/javascript;base64," + btoa(sourceCode);
const c = await import(dataUrl);
console.log(c.default);

[some_source_code.js]

console.log("Hello Deno!");

deno compile で自己完結型バイナリを作成し、実行してみます。

[root@f811fdc6fbf7 app]# ls
compile_src.ts some_source_code.js

[root@f811fdc6fbf7 app]# deno compile --allow-read compile_src.ts
Check file:///usr/src/app/compile_src.ts
Bundle file:///usr/src/app/compile_src.ts
Compile file:///usr/src/app/compile_src.ts
Emit compile_src

[root@f811fdc6fbf7 app]# ./compile_src
Hello Deno!
undefined

# 読み込み対象の some_source_code.js の内容の書き換え
[root@f811fdc6fbf7 app]# echo 'console.log("Hello DenoDenoDeno!");' > some_source_code.js

[root@f811fdc6fbf7 app]# ./compile_src
Hello DenoDenoDeno!
undefined

読み込み対象の、some_source_code.js を動的に読み込んで、実行できました。 ソースコードに、データに当たるものが記述されているときや、実行対象自体を動的に切り替えるにはとても便利です。 この構造、deployctl に対して addEventListener("fetch", ~~ が記述されたソースファイルを渡すのと似ていますね。

TextEncoderStream と TextDecoderStream のサポート

TextEncoderStream と TextDecoderStream の 2 つの Web API が、追加されました。 ReadableStream と一緒に、使うことで簡単にバイト列と文字列を相互に変換できます。 加えて TextDecoderが、streamオプションに対応しました。

ストリームを返してくれるリクエスト先を用意できなかったのでリリースノートよりそのまま使用例を紹介します。 以下は、fetch の応答ストリームを TextDecoderStream をパイプする例です。 これにより、バイト列のストリームが、文字列セグメントの文字列に変換されます。 次の例では、セグメントごとに改行しながら表示します。

const response = await fetch("https://http2.golang.org/clockstream");
const body = response.body.pipeThrough(new TextDecoderStream());
for await (const chunk of body) {
  console.log("!!chunk start!!", chunk, "!!chunk end!!");
}

BroadcastChannel API の追加

BroadcastChannel はウィンドウ、タブ、フレーム間で、同じオリジンを使用して通信できる API です。

こちらのBroadcast_Channel_API説明がわかりやすいです。

developer.mozilla.org

こちらが、--unstable フラグの付与が必要な機能として公開されました。 同じオリジンであれば、別プロセスでも通信可能な本 API ですが、現在は同じプロセスに限定されています。 将来的には、別プロセスでも同一オリジンで起動した複数プロセス間の通信もサポートしたいそうです。

リリースノートを参考に試してみます。ワーカーの呼び出し側の broadcast.tsworker.js を用意します。

[broadcast.ts]

const channel = new BroadcastChannel("foo");
const url = new URL("./worker.js", import.meta.url).href;

for (let i = 0; i < 3; i++) {
  const w = new Worker(url, { type: "module" });
  await new Promise((resolve) => (w.onmessage = resolve));
}

channel.postMessage({ hello: [1, 2, 3] });

[worker.js]

const channel = new BroadcastChannel("foo");

self.postMessage("ready");
channel.onmessage = (e) => {
  console.log("got message", e.data);
};

実行すると次のようになります。

[root@f811fdc6fbf7 app]# deno run --unstable --allow-read broadcast.ts
Check file:///usr/src/app/broadcast.ts
got message { hello: [ 1, 2, 3 ] }
got message { hello: [ 1, 2, 3 ] }
got message { hello: [ 1, 2, 3 ] }

立ち上げた 3 つのワーカーと共有した"foo"というチャンネルに、postMessage()を使いブロードキャストすることで、それぞれのワーカーにメッセージを送ることができます。

こちらの、 BroadcastChannel は比較的多くのブラウザで実装されており、Node.js でも v14.5.0 から利用できるAPIです。

安定化した API への更新

以下の Deno API 群では、ファイルパスをする時に文字列だけでなく、URL を指定できるようになりました。

  • Deno.chdir
  • Deno.realPath
  • Deno.realPathSync
  • Deno.rename
  • Deno.renameSync
  • Deno.symlink
  • Deno.symlinkSync
  • Deno.utime
  • Deno.utimeSync

例えば、Deno.renameSync では以下の 2 種類の書き方ができるようになります。

// 文字列でパス指定
Deno.renameSync("/usr/src/app/main1.ts", "/usr/src/app/main.ts");

// URLでパス指定
Deno.renameSync(new URL("file:///usr/src/app/main.ts"), new URL("file:///usr/src/app/main1.ts"));

deno lsp のアップデート

deno lsp にも今回のリリースにより多数の機能が追加されています。 開発支援の機能ということもあり、リリースノートでは、VSCode との連携で紹介されています。

それぞれ、簡単に触れておきます。

  • IDE から個別のテストコードの実行ができるようになった
    Deno.test と記述された上に Run Test と表示され個別のテストを GUI から呼び出せるように

  • リソースごとの設定をサポート

  • レジストリの自動検出
    import を書いたときに、import 補完機能をサポートしているレジストリだと検出するとプロンプトが出るように
  • JSON(C) とマークダウンファイルのフォーマットをサポート
    deno fmtを個別に実行せずとも、設定に記述することでフォーマット機能を呼び出せるように
  • リンターの診断結果でヒントを表示するように
  • トリプルスラッシュでの参照先に基づいた診断結果の提供
  • X-Deno-Warning ヘッダーを表示 開発者に、ソースコードを提供するサーバー(例えば、deno.land/x) が X-Deno-Warning ヘッダーを用いて警告を送ることができます。
    (deno.land/xではバージョンを含まない URL を使うと、警告します。)

X-Deno-Warning で思うところですが、単純にソースコードを返すだけでなくメッセージまで提供をしてくれるレジストリが、Deno.land/x 以外で登場するのか?が見えないところです。
今後、例えば開発者が提供するモジュールに書いた特定のフォーマットのコメント内容に基づいて、注意事項など出せるようになったりすると面白そうです。

TypeScript 4.3 に対応しました

クラス継承した場合のメソッドへの override キーワードなどが Deno でも使えるようになります。


Deno 1.11 のリリースでは、deno compile での動的インポートが気になるものでした。 自分も含めて Deno でツールを作っている人には朗報では?と思います。
また、BroadcastChannel API は知識がなく今回調べたのですが、こちらは別途試しておく必要を感じました。

次の Deno 1.12 のリリース予定が 7 月 13 日だそうです、また次のレポートでお会いしましょう。

P.S.

■ 採用情報

とらのあなでは、オタクなエンジニアを募集しています。
yumenosora.co.jp

カジュアル面談も随時開催中です。お申し込みはこちら!
yumenosora.connpass.com

■ ToraLab.fmスタートしました!

メンバーによるPodcastを配信中!是非スキマ時間に聞いて頂けると嬉しいです。
anchor.fm

■ Twitterもフォローしてくださいね!

ツイッターでも随時情報発信をしています。
twitter.com