Deno v1.20がリリースされました。
この記事では新しく追加された機能などについて紹介します。
(破壊的変更) Deno.testのpermissionsオプションの挙動が変更
⚠️ この変更はv1.20.1時点ではまだ反映されていません!
現在、正式な修正用のPRが作成されており、おそらくv1.20.2で反映されるはずです
Deno.test
やWorker
などのAPIはpermissions
オプションにより実行時のパーミッションをカスタマイズできます:
Deno.test({
name: "permissions_test",
permissions: { read: true },
fn: async () => {
const content = await Deno.readTextFile("./data.txt");
await Deno.writeTextFile("./data.txt", processContent(content));
},
});
上記のテストコードはDeno.readTextFile
とDeno.writeTextFile
を実行しており、正しく実行するためには--allow-read
と--allow-write
の両方の権限が必要です。
このテストケースでは、下記宣言により--allow-read
を明示的に指定しています。
permissions: { read: true },
ここではwrite: true
が指定されていないため、このテストケースは権限エラーにより失敗するというのが直感的な挙動なのではないかと思います。
しかし、Deno v1.19時点では上記のテストコードは成功してしまいます。
permissions: { read: true },
実は、Deno v1.19において、この指定は下記宣言と同義になります:
// `read`以外はすべてCLIオプションで指定された権限(--allow-writeなど)が継承される
permissions: {
read: true,
env: "inherit",
ffi: "inherit",
hrtime: "inherit",
net: "inherit",
run: "inherit",
write: "inherit",
},
この挙動は直感的ではないということで、Deno v1.20にて修正されました。
具体的には、permissions: { read: true }
の指定は、下記宣言と同義になります:
permissions: {
read: true,
env: "none",
ffi: "none",
hrtime: "none",
net: "none",
run: "none",
write: "none",
},
この変更により、先程紹介したテストコードは、直感通り権限エラーによって失敗するようになります。
deno task
コマンドが実装
⚠️ このコマンドはまだunstableという扱いであり、今後まだ変更が入る可能性が高いためご注意!
Deno本体にnpm-scripts相当の機能が実装されました。
使用方法としては、まずdeno.json(c)の"tasks"
フィールドでスクリプトを定義します。
{
"tasks": {
"start": "deno run --allow-net mod.ts"
}
}
このように定義したスクリプトはdeno task <タスク名>
で実行できます:
$ deno task start
また、引数なしでdeno task
を実行するとdeno.json(c)
で定義されているタスク一覧を表示することができます:
$ deno task
一見、npm-scripts
に似ていますが、下記のような違いがあります:
このように、deno task
では各プラットフォームごとの差分をうまく吸収することが意識されています。(これはdeno_task_shellというRust crateによって実現されています)
deno bench
コマンドが実装
Deno本体にベンチマークを取る機能が実装されました。
まず、Deno
ネームスペースに下記APIが追加されています:
Deno.bench({
// ベンチマークの実行回数 (デフォルトは`1000`)
n: 1000,
// ウォームアップの実行回数 (デフォルトは`1000`)
// JITによる最適化を目的として、ベンチマーク開始前に、ここで指定された回数だけ`fn`が繰り返し実行されます (このウォームアップ処理は計測結果には影響しません)
warmup: 1000,
// ベンチマークコード
// `n`+`warmup`の回数だけこの関数が実行されます
fn: () => {
doSomeHeavyComputation();
},
});
このAPIを使ってベンチマークコードを記述します。
例えば、下記のように利用します。
function sum(...numbers: Array<number>): number {
return numbers.reduce((a, b) => a + b, 0);
}
Deno.bench({
name: "sum",
fn: () => {
sum(1, 2, 3, 4, 5);
},
});
⚠️ ベンチマークコードは
deno test
と同様に、bench.ts
や*_bench.ts
などの名前のファイルで記述する必要があります。
そして、deno bench
コマンドを実行すると、定義したベンチマークコードを実行することができます。
$ deno bench --unstable
running 1 bench from file:///home/uki00a/ghq/github.com/uki00a/deno-sample/bench.ts
bench sum ... 1000 iterations 842 ns/iter (682..59,111 ns/iter) ok
(7ms)
bench result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out (17ms)
こちらのコマンドもまだunstableという扱いであり、今後変更が入る可能性があるためご注意を!
deno test
で--no-prompt
の挙動がデフォルト化
Deno v1.19で--prompt
オプションの挙動がデフォルト化されました
ただし、この振る舞いはdeno test
においては適切ではないということで、deno test
の実行時は--no-prompt
オプションの挙動がデフォルト化されました。
deno test --trace-ops
deno test
コマンドはリソースリークを検知する機能を備えています。
Deno v1.19でこのリソースリーク発生時のトレース情報が大幅に改善されました。
しかしこの改善されたトレース情報の出力にはパフォーマンスに影響があったようで、今回のv1.20でデフォルトで無効化されました。
そして、--trace-ops
という新しいオプションが導入され、改善されたトレース出力を任意で有効化できるようになりました。
$ deno test --trace-ops test.ts
running 1 test from file:///home/uki00a/ghq/github.com/uki00a/deno-sample/test.ts
test trace_ops ... FAILED (6ms)
failures:
trace_ops
Test case is leaking async ops.
- 1 async operation to sleep for a duration was started in this te
st, but never completed. This is often caused by not cancelling a
`setTimeout` or `setInterval` call. The operation was started here
:
at Object.opAsync (deno:core/01_core.js:161:42)
at runAfterTimeout (deno:ext/web/02_timers.js:234:31)
at initializeTimer (deno:ext/web/02_timers.js:200:5)
at setTimeout (deno:ext/web/02_timers.js:337:12)
at file:///home/uki00a/ghq/github.com/uki00a/deno-sample/test.ts:2:3
at testStepSanitizer (deno:runtime/js/40_testing.js:441:13)
at asyncOpSanitizer (deno:runtime/js/40_testing.js:145:15)
at resourceSanitizer (deno:runtime/js/40_testing.js:367:13)
at Object.exitSanitizer [as fn] (deno:runtime/js/40_testing.js
:424:15)
at runTest (deno:runtime/js/40_testing.js:784:18)
HTTPレスポンスの自動的な圧縮がサポート
Denoの組み込みHTTPサーバでレスポンスボディの自動的な圧縮がサポートされました。
まず、Deno.serveHttp()
やstd/httpを使用してHTTPサーバを起動します。
import { serve } from "https://deno.land/std@0.130.0/http/mod.ts";
serve(async () => {
const json = await Deno.readTextFile("./data.json");
return new Response(json);
});
そして、Accept-Encoding
ヘッダを付与してサーバにリクエストを送信します。
すると、下記のようにBrotliによって圧縮された状態でレスポンスが返却されます。
$ curl -I -H "Accept-Encoding: br" http://localhost:8000
HTTP/1.1 200 OK
content-type: text/plain;charset=UTF-8
vary: Accept-Encoding
content-encoding: br
content-length: 53
date: Sat, 19 Mar 2022 05:58:59 GMT
現在では、圧縮方式としてBrotliとgzipの2種類がサポートされています。
Deno.connect()
の戻り値の変更
Deno.connect()
を使うと、TCPやUnixドメインソケットによるコネクションを作成できます。
元々、このAPIはDeno.Conn
という型を返却していましたが、Deno v1.20で以下のような型を返すように変更されました:
- Deno.TcpConn (
transport: "tcp"
の指定時) - Deno.UnixConn (
transport: "unix"
の指定時)
これに合わせて、Deno.Conn
に定義されていたsetNoDelay()
やsetKeepAlive()
メソッドもDeno.TcpConn
へ移動しています。
経緯などについては以下を参照ください:
Deno.listenTls
でcert
とkey
オプションがサポート
元々、Deno.listenTls
ではcertFile
とkeyFile
を使用して、証明書と秘密鍵ファイルを指定することができました。
Deno v1.20ではcert
とkey
オプションがサポートされ、より柔軟な指定ができるようになりました。
const cert = await Deno.readTextFile("example.crt");
const key = await Deno.readTextFile("example.key");
// certとkeyオプションで証明書と秘密鍵を文字列で渡せます
const listener = Deno.listenTls({
hostname,
port,
cert,
key,
});
これにより、例えば環境変数から証明書や秘密鍵を読み込んだりすることができるようになります。
合わせて、既存のcertFile
とkeyFile
オプションは非推奨化されています。
パフォーマンス改善
Deno本体にdeno_opsというcrateが実装され、opの呼び出しが大幅に最適化されました。 (opについてはこちらの記事を参照)
その他には、atob
/btoa
の大幅なパフォーマンスが最大20倍程まで改善されています。
deno.json(c)
でのimportMap
オプションのサポート
deno.json(c)
でImport mapsファイルを指定できるようになりました。
ここでImport mapsファイルを指定しておけば、--import-map
オプションを指定せずとも、自動で読み込まれるようになります。
{
"importMap": "vendor/import_map.json"
}
この機能は特にdeno vendorコマンドを使うときなどに便利でしょう。
AbortSignal.timeout()
がサポート
このAPIを使うと、指定された時間後に自動でキャンセルされるAbortSignal
を作成することができます。
const signal = AbortSignal.timeout(3000); // 3秒後にタイムアウト
const res = await fetch(url, { signal });
fetch
と組み合わせたり、テストコードなどで活用すると便利そうです。
Node.js互換モード(--compat
)の改善
CJSとESM形式のモジュールの相互運用性が改善されています。
具体的には、--compat
指定時にreact
がimport
できない問題などが解消されています(内部的には、.js
形式のファイルは今までESM形式で読まれていましたが、cjs
形式で読まれるように修正されています)
また、CJSモジュールの動的インポートなどもサポートされています。
他にも、std/nodeへの機能追加などにより、node-mysql2パッケージがある程度動作するようになっています。
TypeScriptが4.5.2から4.6.2へアップデート
Denoに搭載されているTypeScriptのバージョンがv4.5.2からv4.6.2にアップデートされました。
TypeScript v4.6については、下記を参照ください:
その他の変更点
Deno.upgradeHttp
という新しいAPIが追加されました。 (WebSocketなどのHTTPベースのプロトコルの実装に使用することを想定したAPIのようです)- TCPソケットの生成時に、非Windows環境ではデフォルトでSO_REUSEADDRが有効化されるように変更されました。
Deno.emit()
でdata: URL
がサポートされました。
など