🦕

Deno 1.16が来たぞ

2021/11/11に公開

2021年11月9日にDeno 1.16がリリースされました。

https://twitter.com/deno_land/status/1458123920838742019

https://deno.com/blog/v1.16

本記事では、上記のリリースノートの内容をざっと紹介していきます。

  1. fetchがfile URLをサポートした
  2. React17のJSX変換がサポートされた
  3. Signal Listener APIが追加された
  4. Error.causeがコンソールに表示されるようになった
  5. TLS接続のハンドシェイクを明示的に実行できるようになった
  6. Web Streams APIに機能が追加された
  7. Deno.startTlsが安定化された
  8. Per-test permissionsが安定化した
  9. localStorageの使用に--locationが不要になった
  10. AbortSignalに理由が指定できるようになった
  11. dntがリリースされた
  12. V8が9.7にバージョンアップした

fetchがfile URLをサポートした

fetchメソッドの引数にファイルパスを取れるようになりました。
import.meta.urlを基準とした相対指定でファイルを読み込む場合などに役立つとのことです。
ファイル読み込みなので、実行には--allow-readオプションが必要です。
ファイルフェッチはWeb仕様では定義されていないため、Firefoxの実装が参考にされています。

最も単純なサンプルはこちら。

fetch_file.ts
const resp = await fetch("file:///etc/hosts");
const text = await resp.text();
console.log(text);

また、次のコードは記事の例を元にしたサーバーでの使用例で、従来のreadTextFileもコメントで併記してみました。
fetchはファイルをチャンク単位で読み込むので、メモリ使用量的に有利になるようです。

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

const addr = ":8080";
console.log(`HTTP server listening on http://localhost${addr}`)

serve(async (request: Request) => {
  const { href, origin, host, pathname } = new URL(request.url);
  console.log({ href, origin, host, pathname });

  // const index = await Deno.readTextFile("./static/index.html");

  const resp = await fetch(new URL("./static/index.html", import.meta.url));
  const index = resp.body;

  return new Response(index, {
    headers: { "content-type": "text/html; charset=utf-8" },
  });
}, { addr });

このサンプルでは"./static/index.html"を表示させています。
readTextFileに相対パスを渡す場合はDeno.cwd()が基準となりますが、new URL()でファイルパスを生成する場合は第2引数のパスが基準となるので、同じファイルを読み込む場合でも、パスの指定が異なる可能性があることに注意してください。

さらに、以下の点が注意点として挙げられています。

  • ファイルが見つからない場合、fetchから返されたPromiseTypeErrorで失敗(reject)となります。
  • ディレクトリが指定された場合も、同様にTypeErrorで失敗します。
  • レスポンスにcontent-lengthヘッダーが設定されていません。応答本文がストリームではなく、正確な長さを事前に知ることができないためです。
  • レスポンスにcontent-typeヘッダーが設定されていません。ファイル拡張子からコンテンツタイプを取得する方法として、media_typesモジュールが紹介されています。

React17のJSX変換がサポートされた

React17で導入されたJSXの変換方式をDenoでもサポートするようになります。

https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html
https://zenn.dev/uhyo/articles/react17-new-jsx-transform

以下の2つの利用方法があります。

1: jsx/tsxファイルで@jsxImportSourceを宣言する

welcome.jsx
/** @jsxImportSource https://esm.sh/preact */

export Welcome({ name }) {
  return (
    <div>
      <h1>Welcome {name}</h1>
    </div>
  );
}

2: Configuration fileを使う

deno.json
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "https://esm.sh/preact"
  }
}

Signal Listener APIが追加された

新たなunstable APIとして、Deno.addSignalListener()Deno.removeSignalListener()が追加されました。
プロセスが特定のシグナルを受け取ったときに実行する関数を追加・除去するAPIです。

以下のコードで試すことができます。

signal_listener_sample.ts
const listener = () => {
  console.log("SIGINT!");
};

console.log("Start listening to SIGINT!");
Deno.addSignalListener("SIGINT", listener);

// Wait 3 seconds
await new Promise((resolve) => setTimeout(resolve, 3000));

console.log("Stop listening to SIGINT!");
Deno.removeSignalListener("SIGINT", listener);

// Wait 3 seconds
await new Promise((resolve) => setTimeout(resolve, 3000));

console.log("End of process!");

これをdeno run --unstable signal_listener_sample.tsで実行し、実行中にCtrl+cSIGINTを送ってみると挙動が確認できると思います。

試してみるとわかりますが、一度addSignalListenerすると、本来の挙動(Ctrl+cでプロセス中断)に戻りません。
これは、内部で使用されているtokio::signalというRustモジュールの抱えている問題とのことで、留意が必要そうです。
https://github.com/denoland/deno/issues/7164

また、unstable docによれば、いまのところWindowsでは使えないようです。

Error.causeがコンソールに表示されるようになった

1.13でErrorオブジェクトにcauseプロパティが追加されていました。

https://zenn.dev/magurotuna/articles/deno-release-note-1-13-0#error-cause

これがコンソールに出力されるようになり、デバッグに使いやすくなりました。

こんな感じのコードで確認できます。

error_cause_sample.ts
try {
  throw new Error("main error", { cause: new TypeError("caused by this") });
} catch (e) {
  console.log(e);
}

TLS接続のハンドシェイクを明示的に実行できるようになった

これまでは、TLS接続で最初にデータを読み書きするとき、自動でハンドシェイクが実行されていました(ほとんどのユーザーはハンドシェイクの詳細を気にする必要がないため…とのこと)。

今回のリリースでDeno.TlsConnhandshake()メソッドが追加され、TLSハンドシェイクを手動で実行できるようになりました。戻り値はハンドシェイクが完了するとresolveするPromiseです。

Web Streams APIに機能が追加された

Web Streams APIに機能がいくつか追加されました。

  • ReadableStreamBYOBReaderがサポートされました。この「Bring-Your-Own-Buffer(BYOB)」リーダーは、開発者が提供したバッファに読み込むことで、通常(ReadableStreamDefaultReader)と比較してコピーを最小限に抑えることができます。このAPIについてはMDNで詳しく紹介されています。
  • WritableStreamDefaultController.signalがサポートされました。詳しくはexplainerをご覧ください。
  • ReadableStream.getIteratorが削除されました。このメソッドはDeno 1.7から非推奨となっており、どのブラウザでも実装されていませんでした。ReadableStreamにはAsyncIterableが実装されているので、ストリームのイテレーションにはそちらの使用が推奨されています。

Deno.startTlsが安定化された

こちらもTLS関連の更新です。

Denoでは、TLSでサーバーに接続する方法として、以下のAPIがあります。

  • Deno.connectTls: TCP接続を開いてすぐにTLSの通信を開始する
  • Deno.startTls:まず平文のTCP接続を作成し、データを交換してから、その上でTLSの通信を開始する

今回のリリースで、後者のstartTls APIが安定化されました。
これにより、SMTPドライバが--unstable不要で動作するようになり、その例としてpostgresモジュールが挙げられています。

Per-test permissionsが安定化した

Deno 1.10で、テスト毎にパーミッションを与えられる機能が追加されていました。

https://zenn.dev/magurotuna/articles/deno-release-note-1-10-1#個々のテストにパーミッションを設定できるようになった

今回の1.16より、これが安定機能(--unstable不要)になりました。

localStorageの使用に--locationが不要になった

これまでのバージョンでは、localStorage APIの使用には起動時に--locationオプションの付与が必要でした。
今回のリリースで、--locationオプションを付けずに実行した場合もlocalStorage APIを使用できるになりました。
--locationオプションを付けない場合、ストレージバケットのキーが暗黙で設定されます。キーを明示的に設定したい場合は--locationフラグが必要です。

使用されるキーは以下のように設定されます。

  • --locationオプションを使用すると、そのオリジンが使用されます。つまり、ロケーションがhttp://example.com/a.tshttp://example.com/b.tshttp://example.com:80/はすべて同じストレージを共有しますが、https://example.com/は異なるストレージになります。
  • --locationの指定がなく、--configで設定ファイルが指定されている場合は、その設定ファイルの絶対パスが使用されます。つまり、deno run --config deno.jsonc a.tsdeno run --config deno.jsonc b.tsは同じストレージを共有しますが、deno run --config tsconfig.json a.tsは異なるストレージになります。
  • --location--configも指定されていない場合、メインモジュールの絶対パスが使用されます。 また、Deno REPLは、Deno.cwd()をもとにメインモジュールを生成します。つまり、同じパスからREPLを複数回起動すると、同じストレージを共有します。

AbortSignalに理由が指定できるようになった

WHATWGAbortSignal理由を指定できるようにしたらしいのですが、これが速攻で取り込まれました。

本件のIssueを見ると、WHATWGのプルリクがマージされてから6時間くらいでDenoに取り込まれ、その翌日に今回のリリースという爆速対応。
@crawKatさんがやってくれたようです。
https://github.com/denoland/deno/issues/12695

dntがリリースされた

これはDeno 1.16の中身ではないのですが、ブログで紹介されているので記載します。

1.15でNode.js互換モード(--compat)が追加されました。
https://zenn.dev/uki00a/articles/node-compat-mode-introduced-in-deno-v1-15

これに関連し、Denoモジュールをnpmパッケージに変換するdntというツールがリリースされています。

https://github.com/denoland/dnt

以下のような変換が行われるようです。

  • Deno namespaceを使っている部分がdeno.nsを使うように変換される
  • 型チェック実行後、d.tsファイルを生成する
  • Deno用テストコードをNode.js用に変換し、さらにNode.js環境での実行確認を行う

ということで、一度設定して実行すれば、簡単にnpm moduleに変換できるとのことです。

例として、@kt3kさんのdeno_license_checkerが紹介されています。

https://github.com/kt3k/deno_license_checker
https://www.npmjs.com/package/@kt3k/license-checker

V8が9.7にバージョンアップした

V8が9.7にバージョンアップしました。1.15の時点では9.5だったので、マイナーバージョンが2つ上がったことになります。

https://v8.dev/blog/v8-release-97

WebAssemblyの参照型がサポートされた

JavaScriptからの外部参照をWebAssemblyモジュールで使えるようになりました。

これはV8 9.6で入った機能とのことです。

https://v8.dev/blog/v8-release-96

findLastおよびfindLastIndexが追加された

ArrayTypedArrayfindLastfindLastIndexが追加されました。
これに際し、同日リリースのstd@0.114.0にて、std/collectionsに入っていた同名のメソッドはdeprecatedとなっています。
https://zenn.dev/kawarimidoll/articles/4ea4219cf69225#findlastindex

おわりに

今回も盛りだくさんのリリースでした。

また、公式ブログで紹介されていない細かな調整も入っていますので、気になる方はGitHubのリリースページをご確認ください。

https://github.com/denoland/deno/releases/tag/v1.16.0

追記:11/11に修正版の1.16.1がリリースされました。
https://github.com/denoland/deno/releases/tag/v1.16.1

もうすぐDeno 2.0も来る…かな?

Discussion