🦕

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

2021/03/03に公開

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

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

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

詳細なリリース内容は上記のリリースノートにまとまっていますが、ざっと紹介していきたいと思います。

  1. WebGPU の実験的サポート
  2. ICU サポート
  3. カバレッジツールの改良版 deno coverage
  4. Import maps が安定化
  5. module フェッチ時の Auth トークンサポート
  6. Deno.test の exit サニタイザ
  7. Deno.permissions 安定化
  8. Deno.link Deno.symlink 安定化
  9. Deno.metrics の粒度が細かくなった
  10. deno fmt で JSON をフォーマットできるようになった
  11. Deno.emit で IIFE をサポート
  12. deno lsp 安定化
  13. TypeScript 4.2 へとアップグレード

1. WebGPU の実験的サポート

1.8 の目玉機能です!
WebGPU API が実装されました。Deno から GPU を使うことができるようになります。
グラフィックのレンダリングはもちろん、GPU による高効率な機械学習・ディープラーニングの計算も可能になります。

昨今のニューラルネットワークはほとんどが Python で扱われますが、Deno のコアチームは Python ではなく JavaScript (TypeScript) もこの分野で理想的なプログラミング言語であると考えていて、今回の WebGPU サポートはその大きな第一歩となります。目下の目標は GPU アクセラレーションを有効にした状態で Tensorflow.js を Deno 上で実行できるようにすること、とのことです。

簡単なデモンストレーションコードです。GPU にアクセスし、名前とサポートされている機能を出力します。

// `deno run --unstable https://deno.land/posts/v1.8/webgpu_discover.ts`
// で実行できます

// アダプタを取得
const adapter = await navigator.gpu.requestAdapter();
if (adapter) {
  // アダプタの詳細を出力する
  console.log(`Found adapter: ${adapter.name}`);
  const features = [...adapter.features.values()];
  console.log(`Supported features: ${features.join(", ")}`);
} else {
  console.error("No adapter found");
}

また、以下のコマンドを実行すると、緑の背景に赤い三角形を表示する、という簡単なデモを実行できます。

$ deno run --unstable --allow-write=output.png https://raw.githubusercontent.com/crowlKats/webgpu-examples/f3b979f57fd471b11a28c5b0c91d0447221ba77b/hello-triangle/mod.ts

このコードの詳細は crowlKats/webgpu-examples リポジトリ を参照してください。

2. ICU サポート

ICU (International Components for Unicode) がサポートされました。これは Deno で 2 番目に要望の多かった機能だったらしいです。(1 番は Deno.compile だと思います)
これで、ICU に依存している JavaScript API はすべてブラウザ上と同じ挙動をするようになりました。

以下のように REPL 上で試すことができます:

$ deno
Deno 1.8.0
exit using ctrl+d or close()
> const d = new Date();
undefined
> d.toLocaleString("ja-JP-u-ca-japanese");
"R3/3/3 19:27:13"

今日は令和 3 年 3 月 3 日、ひな祭りですね!

3. カバレッジツールの改良版 deno coverage

Deno の組み込みテストカバレッジツールは、これまで deno test --coverage というコマンドでしたが、これが 2 つに分割されました。

  • カバレッジの収集を行う deno test --coverage
  • カバレッジレポートを作成する deno coverage

まず deno test --coverage <カバレッジプロファイルを保存するディレクトリ> として、プロファイルを収集します。その後、deno coverage <カバレッジプロファイルが含まれるディレクトリへのパス> とすることで、カバレッジレポートが出力されます。lcov 形式での出力にも対応しています。

codecov.io のようなサービスと連携することも容易に行えます。実際、Deno の標準ライブラリ deno_std では数日前から codecov.io を利用したカバレッジの集計を行っています。以下から閲覧可能です。
https://codecov.io/gh/denoland/deno_std

GitHub Actions でこの連携を行うためには 10 行追加するだけで OK です:

       - name: Run tests
-        run: deno test --unstable --allow-all
+        run: deno test --coverage=./cov --unstable --allow-all
+
+      - name: Generate lcov
+        run: deno coverage --unstable --lcov ./cov > cov.lcov
+
+      - name: Upload coverage
+        uses: codecov/codecov-action@v1
+        with:
+          name: ${{ matrix.os }}-${{ matrix.deno }}
+          files: cov.lcov

4. Import maps が安定化

Import maps が Chrome 89 で安定化されました。これに合わせて、Deno の Import maps も最新の仕様に適合するようアップデートされ、安定化されました。--unstable フラグ無しで以下のように実行することができるようになります。

$ deno run --import-map=./import_map.json ./mod.ts

また、以下のように --import-map に渡すパスとして、URL が利用できるようになりました。

$ deno run --import-map=https://example.com/import_map.json ./mod.ts

Deno では、以下のような "bare" [1] なインポートパス指定は本来サポートされていません。

import * as http from "std/http";

以下のような JSON ファイルを Import map として指定すれば、上記のようなインポートを行うことが可能になります。

{
  "imports": {
    "std/http": "https://deno.land/std@0.85.0/http/mod.ts"
  }
}

5. module フェッチ時の Auth トークンサポート

Deno はこれまで、認証が必要なサーバーからコードをダウンロードすることができませんでした。1.8.0 からは、ドメインごとに認証トークンを指定し、それを使ってモジュールをフェッチすることができるようになりました。
トークンの指定は環境変数 DENO_AUTH_TOKENS を通して行います。以下のように環境変数に値を入れます。

DENO_AUTH_TOKENS=a1b2c3d4e5f6@deno.land;f1e2d3c4b5a6@example.com:8080

これで 「deno.land には a2b2... というトークンを使い、example.com:8080 には f1e2... というトークンを使います」ということになります。

Deno はこの値を見て、モジュールをフェッチする際に Aurhotization ヘッダに Bearer {token} という値を入れたリクエストを送るようになります。

GitHub のプライベートリポジトリからコードを取得する、などについてのより詳細な解説が Private modules and repositories にあります。

6. Deno.test の exit サニタイザ

Deno でテストを書くための API Deno.test には 2 つのサニタイザ が備わっています。これは、テストコードでリソースのリークが起きていないことを確認してくれるものです。例えば、

  • テスト実行中に開かれたファイルやネットワークのハンドルが、テスト終了時に閉じられているか?
  • テスト終了時に、まだ実行中のシステムコールはないか?

といったことを確認してくれます。

1.8 から新たなサニタイザが追加され、Deno.exit() をテストコードの中で呼んでいないか?をチェックしてくれるようになりました。Deno.exit() をテストコードの中で実行すると false positive に繋がりかねず、もし Deno.exit() が残っているとしたら、それは間違った使い方をしているか単なる消し忘れのどちらかである可能性が高いからです。

このサニタイザはデフォルトで有効となっていますが、以下のように sanitizeExitfalse にすれば無効にすることもできます。

Deno.test({
  name: "false success",
  fn() {
    Deno.exit(0);
  },
  sanitizeExit: false,
});

// 上のテスト内で `Deno.exit(0)` があり、そこでプログラムの実行が終了するので
// このテストケースは絶対に実行されない
Deno.test({
  name: "failing test",
  fn() {
    throw new Error("this test fails");
  },
});

上記のサンプルは deno test https://deno.land/posts/v1.8/exit_sanitizer.ts で試すことができます。

7. Deno.permissions 安定化

これまで、パーミッションを付与するためには起動時にオプションとして渡してあげる必要がありました。例えばネットワークアクセスを行う場合は deno run --allow-net foo.ts といった感じです。ほとんどの場合はこれで事足りますが、起動時ではなく実行中にパーミッションを動的に変更したいケースがあるかもしれません。そのようなときに使える以下の 3API が安定化されました。

  • Deno.permissions.query 現在のパーミッションの状態を取得
  • Deno.permissions.request パーミッションを新しく要求する
  • Deno.permissions.revoke パーミッションを取り消す

以下は、環境変数へのアクセスを行うための env パーミッションを実行時にいじくるサンプルスクリプトです。
(deno run https://deno.land/posts/v1.8/permission_api.ts で手元で試すこともできます)

function homedir() {
  try {
    console.log(`Your home dir is: ${Deno.env.get("HOME")}`);
  } catch (err) {
    console.log(`Failed to get the home directory: ${err}`);
  }
}

// ホームディレクトリの取得を試みる
// (まだ env パーミッションが無いので、これは失敗するはず)
homedir();

// ここで env パーミッションをリクエストする
const { granted } = await Deno.permissions.request({ name: "env" });
if (granted) {
  console.log(`You have granted the "env" permission.`);
} else {
  console.log(`You have not granted the "env" permission.`);
}

// 再度ホームディレクトリの取得を試みる
// 上記のリクエストでユーザーが許可した場合は成功する
homedir();

// env パーミッションを revoke する
await Deno.permissions.revoke({ name: "env" });

// またホームディレクトリの取得を試みる
// env パーミッションを revoke したので、また失敗するようになっている
homedir();

8. Deno.link Deno.symlink 安定化

symlink に関する以下の 4 API が安定化されました。

  • Deno.link
  • Deno.linkSync
  • Deno.symlink
  • Deno.symlinkSync

これらを利用するためには適切なパーミッションを付与する必要があります。
Deno.linkDeno.linkSync は、source と target のどちらに対しても read と write のパーミッションが必要です。
Deno.symlinkDeno.symlinkSync は target に対して write パーミッションが必要です。

9. Deno.metrics の粒度が細かくなった

Deno では、例えばファイルをオープンする、といった処理の実体は Rust で書かれています。つまり、Deno.open 関数を呼び出すと、Rust で実装された op_open_async というものが呼び出されます。このように Rust 側へと移譲される処理のことを "ops" [2] と呼びます。
Deno ではこの ops のメトリクスを取るための API として Deno.metrics があります。この API は 同期 ops と 非同期 ops それぞれの開始した数と完了した数、ops 境界を越えてやり取りされたデータの量、などを提供していますが、これまでは「どの op がどれだけ実行されたのか?」といった、op 単位でのデータをとることができませんでした。
ここが改善され、--unstable フラグをつけて Deno を実行した場合に、op 単位でのメトリクスを取ることができるようになりました。

以下は REPL で実行してみた例です。

$ deno --unstable
Deno 1.8.0
exit using ctrl+d or close()
> Deno.metrics().ops["op_open_async"]
undefined
> await Deno.open("./README.md")
File {}
> Deno.metrics().ops["op_open_async"]
{
  opsDispatched: 1,
  opsDispatchedSync: 0,
  opsDispatchedAsync: 1,
  opsDispatchedAsyncUnref: 0,
  opsCompleted: 1,
  opsCompletedSync: 0,
  opsCompletedAsync: 1,
  opsCompletedAsyncUnref: 0,
  bytesSentControl: 54,
  bytesSentData: 0,
  bytesReceived: 22
}

これを見ると、Deno.open によって実行される op_open_async という op のメトリクスを Deno.metrics().ops["op_open_ascyn"] で取得することができているのが分かります。
今後、この粒度のあがったメトリクスを利用して、Deno.test を実行した際のリソースリークエラーの表示が分かりやすくなる予定です。

10. deno fmt で JSON をフォーマットできるようになった

Deno の組み込みフォーマッタ deno fmt.json および .jsonc のフォーマットができるようになりました。また、markdown ファイルの中にある JSON / JSONC のコードブロックに対してもフォーマットが適用されます。

11. Deno.emit で IIFE 形式の出力をサポート

Deno.emit は Deno の組み込みバンドラ API です。この API で IIFE (Immediately Invoked Function Expression) 形式の出力を行うことができるようになりました。
IIFE って何?と思った方(僕も含め)は例を見ると分かりやすいと思います。以下のように Deno.emit を実行してみます。

const { files } = await Deno.emit("/a.ts", {
  bundle: "iife", // "iife" を指定していることに注意
  sources: {
    "/a.ts": `import { b } from "./b.ts";
        console.log(b);`,
    "/b.ts": `export const b = "b";`,
  },
});

console.log(files["deno:///bundle.js"]);

bundle: "iife" を指定しています。これを実行すると、以下のような結果が得られます。
(deno run --unstable https://deno.land/posts/v1.8/emit_iife.ts とすることで手元でも試すことができます)

(function () {
  const b = "b";
  console.log(b);
  return {};
})();

関数を作って即座に呼び出すスタイルですね。これが Immediately Invoked Function Expression です。

これで、ES Module をサポートしていない古いブラウザのためのバンドルを生成することができるようになりました。

12. deno lsp 安定化

Deno 組み込みの Language Server として v1.6 で追加された deno lsp ですが、v1.7.5 にて安定化され、公式の VSCode 拡張 もこの組み込み LSP を利用するようになりました。
組み込み LSP は Deno の他の CLI ツールと同じアーキテクチャで実装されているので、よりシームレスで一貫性のある開発体験が得られることが期待されます。

deno lsp を利用する形で、さまざまなエディタでコミュニティベースのプラグインが開発されています。

13. TypeScript 4.2 へとアップグレード

Deno に内蔵されている TypeScript のバージョンが 4.2 に上がりました。

おわり

以上、1.8.0 の紹介でした。
次のマイナーバージョン 1.9.0 のリリースは 2021 年 4 月 13 日の予定です。

過去のリリース情報

1.7.0

https://zenn.dev/magurotuna/articles/55575eb16ae422

1.6.0

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

脚注
  1. なんて訳せばいいか分からないので逃げました ↩︎

  2. operations の略…だと思います ↩︎

Discussion