Deno v1.21がリリースされました。

この記事では新機能や修正点などについて解説します。

deno checkコマンドの導入

新しいコマンドとしてdeno checkコマンドが追加されました。

挙動としては基本的にdeno cacheコマンドと同等で、与えられたモジュールの型チェック及びリモートモジュールのダウンロードを行います。

$ deno check mod.ts

deno cacheコマンドとの違い

deno cacheコマンドとの大きな違いとして、deno checkコマンドはデフォルトではリモートモジュールの型チェックを行いません。

deno checkコマンドでリモートモジュールの型チェックも行いたい場合は--remoteオプションを指定する必要があります。

deno runコマンドでの型チェックについて

deno checkコマンドの追加による影響として、将来的にdeno runコマンドではデフォルトで型チェックが行われなくなる予定です。(Deno v1.21時点でもDENO_FUTURE_CHECK=1環境変数を設定することで、この挙動を有効化できます)

型チェックを行いたいときは、今回追加されたdeno checkコマンドまたはdeno run --checkで明示的に行う必要が出てきます。

新しいサブプロセスAPI (unstable)

DenoにはDeno.run()というサブプロセスを生成するためのAPIがありますが、今回、新しく3種類のAPIが追加されました:

  • Deno.spawn() (高レベル)
  • Deno.spawnChild() (低レベル)
  • Deno.spawnSync() (Deno.spawnの同期版)

これらのAPIの利用にはDeno.run()と同様に--allow-runパーミッションが必要です。

また、これらのAPIの追加により、Deno.run()は非推奨化される予定のようです。

Deno.spawn()

これは高レベルAPIという位置づけです。

指定したコマンドを実行し、そのコマンドの実行結果(status)や標準出力(stdout)及び標準エラー出力(stderr)への出力内容を返却してくれます。

const { stdout, status } = await Deno.spawn("echo", {
  args: ["foo"],
});
console.assert("foo\n" === new TextDecoder().decode(stdout));
console.assert(status.success);

コマンドの出力結果のみが必要で、細かな制御はいらない場合に便利かと思います。

Deno.spawnChild()

こちらは低レベルAPIという位置づけです。

Deno.Reader/Deno.WriterではなくReadableStream/WritableStreamをベースに実装されています。

const child = Deno.spawnChild("deno", {
  args: ["--version"],
});

for await (const output of child.stdout.pipeThrough(new TextDecoderStream())) {
  console.log(output);
}

プロセスの実行結果を確認したいときは、statusプロパティを利用できます。

const child = Deno.spawnChild("deno", {
  args: ["fmt", "sample.js"],
});

const status = await child.status;
console.assert(status.success);

.output()を使うことで、Deno.spawn()と同様に出力内容やステータスなどを取得できます。

const child = Deno.spawnChild("gh", {
  args: ["issue", "list"],
});

const { stdout, status } = await child.output();

console.assert(status.success);
console.log(new TextDecoder().decode(stdout));

また、Deno.runとは異なり、.close()によって明示的にプロセスを閉じる必要がなくなっています。

Deno.spawnSync

使い方はDeno.spawn()と基本的には同じですが、こちらは同期的に実行結果を返却します。

const { stdout, status } = Deno.spawnSync("echo", {
  args: ["baz"],
});

console.assert(status.success);
console.assert("baz\n" === new TextDecoder().decode(stdout));

deno lspの改善

  • deno.json(c)で定義されたタスクの実行をサポート
    • vscode-denoを入れておけば、Tasks: Run Taskでタスクを実行できます
  • ワークスペース内の特定ディレクトリでのみLSPを有効化できるようになりました ("enablePaths"オプション)
  • vscodeのTesting APIがサポート
    • Testing: Focus on Test Explorer Viewコマンドを実行すると、テストケースの一覧を表示できます

deno testの改善

TestContextオブジェクトにテストケースに関するメタデータが追加されています。(name, origin, 及びparentプロパティ)

Deno.test("math", async (t) => {
  assert(t.name === "math");
  assert(t.origin === Deno.mainModule);
  await t.step("sum", (t) => {
    assert(t.parent.name === "math");
    assert(t.name === "sum");
    assert(6 === sum(1, 2, 3));
  });
});

また、テストケースの実行時にconsole.logなどで出力された内容の取り扱いも改善されています。

具体的には、以下のように出力内容がフォーマットされて表示されます。

$ deno test sample_test.js
running 1 test from ./sample_test.js
foo ...
------- output -------
{ message: "foo" }
baz
----- output end -----
ok (6ms)

test result: ok. 1 passed; 0 failed; 0 ignored;
 0 measured; 0 filtered out (21ms)

また、テストケース中でエラーが発生した際のレポート内容も改善されています。

その他にも、deno_stdへのtesting/bdd.tstesting/mock.tsモジュールの追加なども行われており、テストを記述する際の利便性が向上しています。

deno fmtdeno lintでインクリメンタルなキャッシュがサポート

deno fmtdeno lintの高速化のため、これらのコマンドの実行結果をインクリメンタルにキャッシュする仕組みが導入されました。

内部的にはSQLiteを使用してキャッシュの仕組みが実装されており、以下のファイルにデータベースが保存されます。

  • $DENO_DIR/fmt_incremental_cache_v1 (deno fmtの実行結果)
  • $DENO_DIR/lint_incremental_cache_v1 (deno lintの実行結果)

また、deno fmt.gitディレクトリを無視する修正も行われており、これらの変更により、大幅なパフォーマンスの向上が見込まれるようです。

deno benchの改善

Deno v1.20で実装されたdeno benchの改善が行われています。

大きな変更点としてnオプションとwarmupオプションが削除されています。

今後は、Deno本体が信頼のある結果を得られるまで繰り返しベンチマークを実行してくれます。

Deno.bench("URLPattern", () => {
  const pattern = new URLPattern({ pathname: "/users/:id" });
  pattern.test("http://locahost:3000/users/123");
});

またレポートの出力内容が大幅に改善されており、結果が見やすくなっています。

benchmark       time (avg)             (min … max)       p75       p99      p995
-------------------------------------------------- -----------------------------
URLPattern  210.77 µs/iter (198.87 µs … 634.98 µs) 208.06 µs 293.66 µs 314.84 µs

その他にも、ベンチマークのグループ化もサポートされています。

groupオプションによってグループを定義することができ、これにより、同じgroup内のbaseline: trueが指定されたケースに対する相対的なパフォーマンスが報告されます。

Deno.bench({
  name: "add",
  group: "math",
  baseline: true,
  fn: () => {
    add(1, 2);
  },
});

Deno.bench({
  name: "sum",
  group: "math",
  fn: () => {
    sum(1, 2, 3, 4, 5);
  },
});

レポート:

benchmark      time (avg)             (min … max)       p75       p99      p995
------------------------------------------------- -----------------------------
add        169.17 ns/iter    (157.29 ns … 249 ns) 169.19 ns 221.56 ns 246.76 ns
sum        192.72 ns/iter (187.26 ns … 255.52 ns) 193.52 ns  228.4 ns 230.39 ns

summary
  add
   1.14x times faster than sum

reportError()のサポート

reportError()がサポートされました。

このAPIを使用することで、明示的にUncaught errorを発生させることができます。

また、reportError()で報告されたエラーは"error"イベントで補足できます。

addEventListener("error", e => {
  console.error(e.error);
  e.preventDefault(); // これを呼ばないとexit code=1で終了する
});
reportError(new Error("foo"));

deno replの改善

REPL内で新しくclear()関数が利用できるようになりました。(挙動はconsole.clear()と同等です)

また、--eval-fileオプションが追加され、REPLの起動前に読み込みたいファイルを指定できるようになりました。

$ deno repl --eval-file=sum.js
Deno 1.21.0
exit using ctrl+d or close()
> sum(1, 2, 3)
6

その他にも、REPL内でimportされたモジュールの型チェックが無効化されるように変更されています。

Deno.Listenerrefunrefメソッドが追加 (unstable)

unref()が呼ばれたListenerはプロセスの終了をブロックしなくなります (タイマにDeno.unrefTimerを呼んだときと同様の挙動をします)

これらのAPIはdeno_std/node/netでのServer.ref()Server.unref()の実装で利用されています。

DENO_NO_PROMPT環境変数のサポート

DENO_NO_PROMPT環境変数がサポートされました。

この環境変数を設定すると、Deno v1.19で有効化されたプロンプトの挙動を無効化できるようになりました。(--no-promptを指定したときと同様の挙動です)

その他の変更点

deno rundeno cacheの引数に// @ts-checkコメントが付与されたJavaScriptファイルを指定した場合、型チェックが行われない問題が修正されています。

参考