虎の穴開発室ブログ

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

MENU

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

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

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

Deno 1.39

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

帰ってきた WebGPU

Deno 1.8 で実験的にサポートされDeno 1.32 で削除されたWebGPU が、削除の一因となっていたパフォーマンスの問題を解決し、帰ってきました。

DenoでのWebGPUの実装は、FireFoxでのWebGPU実装と同じ基盤システムに基づいているとのことです。

リリースノートの記述を参考に動作させると次のようになります。

$ deno -V
deno 1.39.4

$ cat webgpu.ts
const adapter = await navigator.gpu.requestAdapter();
if (adapter) {
  const adapterInfo = await adapter.requestAdapterInfo();
  console.log(`Found adapter: ${adapterInfo.device}`);
  const features = [...adapter.features.values()];
  console.log(`Supported features: ${features.join(", ")}`);
} else {
  console.error("No adapter found");
}

$ deno run --unstable-webgpu webgpu.ts
Found adapter:
Supported features: depth-clip-control, timestamp-query, indirect-first-instance, ...[他多数]

上記のように、unstableなAPIなので、--unstable または、--unstable-webgpu が実行時のオプションとして必要です。

新しい deno coverage のレポーター

deno coverageの新しいレポーターとして summary と html の2つが追加されました。 このリリースで、デフォルトのレポーターは、summaryに変更されました。 これまでのデフォルトレポーターを使用する場合には、detailedレポーターを指定します。

また、coverageのデータをデフォルトでディレクトリに書き出すようになりました。

以下のように使用することができます。

$ cat app.ts
export function add(a: number, b: number) {
    return a + b;
}

$ cat app_test.ts
import {expect} from "https://deno.land/std@0.212.0/expect/mod.ts";
import {add} from "./app.ts";

Deno.test("#add", () => {
  const x = add(1, 2);
  expect(x).toEqual(3);
})

$ deno test --coverage
running 1 test from ./app_test.ts
#add ... ok (1ms)

ok | 1 passed | 0 failed (24ms)

# ./coverageにファイルが書き出される

# 指定しない場合、summary レポーターで出力される
$ deno coverage
-----------------------------
File    | Branch % | Line % |
-----------------------------
 app.ts |    100.0 |  100.0 |
-----------------------------
 All files |    100.0 |  100.0 |

# 更新前のデフォルトレポーターは、detailed レポーターを設定すると使用可能
$ deno coverage --detailed
cover file:///D:/development/test/deno-diff/1_39/app.ts ... 100.000% (3/3)


# html レポーターで出力すると、ファイルが書き出される
$ deno coverage --html
HTML coverage report has been generated at file:///hogehoge/coverage/html/index.html

summary は、ざっと見易いものになっていますし、coverageデータ出力先のディレクトリの詳細な指定せずに済むようになったことで、よりシンプルに使うことができるようになったと思います。

上記のテストで作成された、coverage/html/index.html をブラウザで開くと次のようになっています。

summaryレポーターのような簡易なテーブル、htmlレポーターのようなブラウザ向けの出力を標準で備えるようになりましたが、引き続き --lcov 形式出力はサポートするとのことです。
このことで、外部ツールとの統合には支障はありません。

deno compile のアップデート

動的インポートのサポート強化

動的インポートの範囲が広がりました。
これまで静的解析可能な、動的インポートをサポートしていましたが、このリリースでは、実行時にインポートされる可能性のあるファイルをすべてバイナリに含める形で、対応されます。

どのようになっているのか、確認します。

$ ls -lh
total 1
-rwxr-xr-x 1 root root  202 Jan 19 17:06 app.ts
drwxrwxrwx 1 root root    0 Jan 19 17:04 modules
$ ls -lh modules/
total 2
-rwxr-xr-x 1 root root 1.4K Jan 19 17:06 module1.ts

$ cat app.ts
const moduleName = Deno.args[0]
const functionName = Deno.args[1]

const {[functionName]: outputMessage} = await import(`./modules/${moduleName}`);

console.log("Hello deno!");
outputMessage();

$ cat modules/module1.ts
const longString = `
[長い文字列を設定しています。]
`;

export function outputMessage1() {
  console.log("output from module1.ts");
  console.log(longString);
}

# deno compile で実行可能ファイルを作成します。
$ deno compile app.ts
Check file:///hogehoge/app.ts
Compile file:///hogehoge/app.ts to app

# 実行可能ファイルは、134045413Byteでした。
$ ls -lh
total 128M
-rwxr-xr-x 1 root root 134045413 Jan 19 17:12 app
-rwxr-xr-x 1 root root       202 Jan 19 17:06 app.ts
drwxrwxrwx 1 root root         0 Jan 19 17:12 modules

# ./modules にファイルを追加します。
$ ls -l modules/
total 5
-rwxr-xr-x 1 root root 1384 Jan 19 17:06 module1.ts
-rwxr-xr-x 1 root root 1385 Jan 19 16:37 module2.ts
-rwxr-xr-x 1 root root 1386 Jan 19 16:37 module3.ts

# deno compile で再度実行可能ファイルを作成します。
$ deno compile app.ts
Check file:///hogehoge/app.ts
Compile file:///hogehoge/app.ts to app

$ ls -l
total 130911
-rwxr-xr-x 1 root root 134051715 Jan 19 17:13 app
-rwxr-xr-x 1 root root       202 Jan 19 17:06 app.ts
drwxrwxrwx 1 root root         0 Jan 19 17:13 modules

# => ./app の容量が、134045413Byte から、134051715Byte へ 6302Byte 増えました。

# 追加したファイルを動的に呼び出して実行できます。
$ ./app module3.ts outputMessage3
Hello deno!
output from module3.ts

...省略

# ./modules に新たに、module4.ts を追加します。
$ ls -l modules/
total 6
-rwxr-xr-x 1 root root 1384 Jan 19 17:06 module1.ts
-rwxr-xr-x 1 root root 1385 Jan 19 16:37 module2.ts
-rwxr-xr-x 1 root root 1386 Jan 19 16:37 module3.ts
-rwxr-xr-x 1 root root 1386 Jan 19 17:21 module4.ts

# deno compile の実行時に無かったファイルは、ダイナミックインポートできません。
$ ./app module4.ts outputMessage4
error: Uncaught (in promise) TypeError: Module not found: file:///hogehoge/modules/module4.ts
const { [functionName]: outputMessage } = await import(`./modules/${moduleName}`);
                                          ^
    at async file:///usr/src/app/1_39/compile/app.ts:3:43

以上のように、今回の場合./modules 以下に.tsファイルを増やすと、コンパイル結果の容量が増えていることがわかります。 また、ダイナミックインポートする可能性のあるファイルを読み込んで実行バイナリを作るため、あとから増やしたファイルでは動作しないというのがポイントです。

その他、実行可能ファイル名称に関する制限の削除やnode_modules サポートの強化が入っています。

Node.js 互換性向上

sloppy import(Node.jsライクなimport) のサポート

このリリースで、実行時に、--unstable-sloppy-imports フラグを設定することで、Node.jsライクなファイル拡張子を明示しないインポートや、ディレクトリのインポートをサポートします。

動かしてみると次のようになります。

$ deno -V
deno 1.39.4

$ cat app.ts
import { add } from "./module"; // <= 拡張子を明示しない
console.log(add(1, 2));

$ cat module.ts
export function add(a: number, b: number) {
  return a + b;
}

$ deno run app.ts
error: Module not found "file:///hogehoge/module". Maybe add a '.ts' extension or run with --unstable-sloppy-imports
    at file:///hogehoge/app.ts:1:21

--unstable-sloppy-imports をつけて再度実行します。

$ deno run --unstable-sloppy-imports app.ts
Warning Sloppy imports are not recommended and have a negative impact on performance.
Warning Sloppy module resolution (hint: add .ts extension)
    at file:///hogehoge/app.ts:1:21
3

拡張子を記述する形のimportをするように警告が表示されますが、実行でき計算結果が表示されます。 同様にディレクトリのインポートもできます。

$ cat app.ts
import { add } from "./modules";
console.log(add(1, 2));

$ cat modules/index.ts
export function add(a: number, b: number) {
  return a + b;
}

$ deno run --unstable-sloppy-imports app.ts
Warning Sloppy imports are not recommended and have a negative impact on performance.
Warning Sloppy module resolution (hint: specify path to index.ts file in directory instead)
    at file:///hogehoge/app.ts:1:21
3

その他、package.jsonに記述されたscriptsを実行できるようになったり、Object.prototype.__proto__ をサポートするようになりました。
また、Node.js APIの更新が入っています。

DenoAPIの変更

KV watch の導入

unstableなAPIとしてDeno.Kv.watch() が導入されました。 KVデータベースの特定のキーの versionstamp の変更を監視しすることで実現します。

以下のように使用できます。

const db = await Deno.openKv();

setInterval(async () => {
  await db.set(["foo"], new Date().toString())
}, 1000);

const stream = db.watch([["foo"]]);
for await (const entries of stream) {
  console.log(entries)
}

実行すると以下のようになります。

$ deno run --unstable-kv --allow-read --allow-write kv_watch.ts
[
  {
    key: [ "foo" ],
    value: "Sat Jan 20 2024 19:58:39 GMT+0900 (日本標準時)",
    versionstamp: "00000000000003030000"
  }
]
[
  {
    key: [ "foo" ],
    value: "Sat Jan 20 2024 20:00:51 GMT+0900 (日本標準時)",
    versionstamp: "00000000000003090000"
  }
]
# 以下1秒ごとに繰り返す

Deno.cron の拡張

Deno1.38で登場した Deno.cronは、Unix Cron 形式の記述でタスクを実行することができました。 このリリースで、JSON形式の記述をサポートするようになります。

以下のふたつの記述はどちらも、10分毎にタスク実行を行います。

Deno.cron("cron 1", "*/10 * * * *", () => {
  console.log(new Date().toString());
});

Deno.cron(
  "cron 2",
  {
    minutes: { every: 10 },
  },
  () => {
    console.log(new Date().toString());
  },
);

{minutes: { every: 10 }} 以外にも、hourなどの設定も用意されています。

https://deno.land/api@v1.39.4?s=Deno.CronSchedule&unstable=

その他 Deno APIには、以下の対応が入っています。

  • Deno.serve() のUnixソケットサポートが安定化
  • Deno.HttpServer.shutdown() が安定化
  • Deno.HttpClientusing がusingキーワードに対応
  • IO インターフェイス 非推奨について
    • 以下のIOインターフェイスは、非推奨になり、Deno 2で削除予定です。
      • Deno.Reader
      • Deno.ReaderSync
      • Deno.Writer
      • Deno.WriterSync
      • Deno.Closer

Web API の変更

AbortSignal.any() の追加

AbortSignal.any() は、複数のAbortSignalを受け入れます。

以下のように使用できます。

const abortController = new AbortController()
const timeoutAbortSignal = AbortSignal.timeout(500) // 500ミリ秒経過して発火

const anySignal = AbortSignal.any([
    abortController.signal,
    timeoutAbortSignal,
  ]);

console.log(anySignal.aborted);
// => false

// 500ミリ秒経過するとtimeoutAbortSignalが発火しているので、1秒後にはanySignalmは発火している
setTimeout(() => {
  console.log(anySignal.aborted);
  // => true 
}, 1000);

AbortSignal.any()を扱うことで、複数の AbortSignalをまとめて管理することができます。

詳細はこちら

https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/any_static

その他、 ImageData APIの追加や、URLPattern APIのパフォーマンス向上が報告されています。

STD ライブラリの更新

以下のモジュールが、STDライブラリとして追加されました。

  • std/webgpu : WeGPU に関するモジュール群
  • std/ini : INI ファイルのパーサー、シリアライザーを提供
  • std/data_structures : BinaryHeap,BinarySearchTree,RedBlackTree クラスを提供
  • std/text : 文字列のソートや加工、近似文字列を返すなどのユーティリティ関数群を提供
  • std/cli : parseArgspromptSecret の2つを提供
  • std/net : 使用可能なTCPポートを返す getAvailablePort を提供

また std/expect が追加されており、こちらは、テストフレームワーク Jest 互換の expectfn の2つの関数を提供します。

以下のように使用できます。

import { expect, fn } from "https://deno.land/std@0.211.0/expect/mod.ts";

function add(a: number, b: number) {
    return a + b;
} 

function createLog(src:string) {
   return `Log: call ${src}`;
}

function factorial(value: number, logger: {(src: string):string}): number {
  console.log(logger(`factorial(${value})`));
  if (value === 1) {
    return 1;
  }
  return value * factorial(value - 1, logger);
}

Deno.test("Test add",() => {
  expect(add(2, 3)).toEqual(5);
  expect(add(2, 3)).not.toEqual(0);
});


Deno.test("Test factorial",() => {
  const mockCreateLog = fn(createLog) as {(src: string):string};

  const result = factorial(5, mockCreateLog);

  expect(result).toEqual(120);
  // モックした関数は5回呼ばれる
  expect(mockCreateLog).toHaveBeenCalledTimes(5);
});

実行すると次のようになります。

$ deno test
running 2 tests from ./expect_test.ts
Test add ... ok (1ms)
Test factorial ...
------- output -------
Log: call factorial(5)
Log: call factorial(4)
Log: call factorial(3)
Log: call factorial(2)
Log: call factorial(1)
----- output end -----
Test factorial ... ok (0ms)

ok | 2 passed | 0 failed (4ms)

その他

  • LSP(言語サーバー)が強化
  • Deno 1.39 には、TypeScript 5.3 が同梱されます

Deno 1.39 を見てきました。
WebGPU が復活し、ImageData APIが追加されビジュアル表現にかかわる機能の拡充が気になったリリースでした。

また、Node.js互換性について、Deno ライクではない拡張子を明示しないインポートのサポートなど、一段階サポートのレベルが変わったように感じられます。 npmパッケージのサポートは入っていても、自前で書いていてそのままでは実行できなかった部分をフォローできるようになるでしょう。

Deno 1.40 では、unstableながらTemporal APIのサポートの追加や、動作するDecoratorがTypeScriptのDecoratorから、TC39で提案されているDecoratorへの変更が予定されています。

また次回も追いかけたいと思います。

toranoana.deno #15

Deno に関することであれば何でもOKのLT会、toranoana.deno 次回は2月14日開催です。
登壇参加、視聴参加大歓迎です。

yumenosora.connpass.com

採用情報

虎の穴ラボでは一緒に働く仲間を募集中です!
この記事を読んで、興味を持っていただけた方はぜひ弊社の採用情報をご覧ください。
toranoana-lab.co.jp