Deno 1.7.0 がリリースされたので新機能や変更点の紹介

9 min read読了の目安(約8900字

deno illust
Copyright (c) 2018-2021 the Deno authors. MIT License.

日本時間の今日(2021年1月20日)に Deno の v1.7.0 がリリースされました。

https://deno.land/posts/v1.7

詳細なリリース内容は上記のリリースノートにまとまっていますが、いくつか日本語でご紹介したいと思います。

なお、v1.6.0 (2020年12月9日リリース)の紹介記事は以下になります。よろしければこちらもご覧ください。

https://zenn.dev/magurotuna/articles/020f6b103937ed

今回は以下の点を紹介します。

  1. deno compile コマンドの改善
  2. Data URL 形式の import に対応
  3. DNS 解決を行う Deno.resolveDns API の追加
  4. Deno 内部の TypeScript コンパイラを使うための API を Deno.emit に統合
  5. deno fmt コマンドが Markdown のフォーマットに対応
  6. Web Stream API を最新の仕様に合わせる形に修正
  7. Web Worker のパーミッションを設定できるように
  8. globalThis.location に対応、相対パスでの fetch が可能に
  9. fetch で、リクエストボディをストリームにすることができるように
  10. TLS セッションのキャッシュをサポート
  11. テストカバレッジをとるためのコマンド deno test --coverage の改善
  12. 内部で利用している非同期ランタイムライブラリ Tokio 1.0 へのアップグレード

1. deno compile コマンドの改善

1.6.0 で deno compile コマンドが追加されました。これは Deno の実行環境と実行コードをひとまとめにすることで、スタンドアローンで実行できるシングルバイナリを生成する機能です。
この deno compile コマンドに関して、3つの改善がなされました。

クロスコンパイルに対応

Windows、macOS、Linuxの間でクロスコンパイルが可能になりました。例えば、Linux 上で Windows や macOS 向けのバイナリを生成できるということです。

生成バイナリのサイズが 40-60% 削減された

deno compile コマンドは、Deno の実行環境をまるごとパッキングするので、生成されるバイナリのサイズが結構大きいです。具体的には、Deno 1.6.0 では、シンプルなスクリプトであっても 50MB ほどのサイズになってしまっていました。
1.7.0 では、--lite というフラグが新たにサポートされました。deno compile --lite とすることで、生成バイナリのサイズを削減することができます。これによってシンプルなプログラム、例えば Hello World のようなプログラムが、30MB ほどまで削減されます。

Deno のパーミッション指定やコマンドライン引数などもパッキングできるようになった

1.6.0 では、実行時にコマンドライン引数で渡すようなパラメータをパッキングすることはできませんでした。例えば、ネットワークアクセスを行うプログラムを書いたとき、Deno では --allow-net というフラグを明示的につけなければパーミッションエラーとなりますが、このフラグは「コンパイル後のバイナリを実行する際に」つけなければなりませんでした。
1.7.0 ではこの点が改善され、「コンパイル時にフラグをつける」ことができるようになりました。フラグ付きでコンパイルされて生成されたバイナリは、フラグをつけずに起動させることができます。

CA 証明書、V8 のフラグ、Deno のパーミッションフラグ、その他の事前入力するコマンドライン引数を含めることができるとのことです。

2. Data URL 形式の import に対応

Data URL 形式のデータを import できるようになりました。

export const a = "a";

export enum A {
  A,
  B,
  C,
}

というようなコードを用意して、これを Data URL に変換したものを以下のように import してみます。

import_data_url.ts
import * as a from "data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=";

console.log(a.a);
console.log(a.A);
console.log(a.A.A);

deno run で実行すると、きちんとした結果が出力されるのが確認できます。

$ deno run import_data_url.ts
a
{ "0": "A", "1": "B", "2": "C", A: 0, B: 1, C: 2 }
0

動的インポート、Web Worker にも対応しています。

3. DNS解決を行う Deno.resolveDns API の追加

Deno はこれまで DNS 解決を行うための API が存在していませんでした。1.7.0 で、unstable API ではありますが、Deno.resolveDns という形で DNS 解決 API が実装されました。
今のところは TCP/UDP による lookup のみ対応していて、またサポートされているレコードタイプは AAAAAANAMECNAMEMXPTRSRVTXT のみです。

簡易的な dig コマンドを作ってみます。

dig.ts
const domainName = Deno.args[0];
if (!domainName) {
  throw new Error("Domain name not specified in first argument");
}

const records = await Deno.resolveDns(domainName, "A");
for (const ip of records) {
  console.log(ip);
}

コマンドライン引数から解決対象のドメインを受け取って、Aレコードを lookup するスクリプトです。実行してみます。

$ deno run --allow-net --unstable dig.ts deno.land
104.21.18.123
172.67.181.211

4. Deno 内部の TypeScript コンパイラを使うための API を Deno.emit に統合

Deno に組み込まれている TypeScript コンパイラにアクセスするための API として、Deno.transpileOnlyDeno.bundleDeno.compile という3つの API が提供されていましたが、これらを1つにまとめ、改良を加えた Deno.emit API が登場しました。

5. deno fmt コマンドが Markdown のフォーマットに対応

Deno の組み込みフォーマッタである deno fmt コマンドが、Markdown のフォーマットに対応しました。これで .js .jsx .ts .tsx に加えて .md ファイルのフォーマットができることになります。

6. Web Stream API を最新の仕様に合わせる形に修正

Deno は Web 標準に可能な限り準拠することを目指しています。ReadableStream クラス は、Deno に実装されたときの仕様では getIterator というメソッドをもっていたのですが、最新の仕様ではこのメソッドが削除されました。この仕様変更に追従するため、Deno の ReadableStreamgetIterator メソッドが deprecated とされました。Deno 1.8.0 でこの deprecated API が完全に削除される予定です。

Deno 標準ライブラリを使っているユーザーは、バージョン 0.84.0 以降にアップデートすれば問題ありません。また、ReadableStream を直接使ったコードを書いている場合は、以下のように書き換えれば OK です。

- for await (const item of body.getIterator()) {
+ for await (const item of body) {

7. Web Worker のパーミッションを設定できるように

Web Worker にパーミッションを指定することができるようになりました。--allow-read のパーミッションを付与した Worker を起動する、といったことが可能になったということです。
注意が必要なのは、Worker に指定するパーミッションは、Deno そのものを起動するときに指定するパーミッションのサブセットになっていないといけない、ということです。つまり、Deno の起動時に何もオプションをつけていないにも関わらず、Worker には --allow-read 権限を与える、といったことはできないということです。

read 権限をつけた Worker を立ち上げてカレントディレクトリの log.txt を読み取って console.log するプログラムの例が以下です。

worker_permissions.ts
const workerUrl = new URL("worker_permissions_worker.ts", import.meta.url).href;
const worker = new Worker(workerUrl, {
  type: "module",
  deno: {
    namespace: true,
    permissions: {
      read: true,
    },
  },
});

worker.postMessage({ cmd: "readFile", fileName: "./log.txt" });
worker_permissions_worker.ts
self.onmessage = async function (e) {
  const { cmd, fileName } = e.data;
  if (cmd !== "readFile") {
    throw new Error("Invalid command");
  }
  const buf = await Deno.readFile(fileName);
  const fileContents = new TextDecoder().decode(buf);
  console.log(fileContents);
  self.close();
};

実行してみましょう。--allow-read をつける必要があることをお忘れなく。

$ echo "hello world" > ./log.txt
$ deno run --allow-read --unstable https://deno.land/posts/v1.7/worker_permissions.ts
hello world

8. globalThis.location に対応、相対 URL での fetch が可能に

--location オプションを付与して実行すると、globalThis.location に値がセットされるようになりました。 値としては http または https 形式の URL を指定できます(実在する URL でなくても OK)。
--location によって値がセットされている場合には、fetch や Web Worker の URL として 相対 URL が使われるようになります。例を見てみましょう。

example.ts
console.log(globalThis.location.href);

const res = await fetch("/std/version.ts");
console.log(res.status, res.url);
console.log(await res.text());

--location="https://deno.land" をつけて起動してみます。上記の fetch 内で指定している /std/version.ts が相対 URL として解釈されて、https://deno.land/std@0.83.0/version.ts にアクセスしていることが分かります。

$ deno run --location="https://deno.land" --allow-net example.ts
https://deno.land/
200 https://deno.land/std@0.83.0/version.ts
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
/** Version of the Deno standard modules
 *
 * Deno std is versioned differently than Deno cli because it is still unstable;
 * the cli's API is stable. In the future when std becomes stable, likely we
 * will match versions with cli as we have in the past.
 */
export const VERSION = "0.83.0";

9. fetch で、リクエストボディをストリームにすることができるように

fetch のリクエストボディとして、ストリームを使うことができるようになりました。巨大なファイルをサーバーにアップロードしたいとき、普通のやり方だと最初にファイル全体をメモリにロードする必要があります。ストリームを使えば、まとめてメモリ上にロードする必要がなくなり、メモリにとっても優しいです。

使い方は以下の例を参照してください。fetchbody フィールドに ReadableStream を指定します。

import { readableStreamFromAsyncIterator } from "https://deno.land/std@0.84.0/io/streams.ts";

// サーバーにアップロードしたいファイルを開く
const file = await Deno.open("./large_file_on_disk.txt");

// Deno.Reader 型(Deno.open の戻り値)から `ReadableStream` を作る
const body = readableStreamFromAsyncIterator(Deno.iter(file));

// サーバーに送信する
const res = await fetch("https://myfileserver.com/upload", {
  method: "POST",
  body,
});

現時点では、「リクエストボディをすべて送り終わってからでないとレスポンスを受け取ることができない」という制限があります。これは fetch の仕様上の制約ではなく、実装上の都合です。将来的には解決される見込みです。

10. TLS セッションのキャッシュをサポート

これまで Deno は TLS セッションのキャッシュに対応しておらず、接続のたびに毎回 TLS セッションを確立し直していました。1.7.0 でこの状況が改善され、TLS セッションのキャッシュをサポートするようになり、コネクションの使い回しができるようになりました。

11. テストカバレッジをとるためのコマンド deno test --coverage の改善

Deno は組み込みのテストランナー deno test を備えていて、--coverage フラグをつけることでカバレッジを収集することもできます。
今回このカバレッジにも改良が加えられ、テストコードがサブプロセスを立ち上げた場合にそのサブプロセスによって実行された箇所のカバレッジも収集することができるようになりました。さらに、部分的にカバーされた行のカバレッジを正しく集計するようになりました。

12. 内部で利用している非同期ランタイムライブラリ Tokio 1.0 へのアップグレード

先月、Tokio 1.0 がリリースされました。Deno は多くの点で Tokio のエコシステムを利用していて、エコシステム全体が Tokio 1.0 に対応するのに歩を合わせ、1.0 への移行を成し遂げました。
移行に合わせて、Deno の内部構造が大幅に再構築されました。具体的に言うと、ResourceTable というファイルハンドルや TCP コネクションなどを管理する構造がゼロから書き直されました。これによって Deno 内部の構造がかなりきれいになって、安定性の向上にもつながると見られています。

その他

WPT

Deno は Web 標準に可能な限り準拠することを目指しています(2回目)が、これを強力に推進するために Web Platform Tests (通称 WPT)を走らせることになりました。WPT は、Web プラットフォーム仕様にちゃんと沿っていることを確認するためにブラウザベンダが利用しているテストです。

https://github.com/web-platform-tests/wpt

やるべきことが非常にたくさんあるので、コントリビュートのチャンスです。興味のある方は こちらの tracking issue にて進捗状況を確認してみてください。

Deno survey

また、最近 Deno コアチームによる技術サーベイが始まりました。Deno の現状の使われ方と、将来求められていることを集計して、今後の開発方針を決めることなどに使われるそうです。
英語ですが、10分ほどで終わる内容ですので、すでに Deno を使っている方も、興味はあるけどまだ何らかの理由があって使っていない方も、ぜひ回答してみてください。

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

おわり

手前味噌ですが Deno.resolveDns僕が実装した(多くの方のアドバイスをもらいながら)ので、たくさん使われると嬉しいなと思いつつ、変なバグがあったら怖いなと思っていたりもします。フィードバックお待ちしています。

次のマイナーバージョンアップデート、Deno 1.8 は 2021年3月2日(おそらく日本時間だと3日) に予定されています。2021年の Deno の発展が楽しみですね。

この記事に贈られたバッジ