はじめに

Deno v2.9がリリースされました。

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

deno desktop

実験的機能としてDenoでデスクトップアプリケーションを開発するためのdeno desktopコマンドが実装されています (#33441, #35267, #35296, #35378, #35420, #35424, #35425, #35442, #35472, #35484, #35485)

内部的にはdeno compileの仕組みを活用して実装されているようで、フレームワークの検出機能などもサポートされています。

deno desktopでは--backendオプションにより以下の3種類のバックエンドを切り替えることができます:

  • webview (デフォルト)
  • cef
  • raw

各種バックエンドの実装はlaufeyというライブラリから提供されており、$DENO_DIR/laufey/<laufey-version>にダウンロードされます。

--outputオプションに指定したファイル名の拡張子から、様々なフォーマットへのパッケージングがサポートされています (.dmg/.msi/.AppImage/.deb/.rpmなど)

その他にも--hmrオプションによるHMRのサポート、Deno.serve()との統合、bindings[name]()によるDenoランタイムからバックエンドへの関数の公開、alert()/confirm()などによるネイティブポップアップの表示、NotificationAPIによる通知、Deno.BrowserWindow/Deno.Tray/Deno.dock/Deno.MenuItem/Deno.autoUpdate()などのAPIなどがサポートされています。

先週に紹介した時点からの差分として、フレームワークの検出機能において通常のViteを使用したプロジェクトの検出がサポートされています (#35470)。今まではVite SSRを使用しているケースのみがサポートされていましたが、より様々なプロジェクトで活用の余地が広がりそうです。

deno bundle

--declaration

deno bundle--declarationオプションが追加されています (#33838)。バンドルとともに単一の型定義ファイルを生成してくれるため、deno packコマンドなどと併用すると便利そうです。

$ deno bundle src/lint.ts --outdir dist --declaration
⚠️  deno bundle is experimental and subject to changes
Emit dist/lint.d.ts
Bundled 15 modules in 62ms
  dist/lint.js 63.9KB

deno link及びdeno unlinkコマンドが実装されています (#34359)。deno.jsonlinksをコマンドによって操作できます。

$ deno link ../redis
Link ../redis (../redis)

$ git status --short
 M deno.json
 M deno.lock

$ cat deno.json | jq .links
[
  "../redis"
]

$ deno unlink @db/redis
Unlink @db/redis

$ cat deno.json | jq .links
null

また、これらのコマンドの実装に合わせてdeno.jsonlinksプロパティーが安定化されています (#34996)。

deno list

deno listコマンドが実装されています (#34972)。npm lsに相当するコマンドで、プロジェクトの依存関係として宣言されているパッケージを一覧表示できます。

$ deno list
┌───────────────────────────────┬──────────┬──────────┐
│ Package                       │ Required │ Resolved │
├───────────────────────────────┼──────────┼──────────┤
│ jsr:@std/collections          │ ^1.1.3   │ 1.1.3    │
├───────────────────────────────┼──────────┼──────────┤
│ jsr:@std/fmt                  │ ^1.0.8   │ 1.0.8    │
├───────────────────────────────┼──────────┼──────────┤
│ jsr:@std/semver               │ ^1.0.8   │ 1.0.8    │
├───────────────────────────────┼──────────┼──────────┤
│ npm:json-schema-to-typescript │ ^15.0.4  │ 15.0.4   │
├───────────────────────────────┼──────────┼──────────┤
│ npm:jsonc-parser              │ ^3.3.1   │ 3.3.1    │
├───────────────────────────────┼──────────┼──────────┤
│ npm:lines-and-columns         │ ^2.0.4   │ 2.0.4    │
├───────────────────────────────┼──────────┼──────────┤
│ npm:string-argv               │ ^0.3.2   │ 0.3.2    │
└───────────────────────────────┴──────────┴──────────┘

--depth=<N>オプションを指定することで、指定したレベルまでの推移的依存関係をツリー形式で列挙できます。

$ deno list --depth=1
jsr:@std/collections 1.1.3
jsr:@std/fmt 1.0.8
jsr:@std/semver 1.0.8
npm:json-schema-to-typescript 15.0.4
├─ npm:@apidevtools/json-schema-ref-parser 11.9.3
├─ npm:@types/json-schema 7.0.15
├─ npm:@types/lodash 4.17.20
├─ npm:is-glob 4.0.3
├─ npm:js-yaml 4.1.0
├─ npm:lodash 4.17.21
├─ npm:minimist 1.2.8
├─ npm:prettier 3.6.2
└─ npm:tinyglobby 0.2.15
npm:jsonc-parser 3.3.1
npm:lines-and-columns 2.0.4
npm:string-argv 0.3.2

deno watch

deno watchコマンドが追加されています (#35301)

deno run --watch-hmrのエイリアスとして機能します。

deno fmt

laxへの移行

以下のフォーマットにおいてlaxというライブラリを使うように内部実装が変更されています (#35160, #35174, #35161)。

安定性やパフォーマンスの改善などを目的としているようです。これに伴い、.sassファイルのフォーマットはサポートが廃止されています。

sortNamedImports/sortNamedExports

deno.jsonfmt.sortNamedImports及びfmt.sortNamedExportsが追加されています (#33313)

import/export対象の要素を並び替えるためのオプションで、以下のいずれかの値を指定できます:

  • caseSensitive - 大文字・小文字を区別する
  • caseInsensitive - 大文字・小文字を区別しない (デフォルト)
  • maintain - 要素の並び替えを行わない

.editorconfigのサポート

.editorconfigのサポートが追加されています (#34071)

Denoが自動で.editorconfigを検出し、以下の設定内容をベースにdeno fmtの挙動を調整してくれます:

  • indent_style
  • indent_size
  • tab_width
  • max_line_length
  • end_of_line

この振る舞いは--no-editorconfigオプションまたはdeno.jsonfmt.useEditorConfigで無効化可能です。

deno.json.editorconfigの両方で設定が行われている場合、deno.jsonの方の設定が優先されます。

deno compile

Storage/Deno KVの永続化がサポート

deno compileによって作成された実行可能ファイルにおいて、localStorageDeno.KvなどのAPIにおけるアプリケーションごとの専用ディレクトリへの永続化がサポートされています (#34618)

--app-nameオプションによってアプリケーション名を指定することが出来ます。例えば、Linuxにおいてはデフォルトで~/.local/share/<app-name>ディレクトリにデータが永続化されます (cli/rt/binary.rs#L235)。

deno task

キャッシュの仕組みが導入

deno taskコマンドにキャッシュの仕組みが導入されています (#34509)。

この仕組みを利用するためには、タスクの定義時にfilesに該当タスクが依存するファイルをglob形式で指定する必要があります。そして、outputに指定したファイルはタスクの実行後、ローカルのファイルシステム上にキャッシュされます。また、追加で該当タスクが依存する環境変数も定義することができます。

{
  "tasks": {
    "tools:generate-rules-md": {
      "command": "deno run --permission-set=tools:generate-rules-md tools/generate-rules-md.ts && deno fmt docs/rules.md",
      "files": ["./src/rules.ts"],
      "output": ["docs/rules.md"],
      "env": ["FOO"]
    }
  }
}

deno taskfiles,envまたはコマンドの定義のいずれかが前回のタスク実行時から変更されていなければ、ローカルのファイルシステム上にキャッシュされたoutputを復元し、タスクの実行をスキップします。

# (1) 初回は通常通り、タスクが実行されます
$ deno task tools:generate-rules-md
Task tools:generate-rules-md deno run --permission-set=tools:generate-rules-md tools/generate-rules-md.ts && deno fmt docs/rules.md
Warning Permissions in the config file is an experimental feature and may change in the future.
/path/to/docs/rules.md
Checked 1 file


# (2) キャッシュの仕組みによりタスクの実行がスキップされます
$ deno task tools:generate-rules-md
Task tools:generate-rules-md deno run --permission-set=tools:generate-rules-md tools/generate-rules-md.ts && deno fmt docs/rules.md (cached, inputs unchanged)


# (3) `env`で指定した環境変数が変更されると、通常通り、タスクが実行されます
$ FOO=1 deno task tools:generate-rules-md
Task tools:generate-rules-md deno run --permission-set=tools:generate-rules-md tools/generate-rules-md.ts && deno fmt docs/rules.md
Warning Permissions in the config file is an experimental feature and may change in the future.
/path/to/docs/rules.md
Checked 1 file

キャッシュは$DENO_DIR/task_cache_v1ディレクトリに保存されます。

--if-present

--if-presentオプションが追加されています (#35315)。pnpm runコマンドにおける同名オプションと同様に、 このオプションを指定すると、該当のタスクが見つからない場合に終了コードとして1ではなく0が返却されます。

--jobs (--concurrency)

--jobs (--concurrency) オプションが追加されています (#35318)

DENO_JOBS環境変数と同様に、ワークスペース内における各メンバーのタスクを一括実行する (-r/--filter) 際の並列実行数を数値で指定することができます。

deno test

--shard

--shardオプションが追加されています (#35057)。JestやVitestにおける同名オプションと同様に--shard=<index>/<count>形式で指定することにより、テストファイルのシャーディングが行えます。

$ deno test --shard=1/3

deno testコマンドに--changed及び--relatedオプションが追加されています (#35199)。

--changed オプションを指定すると、デフォルトでGitリポジトリ内におけるまだ変更がコミットされていないファイルに関連するテストファイルのみを実行してくれます。また、引数として任意の ref を指定することも可能で、該当の ref からの差分に基づいてテストが実行されます。

$ deno test --changed=origin/main

--related オプションを指定すると、与えられたモジュールに関連するテストファイルのみを実行してくれます。

$ deno test --related=src/permissions.ts

これらのオプションはモジュール間の依存関係に基づいて実行するテストファイルを決定してくれます。

Deno.test.each()

Deno.test.each()が追加されています (#34938)。JestやVitestにおけるtest.each()と同様に、複数の値の組み合わせに対して同様のテストを繰り返し実行したい際に便利です。

Deno.test.each(
  [
    ["--allow-all", true],
    ["-A", true],
    ["-rAq", true],
    ["--allow-allx", false],
    ["--allow-read", false],
  ],
)("isAllowAllFlag(\"%s\")", {
  permissions: "none",
}, (given, expected) => {
  const actual = isAllowAllFlag(given);
  assert.equal(actual, expected);
});

// オブジェクト形式でケースを指定
Deno.test.each(
  [
    { given: "--allow-all", expected: true },
    { given: "-A", expected: true },
    { given: "-rAq", expected: true },
    { given: "--allow-allx", expected: false },
    { given: "--allow-read", expected: false },
  ],
)("isAllowAllFlag(\"$given\")", {
  permissions: "none",
}, ({ given, expected }) => {
  const actual = isAllowAllFlag(given);
  assert.equal(actual, expected);
});

retry/repeats

Deno.test()retry及びrepeatsオプションが追加されています (#35053)。

repeatsオプションを指定すると、指定された回数だけ追加でテストケースが実行され、それらすべてが成功した場合のみ、該当のテストケースは成功とみなされます。

Deno.test({
  name: "some flaky test",
  repeats: 3,
  fn: async () => {
    const actual = await doSomethingAsync();
    const expected = true;
    assert(actual, expected);
  },
});

retryオプションを指定した場合は、テストケースが失敗した際に、最大で指定された回数までテストケースのリトライが行われます。

Deno.test({
  name: "some flaky test",
  retry: 2,
  fn: async () => {
    const actual = await doSomethingAsync();
    const expected = true;
    assert(actual, expected);
  },
});

また、deno testコマンドに--retryまたは--repeatsオプションを指定することで、すべてのテストケースに一括で再試行回数を指定することも可能です (Deno.test()で個別にretryまたはrepeatsオプションが指定されている際は、CLIオプションによる指定よりも優先されます)

スナップショットテスティング

Deno.TestContext#assertSnapshot()が実装されています。デフォルトでは__snapshots__/*.ts.snap@std/testing/snapshotと同様のフォーマットでスナップショットが保存されます。

Deno.test({
  name: "renderSomething()",
  fn: async (t) => {
    await t.assertSnapshot(renderSomething());
  },
});

deno testコマンドに--update-snapshotsオプションを指定することで、スナップショットを更新することができます。

スナップショットの保存先はdirオプションによって変更することも可能です。その場合は、該当のディレクトリに対する読み込み (--allow-read) と書き込み (--allow-write) 権限が必要です。

await t.assertSnapshot(renderSomething(), {
  dir: "custom_dir", // `custom_dir`への読み書き権限が必要です
});

deno coverage

coverage.thresholds

deno.jsoncoverage.thresholdsがサポートされています (#35056)。カバレッジの閾値を設定することができます。

{
  "coverage": {
    "thresholds": {
      "branches": 95,
      "functions": 95,
      "lines": 95
    }
  }
}

設定された閾値を下回っている場合、エラーが報告されます。

$ deno test --coverage tests/permissions.test.ts
  ...

| File           | Branch % | Function % | Line % |
| -------------- | -------- | ---------- | ------ |
| permissions.ts |     94.3 |      100.0 |   97.0 |
| All files      |     94.3 |      100.0 |   97.0 |
Lcov coverage report has been generated at file:///path/to/coverage/lcov.info
HTML coverage report has been generated at file:///path/to/coverage/html/index.html
error: Coverage threshold not met:
  - Branch coverage 94.28% is below the threshold of 95.00%

deno lsp

deno/inferredType

deno/inferredTypeが実装されています (#35099)。指定されたシンボルに対して推論された型情報を取得できるようです。

これをベースに vscode_deno においてCopy Inferred Typeコマンドの実装が進められています (denoland/vscode_deno#1381)

--env-file

deno.jsontasks.testタスクが--env-fileを指定している場合、deno/testRun経由でのテスト実行時に.envが読まれるように改善されています (#34905)

deno remove

--global

deno remove --globalがサポートされています (#35327)。deno uninstall --globalと同等に振る舞います。

deno.lock

コンフリクトの解消がサポート

Denoがdeno.lockで発生したコンフリクトを自動で解消してくれるように挙動が改善されています (#34726)

セキュリティ

minimumDependencyAge

minimumDependencyAgeが未設定の場合、デフォルトで1440 (1日)が適用されるように挙動が変更されています (#35458)

この変更に合わせて、minimumDependencyAge0を指定した場合、minimumDependencyAgeを無効化できるように振る舞いが変更されています。また、NPM_CONFIG_MIN_RELEASE_AGE環境変数による設定もサポートされています。

trust-policy=no-downgrade

サプライチェーン攻撃への対策として、.npmrctrust-policy=no-downgradeの設定がサポートされています (#34927)

trust-policy=no-downgrade

これを有効化しておくと、pnpmにおけるtrustPolicy: no-downgradeを設定した場合と同様に、npmパッケージのあるバージョンをインストールしようとした際に、そのバージョンが以前のバージョンよりも信頼性が低下していると判断された場合は、インストールが拒否されます。信頼性は該当バージョンの公開方法によって判断されます。具体的にはStaged publishingで公開されたバージョンは最も信頼性が高いと判断され、それよりも新しいバージョンにおいてより信頼性が低い方法で公開されているケースが検出された場合、Denoは該当のバージョンのインストールを拒否します。

また、pnpmと同様にtrust-policy-ignore-after/trust-policy-excludeオプションによって制限を緩めることも可能です。

--unstable-unsafe-protoの安定化

--unsafe-protoオプションが追加されています (#34738)。振る舞いは--unstable-unsafe-protoオプションと同様です。

また、--unsafe-protoオプションなしで__proto__へのアクセスが検出された際にDenoがヒントを表示するように振る舞いが変更されています (#35192)

--allow-run

Deno.kill()またはprocess.kill()でDenoプロセス自身を停止させる場合は--allow-runが不要化されました (#34382)。この用途のために--allow-runを指定していた場合は見直した方が良さそうです。

--allow-net=unix:<host>

以下のAPIが--allow-net=unix:<host-or-path>形式のパーミッションを要求するように振る舞いが変更されています (#34395, #35231)

  • Deno.connect()/Deno.listen()/Deno.listenDatagram()におけるtransport: "unix" または transport: "unixpacket"
  • Deno.DatagramConn#send()
  • Deno.createHttpClient()におけるproxy.path (proxy.transport: "unix")

node:http

NODE_USE_ENV_PROXYでプロキシが有効化されている場合、プロキシ先のホストに対する--allow-netパーミッションが要求されるように改善されています (#35241)。今まではプロキシーサーバー自身のホストに対するパーミッションのみが要求されていたようです。

Web API

CSSモジュール (with { type: "css" })

.cssファイルのimportに関する実験的サポートが追加されています (#35093)

with { type: "css" }を指定することで、.cssファイルをCSSStyleSheetオブジェクトとして読み込むことができます:

import stylesheet from "./styles.css" with { type: "css" };

console.assert(stylesheet instanceof CSSStyleSheet);

本機能を利用するためには、現時点では--unstable-raw-importsの指定が必要です。

Web Locks API

Web Locks API (navigator.locks) が実装されています (#31166)

Web Crypto API

WICG/webcrypto-modern-algosにおける下記アルゴリズムがサポートされています (#35223):

  • KT128
  • KT256
  • KangarooTwelve
  • KMAC128
  • KMAC256
  • SLH-DSA-SHA2-*
  • SLH-DSA-SHAKE-*
  • Argon2i
  • Argon2d
  • Argon2id

navigator.userAgentDataが実装されています (#34743)

.wasm

.wasmにおけるglobal exportがWebAssembly.Globalでラップされないように挙動が変更されています (#34912)

Deno API

Deno.connect()

以前に紹介したDeno.connect()及びDeno.connectTls()におけるHappy Eyeballsのサポートが正式に導入されています (#31726)

この機能はデフォルトで有効化されており、無効化したい場合はautoSelectFamilyオプションにfalseを指定する必要があります。 指定されたホストに対して複数のアドレスが解決された場合、まずIPv6アドレスに対する接続を試み、その後、デフォルトで250ミリ秒後 (autoSelectFamilyAttemptDelayオプションでカスタマイズ可能) にIPv4アドレスへの接続が並列で試行され、最初に接続が成功したホストに対して以降のやり取りが行われます。

Deno.serve()

Deno.serve()automaticCompressionオプションが追加されています (#35253, #35486)

このオプションの追加に合わせて、デフォルトでレスポンスボディの圧縮が無効化されています。もし有効化したい場合はautomaticCompressiontrueを設定する必要があります。もしくはDENO_SERVE_AUTOMATIC_COMPRESSION=1を設定することで、すべてのDeno.serve()に対してレスポンスボディの圧縮を有効化することも可能です。

Deno.watchFs()

Deno.watchFs()ignoreオプションが実装されています (#31582)。ignoreオプションには配列で複数のパスを指定することが可能で、もし指定されたパスにマッチするファイルに対して変更が検知された場合、イベントの通知がスキップされます。

Node.js互換性の改善

--unstable-bare-node-builtinsの安定化

--unstable-bare-node-builtinsが安定化されています (#33316)。これによりnode:なしでのNode.js組み込みモジュールのimportが常に動作します。

node:child_process

Node.jsがインストールされていない場合、$DENO_DIR/node_compat_bin/ディレクトリにnode shim (実体はDenoへのシンボリックリンク) を作成し、PATH$DENO_DIR/node_compat_bin/を自動的に追加する機能が実装されています (#34969)。この shim はNode.jsのコマンドライン引数を解釈した上で、Denoのコマンドライン引数へと変換してくれるようです。

spawn()などのAPIによってnodeが実行されるケースが検知された場合にこの仕組みが実行されます。

この仕組みはDENO_DISABLE_NODE_SHIM=1によって無効化可能です。

catalog:

deno.jsonimportsにおけるcatalog:プロトコルの指定がサポートされています (#35168)

{
  "catalog": {
    "valibot": "^1.4.1"
  },
  "imports": {
    "valibot": "catalog:",
  }
}

deno install

他のパッケージマネージャーのロックファイルからdeno.lockへの移行がサポート

deno.lockが存在せず、以下のいずれかのロックファイルが検出された場合、それらに基づいてDenoが自動的にdeno.lockを生成してくれる機能が実装されています:

ワークスペース

pnpm-workspace.yamlのサポートが追加されています (#34993)。Denoがnpmパッケージに関する依存解決に失敗した際にpnpm-workspace.yamlの検出を試みます。もしpnpm-workspace.yamlが検出されたら、その設定内容に基づいてpackage.jsonまたはdeno.jsonワークスペース及びカタログに関する設定を自動で更新してくれます。

また、npmやpnpmと同様に、ワークスペース中のpackage.jsonを持つ各メンバーのディレクトリにおいてnode_modulesが作成されるように振る舞いが変更されています (#34970)。各メンバーが直接の依存関係としてbinフィールドを定義するパッケージに依存している場合、適切なbinが実行されるようにすることを目的としているようです。

package.json

package.jsonにおけるengines.nodeengines.denoのチェックがサポートされています (#34225)。ミスマッチが検出されたら、警告が出力されます。

jsrDepsInNodeModules

deno.jsonjsrDepsInNodeModulesが追加されています (#35029)。このオプションにtrueが設定され かつ node_modulesの使用が有効化されている場合、jsr:指定されたjsrパッケージがnpm.jsr.ioからnode_modulesにダウンロードされます。(jsr:@<scope>/<package>npm:@jsr/<scope>__<package>として解釈されるように挙動が変わります)

preferPackageJson

deno.jsonpreferPackageJsonが追加されています (#35392)。このオプションにtrueを設定すると、依存パッケージの追加時にdeno.jsonではなくpackage.jsonが更新されます (v2.8で追加された--package-jsonオプションと同様の挙動です)

deno task

package.jsonのスクリプトをdeno taskで実行する際にnpm_execpath/npm_node_execpath/npm_commandや各種npm_lifecycle_*/npm_lifecycle_*環境変数が設定されるように改善されています (#35317, #35252)

node:test

下記APIが実装されています:

node:http2

createServer()DENO_SERVE_ADDRESSがサポートされています (#35089)

node:process

process.resourceUsage()が実装されています (#35468)

node:worker_threads

isInternalThreadが実装されています (#35234)

Node-API

node_api_create_buffer_from_arraybuffer()が実装されています (#35270)

参考