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

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

package.jsonサポート

Denoでpackage.jsonがサポートされました。

具体的には、以下のような機能などが実装されています。

  • package.jsonの自動探索
  • dependencies/devDependenciesの定義内容によるbare specifierの解決
  • deno taskによるscriptsの実行

package.jsonの自動探索

deno.jsonなどのファイルと同様に、Denoがpackage.jsonも自動で探索してくれるようになりました。

もしpackage.jsonが見つかった場合、必要に応じてdependenciesdevDependenciesで記述された依存パッケージを自動でダウンロードし、デフォルトでnode_modulesディレクトリに保存してくれます。 (従来通り、package.jsonが存在しない状態でnpm:経由でnpmパッケージをimportした際は、--node-modules-dirを指定しない限り、node_modulesは作成されません)

もし、package.jsonの自動探索を無効化したい場合は、以下のいずれかの手段で無効化できます (以下の機能は、それぞれ次のリリースであるv1.31.1で追加された機能のためご注意!)

  • --no-configまたは--no-npmオプションを指定する。
  • DENO_NO_PACKAGE_JSON環境変数に1を指定する

dependencies/devDependenciesによるbare specifierの解決

Denoがpackage.jsondependenciesdevDependenciesで定義された依存関係を元に、Import Mapsライクにbare specifierを解決してくれるようになりました。

例えば、以下のような内容のpackage.jsonが存在したとします。

{
  "dependencies": {
    "chalk": "^5.2.0",
    "koa": "2"
  }
}

この場合、アプリケーションでは以下のようにbare specifierを記述することがでます。

import chalk from "chalk"; // => `npm:chalk@^5.2.0`
import Koa from "koa"; // => `npm:koa@2`

const app = new Koa();

app.use((ctx) => {
  ctx.body = "Hello world";
});

app.listen(3000, () => {
  console.log(chalk.blue.bold("Listening on port 3000"));
});

もしImport Maps (deno.jsonimportsプロパティなど)とpackage.jsonの両方が存在する場合は、Import Mapsが優先されます。

そのような場合、DenoはまずImport Mapsを元にbare specifierの解決を試み、それがうまくいかなければpackage.jsonによる解決へfallbackします。

deno taskによるscriptsの実行がサポート

package.jsonscriptsで定義されたスクリプトのdeno taskコマンドによる実行がサポートされました。

dependenciesdevDependenciesで宣言されているパッケージであれば、それらのパッケージで提供されるコマンドも実行することができます。

具体的には、以下はdeno taskcowsayパッケージを実行する例です。

{
  "devDependencies": {
    "cowsay": "^1.5.0"
  },
  "scripts": {
    "hello": "cowsay Hello"
  }
}

以下のように実行できます

$ deno task hello

このようにnpmパッケージに含まれるコマンドを実行する際は、内部的には以下のようなコマンドが実行されます。

$ deno run -A npm:cowsay@^1.5.0 hello

もしプロジェクト内にdeno.jsonpackage.jsonの両方が存在する場合は、deno.jsonが優先されます。

そのような場合、Denoはまずdeno.jsontasksに対象のタスクが定義されていないかを確認し、もし存在しなければ、package.jsonscriptsからタスクの探索を試みます。

deno_std/nodeのDeno本体への組み込み

今まで、DenoにおけるNode.js互換機能は、deno_std/nodeで実装されたポリフィルを利用することで実現されていました。

その関係で、Denoの初回実行時にdeno_std/nodeのダウンロードが必要であったり、ポリフィルの実装にDenoの内部APIやOpなどの仕組みが使いづらいというような課題がありました。

これらの課題を解決するため、Deno本体にポリフィル(deno_std/node)が組み込まれました。

Node.jsのポリフィルはDenoのビルド時にあらかじめ作成されるV8のスナップショットに含まれており、Denoからnpmパッケージを利用する際の起動の高速化などが期待されます。

この変更を受けて、今までTypeScriptで実装されていた一部の組み込みパッケージ(node:punycodenode:cryptoなど)の実装などがRustへポートされています。

また、今までTypeScriptのみでは実装が難しかったnode:v8パッケージの一部の機能も実装が追加されています。

import { getHeapStatistics } from "node:v8";

console.log(getHeapStatistics());

Node.js互換性に関する安定化

Node-APIの安定化

Node-APIが安定化されました。

今後はNode-APIを--unstableなしで利用できます。(ただし、Node-APIの利用には--allow-ffiの指定が必要です)

PrismaなどのパッケージがNode-APIに依存しているため、そういったパッケージがより使いやすくなりそうです。

npm:に依存したリモートモジュールに関する安定化

ローカルモジュールからのnpm:の利用はすでにDeno v1.28で安定化されたものの、npm:を含むリモートモジュールを利用する際は依然として--unstableが必要な状況でした。

今回のリリースから、こういったnpm:を含むリモートモジュールも--unstableなしで利用できるようになりました。

deno benchでJSONレポーターがサポート

--jsonオプションを指定することで、ベンチマーク結果をJSON形式で出力することができます。

$ deno bench --json main_bench.ts

以下のように結果が出力されます。

{
  "runtime": "<ランタイム情報 (Denoバージョンなど)>",
  "cpu": "<CPU名>",
  "benches": [
    {
      "origin": "file:///home/uki00a/ghq/github
.com/uki00a/deno-sample/main_bench.ts",
      "group": null,
      "name": "sum",
      "baseline": false,
      "results": [
        {
          "ok": {
            "n": 5972,
            "min": 4.7421,
            "max": 24.2738,
            "avg": 8.406597823174797,
            "p75": 11.036,
            "p99": 15.3162,
            "p995": 17.3743,
            "p999": 22.9964
          }
        }
      ]
    }
  ]
}

deno bundleコマンドの非推奨化

今後、deno bundleコマンドを利用する際は、以下のような警告メッセージが表示されます。

Warning "deno bundle" is deprecated and will be removed in the future.
Use alternative bundlers like "deno_emit", "esbuild" or "rollup" instead.

こちらのメッセージにもあるとおり、今後はdeno_emitesbuildなどに移行するとよさそうです。

deno fmtコマンドのオプションの簡略化

CLIオプションによってdeno fmtの挙動を変更したい場合、今までは、--options-から始まるオプションを指定する必要がありました。

$ deno fmt --options-no-semicolons --options-single-quote main.js

今回のリリースからoptions-というプレフィックスを省略できるようになり、より簡潔にオプションを指定できるように改善されています。

$ deno fmt --no-semicolons --single-quote main.js

unstable APIの安定化

Deno.Command

Deno v1.28で追加されたDeno.Commandが安定化され、--unstableなしで利用できるようになりました。

この安定化に合わせて、Deno.CommandOptions.argsstring[]からreadonly string[]型に変更されています。

(補足) Deno.runの非推奨化について

元々は今回のDeno v1.31で予定されていたようですが、Deno.Commandの安定化とDeno.runの非推奨化を同時に行うと移行猶予が短くなってしまうということもあり、Deno.runの非推奨化は次のv1.32移行に持ち越されそうです。

Deno.osUptime

Deno.osUptimeが安定化されました。

今後は--unstableオプションを指定せずに利用できます。

パーミッションプロンプトの利便性の向上

Denoは--allow-*オプションによって権限が与えられていない処理を実行する場合、デフォルトでプロンプトを表示して、ユーザーに対して権限の付与を求めます。

例えば、以下はDeno v1.30におけるプロンプトの表示内容です。

⚠️  ┌ Deno requests read access to "foo.txt".
   ├ Requested by `Deno.readTextFile()` API
   ├ Run again with --allow-read to bypass this prompt.
   └ Allow? [y/n] (y = yes, allow; n = no, deny) >

Deno v1.31では、このプロンプトに今までのynに加えて、新しくAという選択肢が追加されています。

┌ ⚠️  Deno requests read access to "foo.txt".
├ Requested by `Deno.readTextFile()` API
├ Run again with --allow-read to bypass this prompt.
└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions) >

このAを選択すると、該当のカテゴリの全リソースに対する権限が与えられます。(例えば、上記の場合、ファイルシステムの全ファイルへの読み込み権限が与えられます)

今までは、パーミッションプロンプトからはアクセスしたいリソースごとに一つずつ権限を与える必要がありましたが、これにより利便性が向上しています。

Deno APIに関する新機能・改善

Deno.resolveDnssignalオプションが追加

AbortSignalを使用して問い合わせをキャンセルできるようになりました。

const ac = new AbortController();
// ...

const records = await Deno.resolveDns("deno.land", "A", {
  signal: ac.signal,
});

for (const x of records) {
  console.log(x);
}

Deno.build.osの型定義の改善

このプロパティは元々は以下のように型が定義されており、FreeBSDなどのOSで実行した場合、型エラーが起きてしまう問題がありました。

"darwin" | "linux" | "windows"

この問題を解消するために、v1.31で以下のように型定義が改善されました

"darwin" | "linux" | "windows" | "freebsd" | "netbsd" | "aix" | "solaris" | "illumos"

Deno.errors.WouldBlockの追加

Deno.errors.WouldBlockが追加されています。

これに合わせて、EWOULDBLOCKエラーが起きたときにプロセスがパニックする問題も修正されています。(そのような場面でこのDeno.errors.WouldBlockthrowされます)

FFI

Deno.UnsafePointer/Deno.UnsafePointerViewの新機能

Deno.UnsafePointerに以下のstaticメソッドが追加されています

メソッド説明
create(value: number | bigint): Deno.PointerValue数値をDeno.PointerValueへ変換します。
equals(a: Deno.PointerValue, b: Deno.PointerValue): boolean2つのポインタが同じアドレスを参照していればtrueを返します。
offset(value: NonNullable<Deno.PointerValue>, offset: number): Deno.PointerValueポインタから指定されたオフセットだけずらした位置のアドレスを指すポインタを取得します。
value(value: Deno.Pointer): number | bigintDeno.PointerValueを数値に変換します。

また、Deno.UnsafePointerView.getPointerが追加されており、現在の地点から指定されたオフセットだけずらした位置のポインタをDeno.PointerValue型として取得することができます。

Deno.UnsafeCallbackref/unrefに関する改善

unrefメソッドなどによって参照カウントが0になった状態のDeno.UnsafeCallbackrefメソッドによって再度参照カウントを増加させた際に、Denoのイベントループが意図せずして停止してしまうことのある問題が修正されています。

また、あらかじめrefされた状態のコールバックを作成するために、Deno.UnsafeCallbackthreadSafeというstaticメソッドが追加されています。

その他のバグ修正など

  • Deno.serve()で起動したHTTPサーバがHEADリクエストを受信した際に、レスポンスのサイズによっては間違ったContent-Lengthが返却されてしまう問題が修正されています。
  • GPUAdapter.featuresundefinedになってしまう問題が修正されています。
  • GPUDevice.createSamplerを呼ぶとエラーが発生する問題が修正されています。
  • console.tableで整数のみが含まれる列のみが右寄せで表示されるように改善されています。
  • WebSocketクライアントからPongが余分に送信される問題が修正されています。

参考