虎の穴開発室ブログ

虎の穴ラボ株式会社所属のエンジニアが書く技術ブログです

MENU

Deno 1.22 へのアップデートと変更事項まとめ

皆さんこんにちは。おっくんです。

去る 2022 年 5 月 18 日に Deno 1.22 がリリースされました。 今回も、リリースノートを参考に 変更事項の気になるところを紹介します。

Deno 1.22

Deno 1.22 での変更事項をDeno 1.22 リリースノートを元に確認します。

deno.com

型チェックのデフォルト動作が変更されました

Deno 1.21 で発表されていた型チェックの動作変更が、このリリースで部分的に適用されています。

「外部モジュールを含めた型チェック」から「ローカルコードの型チェック」に数多くのコマンドが変更になりました。

Deno 1.22 リリースノートに、現在の型チェックの導入状況が非常に丁寧な表で掲載されているのでそちらをご覧いただきたいと思います。

また、こういった型チェック動作の変更の中で、--no-check=remote--check に置き換わることも予定されています。

Deno.emit()、Deno.formatDiagnostics()、Deno.applySourceMap() が廃止に

Deno.emit() は、プログラムから、ソースコードをトランスパイルやバンドルするAPIです。 この機能は、トランスパイプラインをより複雑にする要因になっているため、Deno 本体から削除されました。 また関連して、Deno.formatDiagnostics()Deno.applySourceMap() も削除されました。

機能は、deno_emit に切り出されました。

Web Worker が、名前空間 Deno をデフォルトで使用できるようになりました

Web Worker API は、Deno 1.0 からサポートされていました。 しかし、名前空間 Deno はオプションで明示的に有効にする必要があるものでした。 このリリースより、名前空間 Deno を標準で使用することができるようになります。

試してみます。

次の 名前空間 Deno を使用した Worker を用意します。

[worker.js]

console.log(Deno.env.get("ENV_DATA"));
self.postMessage("worker=>main");

こちらを使用して各環境とオプションの設定での動作状況は次のようになります。 注意事項として、denoのオプションを Worker の引数にわたすためには、--unstable の付与が必要です。

// Deno 1.21 + オプション無 => 名前空間 Deno を使用できない
const worker = new Worker(new URL("./worker.js", import.meta.url), {
  type: "module",
});
worker.onmessage = function (e) {
  worker.terminate();
};
// 実行
// $ ENV_DATA=ENV_STRING deno run --allow-read=./worker.ts,./sample.txt --allow-env=ENV_DATA use_worker.ts
//   error: Uncaught (in worker "") ReferenceError: Deno is not defined
//   console.log(Deno.env.get("ENV_DATA"));
//               ^
//       at file:///usr/src/app/worker.ts:1:13
//   error: Uncaught (in promise) Error: Unhandled error in child worker.
//       at Worker.#pollControl (deno:runtime/js/11_workers.js:170:21)
// Deno 1.21 + オプション有 => 名前空間 Deno を使用できる
const worker = new Worker(new URL("./worker.js", import.meta.url), {
  deno: true, // <= 追記
  type: "module",
});
worker.onmessage = function (e) {
  worker.terminate();
};
// 実行
// $ ENV_DATA=ENV_STRING deno run --allow-read=./worker.ts,./sample.txt --allow-env=ENV_DATA --unstable use_worker.ts
// => ENV_STRING
//    sample
// Deno 1.22 + オプション無 => 名前空間 Deno を参照できる
const worker = new Worker(new URL("./worker.js", import.meta.url), {
  type: "module",
});
worker.onmessage = function (e) {
  worker.terminate();
};
// 実行
// $ ENV_DATA=ENV_STRING deno run --allow-read=./worker.ts,./sample.txt --allow-env=ENV_DATA use_worker.ts
// => ENV_STRING
//    sample

また、workerに許可するパーミッションをプログラムで指定することができるようになっています。

// Deno 1.22 + オプション => 名前空間 Deno を使用できる
const worker = new Worker(new URL("./worker.js", import.meta.url), {
  deno: {
    permissions: {
      read: true,
    },
  },
  type: "module",
});
worker.onmessage = function (e) {
  worker.terminate();
};
// $  ENV_DATA=ENV_STRING deno run --allow-read=./worker.ts,sample.txt --allow-env=ENV_DATA --unstable use_worker.ts
// =>⚠️  ️Deno requests read access to all. Run again with --allow-read to bypass this prompt.
//      Allow? [y/n (y = yes allow, n = no deny)]  y
//   ⚠️  ️Deno requests env access to "ENV_DATA". Run again with --allow-env to bypass this prompt.
//      Allow? [y/n (y = yes allow, n = no deny)]  y
//   ENV_STRING
//   sample

以下の、Worker の作成時に渡しているオブジェクトは、WorkerOptions です。

{
  deno: {
    permissions: {
      read: true,
    },
  },
  type: "module",
}

これは、Denoの拡張の機能で、unstable な機能です。

--no-config フラグを追加

Deno 1.18 で、設定ファイル deno.json を自動的に検出する機能が導入されました。 このリリースでは、この自動検出機能を停止する --no-config フラグが追加されました。 このフラグを使用した時には、すべてのオプションをコマンドラインオプションで設定する必要です。

Navigator.userAgent が追加

グローバル空間の Navigator に、userAgent プロパティが増えました。 HTTP リクエストする際にヘッダーに設定される値と同じものがuserAgent プロパティで取得できます。

$ deno
Deno 1.22.0
exit using ctrl+d or close()
> navigator.userAgent
"Deno/1.22.0"

Deno.resolveDns() APIがアップデート

Deno.resolveDns() はDeno 1.7で登場しDeno 1.15 で安定版になったDNSに問い合わせを行うためのAPIです。 これまでは、A レコードのみ取り扱いができましたが、このリリースで NS CAA SOA NAPTR レコードを取り扱えるようになりました。

新たに増えたすべてのレコードが登録されたドメインとして例に出せる適切なものが用意できないので、NS と、SOA レコードのみ抜粋で紹介します。

const nameServer = {
  nameServer: { ipAddr: "8.8.8.8", port: 53 },
};

const [ns, soa] = await Promise.all([
  Deno.resolveDns("yumenosora.co.jp", "NS", nameServer),
  Deno.resolveDns("yumenosora.co.jp", "SOA", nameServer),
]);

console.log("NS", ns);
// => NS [ "01.dnsv.jp.", "02.dnsv.jp.", "03.dnsv.jp.", "04.dnsv.jp." ]

console.log("SOA", soa);
// => SOA [
//      {
//        mname: "01.dnsv.jp.",
//        rname: "hostmaster.dnsv.jp.",
//        serial: 1528709533,
//        refresh: 3600,
//        retry: 900,
//        expire: 604800,
//        minimum: 300
//      }
//    ]

新しい Response.json() が追加

新しいグローバル空間の Response に静的メソッドの .json() が追加されます。 この .json() という名前から、json を返してくれるメソッドに見えますが、jsonを入力にオブジェクトを生成するメソッドです。 旧来の書き方を紹介し、新しい書き方を紹介します。

// 旧来の書き方
const json = { prop: "hellow deno" };

const body = JSON.stringify(json);
const response = new Response(body, {
  headers: { "content-type": "application/json" },
});

console.log(response)
// => Response {
//      body: ReadableStream { locked: false },
//      bodyUsed: false,
//      headers: Headers { "content-type": "application/json" },
//      ok: true,
//      redirected: false,
//      status: 200,
//      statusText: "",
//      url: ""
//    }

const b = await response.body!;
console.log(new TextDecoder().decode((await b.getReader().read()).value));
// => {"prop":"hello deno"}
// Response.json() を使用した新しい書き方
const json = { prop: "hello deno" };

const response = Response.json(json);
console.log(response)
// => Response {
//     body: ReadableStream { locked: false },
//     bodyUsed: false,
//     headers: Headers { "content-type": "application/json" }, 
//     ok: true,
//     redirected: false,
//     status: 200,
//     statusText: "",
//     url: ""
//   }

const b = await response.body!;
console.log(new TextDecoder().decode((await b.getReader().read()).value));
// => {"prop":"hello deno"}

Response.json()では、headers の設定も記述が不要になり、 Response オブジェクトがスッキリ作れるようになっています。 Deno の場合、std/http モジュールでAPIサーバーを作っているような場合には、Response オブジェクトの作成をボイラープレートのような形で多数記述することが考えられます。 Response.json() を使っていくとよりシンプルに記述できるかと思います。

Deno.spawn()、 Deno.spawnChild() に AbortSignal を渡せるようになった

Deno 1.21 で登場した サブプロセス呼び出し用の Deno.spawn(), Deno.spawnChild() の二つのAPIに、AbortSignal が対応しました。

Deno.spawn() に使う場合は次のようにできます。

const controller = new AbortController();
const timeoutMSec = 200;

setTimeout(() => controller.abort(), timeoutMSec);

const { status, stdout, stderr } = await Deno.spawn(Deno.execPath(), {
  args: [
    "eval",
    `import { delay } from 'https://deno.land/std@0.140.0/async/mod.ts';
    delay(100);
    console.log('hello');
    console.error('world')`,
  ],
  signal: controller.signal,
});

console.log(status.code);
console.log("STDOUT", new TextDecoder().decode(stdout));
console.log("STDERR", new TextDecoder().decode(stderr));

Deno.spawn() で呼び出す子プロセスでは、console.log() が実行されるまでに、100ms の待ちが発生します。 また、200ms待って、AbortSignal が発火するようにしたコードになっています。 この場合、AbortSignal は発火すること無く 子プロセスの実行結果を取得できます。

$ deno run --unstable --allow-read --allow-run spawn.ts
Check file:///usr/src/app/spawn.ts
0
STDOUT hello

STDERR world

(子プロセスの改行を引き継いでしまうので、間に行が空いています。)

AbortSignal の発火までを10ms など子プロセスの処理が終了するよりも十分に短い時間にした場合には、次にのように動作します。

$ deno run --unstable --allow-read --allow-run spawn.ts
Check file:///usr/src/app/spawn.ts
143
STDOUT
STDERR

終了コード(ソース上のstatus.code)は143となり、 STDOUTとSTDERR は、取得できていません。 timeoutMSecDeno.spawn() で呼び出した処理が終了するまでに十分な長さに調整し実行すると次のようになります。

performance.timeOrigin が追加

グローバル空間の performance に追加された .timeOrigin は、プロセス起動時の高解像度UNIX時刻が設定されています。 プロセス起動後の経過時間を取得する performance.now() と組み合わせると、高解像度のタイムスタンプを作成できます。

import { delay } from "https://deno.land/std@0.140.0/async/mod.ts";

await delay(1000);
const startTime = new Date(performance.timeOrigin);
const elapsedTime = performance.now();
const timestamp = performance.timeOrigin + elapsedTime
const endTime = new Date(timestamp);

console.log("Elapsed Time(ms)", elapsedTime);
console.log("START:", startTime.toLocaleString("ja-JP", { timeZone: "JST" }));
console.log("TimeStamp", timestamp)
console.log("END  :", endTime.toLocaleString("ja-JP", { timeZone: "JST" }));

注意として、高解像度時刻を得るには、--allow-hrtime の付与が必要になるのが注意です。

$ deno run timestamp.ts
Elapsed Time(ms) 1020
START: 2022/6/16 17:31:55
TimeStamp 1655368316788
END  : 2022/6/16 17:31:56

$ deno run --allow-hrtime timestamp.ts
Elapsed Time(ms) 1894.350113
START: 2022/6/16 17:31:29
TimeStamp 1655368291116.35
END  : 2022/6/16 17:31:31

--allow-hrtime をつけたときには、時間精度が向上しているのが確認できます。

その他

  • deno lsp のリンタ機能がデフォルトで、有効になりました。
  • Deno test の表示が改善されより見易くなりました。

まとめ

Deno 1.22 では、Deno 1.21 で発表されていた型チェック実行のルール変更が始まりました。 より使いやすい実行ランタイムへの進化が進んでいるのを感じます。

また、ランタイムとしての進化だけでなく、昨今のニュースとして、

  • Deno Deploy ベースの Netlify Edge Functions 公開
  • Deno Deploy ベースの Supabase Edge Functions 公開
  • Remix が、Deno を公式にサポート
  • Deno だけでなく、CloudFlareやVercelなどが参加するJavaScriptランタイム間での相互運用性の向上を目指すコミュニティグループ「WinterCG」が設立

など話題が尽きません。

2022年6月16日に、Deno 1.23 も公開になりました。またこちらもレポートしていきます。

P.S.

採用情報

■募集職種
yumenosora.co.jp