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

この記事では主な変更点などについて解説します。

--checkオプション指定時のトランスパイルの挙動が変更

tscがトランスパイル用途では使用されなくなりました。

型チェックトランスパイル
--check (v1.23)tsctsc
--no-check (v1.23)-swc
--check (v1.24)tscswc
--no-check (v1.24)-swc

これにより、トランスパイルの効率化が見込まれます。

また、tscの実行結果をSQLiteにキャッシュする仕組みも導入されており、型チェックについても効率化が図られています。($DENO_DIR/check_cache_v1にSQLiteのデータベースファイルが作成されます)

FFI(Deno.dlopen)の改善

v1.23.3でV8 Fast API Calls+JITコンパイルの導入による最適化が実施されました。

今回のリリースでは、64ビット値を取り扱う関数に対してもV8 Fast API Callsを利用した最適化が適用されるように改善されています。

サブプロセスAPIへの変更

Deno v1.21 で入ったサブプロセスAPIに変更が加わっています。

Deno.Child.unref()Deno.Child.ref()のサポート

挙動としてはDeno.refTimerDeno.unrefTimerと同様で、Deno.Child.unref()を呼ぶと、対象の子プロセスがDenoの終了をブロックしなくなります。

const child = await Deno.spawnChild("some_long_running_process", { args });
child.unref();

(破壊的変更) Deno.Childの型定義が変更

Deno.spawnChildで返却されるDeno.Childのstdio関連のプロパティに関する型定義が変更されています。

v1.23.4

class Child<T extends SpawnOptions> {
  readonly stderr: T["stderr"] extends "inherit" | "null" ? null : ReadableStream<Uint8Array>;
  readonly stdin: T["stdin"] extends "piped" ? WritableStream<Uint8Array> : null;
  readonly stdout: T["stdout"] extends "inherit" | "null" ? null :
  // ...
}

v1.24.0

class Child {
  get stdin(): WritableStream<Uint8Array>;
  get stdout(): ReadableStream<Uint8Array>;
  get stderr(): ReadableStream<Uint8Array>;
  // ...
}

stdin/stdout/stderrがgetterに変更されており、"piped"が指定されていないストリームを参照した際に、TypeScriptの型エラーではなく実行時エラー(TypeError)が発生するように修正されています。

const child = Deno.spawnChild("echo", { args: ["foobar"], stdout: "null" });
const stream = child.stdout.pipeThrough(new TextDecoderStream()); // => v1.23.4までは型エラー、v1.24.0からは実行時エラー

(破壊的変更) Deno.SpawnOutputの型定義が変更

Deno.spawn()またはDeno.spawnSync()で返却されるDeno.SpawnOutputの型定義が変更されています。

v1.23.4

interface SpawnOutput<T extends SpawnOptions> {
  status: ChildStatus;
  stderr: T["stderr"] extends "inherit" | "null" ? null : Uint8Array;
  stdout: T["stdout"] extends "inherit" | "null" ? null : Uint8Array;
}

v1.24.0

interface SpawnOutput extends ChildStatus {
  get stderr(): Uint8Array;
  get stdout(): Uint8Array;
}

interface ChildStatus {
  code: number;
  signal: Signal | null;
  success: boolean;
}

こちらもDeno.Childと同様にジェネリクスを使用しないように型定義が変更されています。

また、Deno.SpawnOutputDeno.ChildStatusextendすることで、よりフラットな構造へ変更されています。

const output = await Deno.spawn("echo", { args: ["foobar"] });
output.success; // => true
output.signal; // => null

import.meta.resolve()がサポート

import.meta.resolve()が実装されました。

これを使うことで、

const worker = new URL("./worker.js", import.meta.url).href;

と同様の処理が下記のようにして実現できます。

const workerURL = import.meta.resolve("./worker.js");

また、import.meta.resolve()はImport mapsも解釈してくれます。

{
  "imports": {
    "redis": "https://deno.land/x/redis@v0.26.0/mod.ts"
  }
}

このようなImport mapsファイルがあった場合、import.meta.resolve()は下記のような結果を返してくれます。

import.meta.resolve("redis"); // => https://deno.land/x/redis@v0.26.0/mod.ts

"unhandledrejection"イベントのサポート

ブラウザと同様に、rejectされたcatchされていないPromiseが存在する場合、このイベントが発火されます。

addEventListener("unhandledrejection", (event) => {
  console.error(event.reason); // error: Uncaught (in promise) Error: foo
});

Promise.reject(new Error("foo"));

Window: unhandledrejection event

"beforeunload"イベントがサポート

Denoのイベントループが停止される直前に発火されます。

また、"beforeunload"のリスナ内で.preventDefault()を呼ぶとイベントループが再開されるため、改めてsetTimeout()などの非同期処理を呼ぶことが出来ます。

let called = false;
addEventListener("beforeunload", (event) => {
  if (!called) {
    called = true;
    event.preventDefault();
    setTimeout(() => {
      console.log(3);
    }, 50);
  }
});

setTimeout(() => {
  console.log(1);
  setTimeout(() => {
    console.log(2);
  }, 200);
}, 100);

// Output:
// 1
// 2
// 3

Window: beforeunload event

deno.json(c)deno testがカスタマイズできるように

deno lintdeno fmtなどと同様に、deno.json(c)deno testによるテスト対象をカスタマイズできるようになりました。

例えば、下記のように設定しておくと、vendorディレクトリ配下のテストコードが実行されなくなります。

{
  "test": {
    "files": {
      "exclude": ["vendor"]
    }
  }
}

deno testコマンドの実行時にテスト対象ファイルや--ignoreオプションが指定された際は、そちらがdeno.json(c)の設定内容よりも優先されます。


cli/schemas/config-file.v1.json#L325-L349

deno test--parallelオプションがサポート

挙動としては、既存の--jobsオプションと同様に、テストを並列実行したい場合に利用します。

$ DENO_JOBS=4 deno test --parallel

既存の--jobsとの違いとして、テスト実行の並列数を指定したい場合は、オプション引数ではなくDENO_JOBS環境変数に指定する必要があります。

--parallelの導入に伴い、--jobsは非推奨化されています。

dneo lspの改善

import-map-remapコードアクションがサポート

Import mapsファイルで指定されたマッピング情報を元に、import指定子を更新してくれます。

例えば、下記のようなImport mapsファイルがあったとします。

{
  "imports": {
    "preact": "https://esm.sh/preact@10.8.1"
  }
}

次に、下記のようなコードがあったとします。

import { render } from "https://esm.sh/preact@10.8.1";

上記のimportに対してimport-map-remapアクションを実行すると、下記のように、Import mapsファイルで指定された内容へ変換されます。

import { render } from "preact";

その他の改善点

  • checkJsオプションが有効化されている際に、@deno-typesなどによってjsファイルから参照されているtsファイルが存在する場合に、プロセスがパニックする問題が修正されています。
  • deno replでimport指定子が空の状態でタブ補完をしようとすると、プロセスがパニックする問題が修正されています。
  • deno task--cwdオプションを指定した際に、そこで指定されたディレクトリではなく、カレントディレクトリのdeno.jsonが読まれてしまう問題が修正されています。

参考