皆さんこんにちは、ゴルドバーン抽選先行販売に受かりました。おっくんです。
去る 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 1.11
Deno 1.11 での変更事項をDeno 1.11 リリースノートを元に確認します。
公式 Docker イメージが出ました
以下のイメージを dockerhubから取得できるようになりました。
- Alpine Linux: denoland/deno:alpine
- Centos: denoland/deno:centos
- Debian: denoland/deno:debian (default)
- Distroless: denoland/deno:distroless
- Ubuntu: denoland/deno:ubuntu
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 のメソッドが追加されます。
既に、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()
の実装が進んでいるようです。
fetch API の中止に対応しました
進行している fetch の停止できるとうれしいことがあります。
例えば、応答の無いリクエストをタイムアウトさせたい時などです。
今回のリリースでは、fetch
を停止するために用いる標準 API AbortSignal に対応しました。
リリースノートでは、次のように示されています。
[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) []
AbortError
を catch
して処理できました。
Deno lint が stable になりました
Deno には、deno lint
というサブコマンドで、使用できるリンターが組み込まれています。
Deno 1.1.0 で登場したdeno lint
ですが、これまではバグの可能性から --unstable
フラグが必要でした。
安定性向上の為の、複数回のリファクタリングを行い、ESLint の推奨ルールセットと一致するルールを追加しました。
これまでdeno lint
は、不安定なことから、一部ユーザーからまだ使用したくないというフィードバックがありました。
現在deno lint
は、安定化したと考えており、--unstable
フラグが不要になりました。
ESLint の推奨ルール相当のルールが追加されている現在ですが、推奨ルールセットを変更する可能性があるそうです。
deno lint
の改善には、日本人コミッター Yusuke Tanaka さん が貢献されたそうです。
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説明がわかりやすいです。
こちらが、--unstable
フラグの付与が必要な機能として公開されました。
同じオリジンであれば、別プロセスでも通信可能な本 API ですが、現在は同じプロセスに限定されています。
将来的には、別プロセスでも同一オリジンで起動した複数プロセス間の通信もサポートしたいそうです。
リリースノートを参考に試してみます。ワーカーの呼び出し側の broadcast.ts
と worker.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