Deno 1.10.1 がリリースされたので新機能や変更点の紹介
Copyright (c) 2018-2021 the Deno authors. MIT License.
日本時間の今日(2021 年 5 月 12 日)に Deno の v1.10.0 v1.10.1 がリリースされました。
※ 1.10.0 がリリースされる予定でしたが、1.10.0 のリリース後すぐにバグが発覚したため、revert 対応がなされた 1.10.1 がリリースされました。
詳細なリリース内容は上記のリリースノートにまとまっていますが、ざっと紹介していきたいと思います。
-
deno test
コマンドの改良 -
Worker.postMessage
が「構造化複製アルゴリズム」をサポート - Web Storage API サポート
- Markdown ファイルへのフォーマッタ適用を抑制する
deno-fmt-ignore-file
ディレクティブ - 共有Wasmメモリの有効化をサポート
- リモート import map が使えるように
- plugin API のアップデート
- Deno CLI の機能を使う際に必要だった
--unstable
フラグが削除
deno test
コマンドの改良
1. Deno は組み込みのテストランナーを備えています。このテストランナーに大幅な改修が施されました。
テストの並列実行ができるようになった
これまで Deno のテストランナーは直列で実行されていましたが、v1.10 からは並列実行ができるようになりました。
--jobs
というオプションで並列数を指定できます。特に何も指定しなかった場合は、これまで通り直列で実行されます。
個々のテストにパーミッションを設定できるようになった
Deno はファイルへの読み書き、ネットワークアクセス、などに対して明示的な「パーミッション」を与える必要があるというモデルを採用しています[1]が、v1.10 からは個々のテストケースに対してパーミッションを指定することができるようになりました。
以下の例を見てみます。
Deno.test({
name: "write only",
permissions: { write: true, read: false },
async fn() {
await Deno.writeTextFile("./foo.txt", "I can write!");
console.log(await Deno.readTextFile("./foo.txt"));
},
});
この例では、ファイルの読み込みと書き込みを行っていますが、permissions
で read: false
となっています。つまり、このテストは read
パーミッションが無いという理由で失敗することが期待されます。
実行してみましょう。
$ deno test --allow-read --allow-write --unstable test_permissions.ts
Check file:///Users/ry/src/deno/test_permissions.ts
running 1 test from file:///Users/ry/src/deno/test_permissions.ts
test write only ... FAILED (5ms)
failures:
write only
PermissionDenied: Requires read access to "./foo.txt", run again with the --allow-read flag
at deno:core/core.js:86:46
at unwrapOpResult (deno:core/core.js:106:13)
at async open (deno:runtime/js/40_files.js:46:17)
at async Object.readTextFile (deno:runtime/js/40_read_file.js:40:18)
at async fn (file:///Users/ry/src/deno/test_permissions.ts:6:17)
at async asyncOpSanitizer (deno:runtime/js/40_testing.js:21:9)
at async resourceSanitizer (deno:runtime/js/40_testing.js:58:7)
at async exitSanitizer (deno:runtime/js/40_testing.js:85:9)
at async runTest (deno:runtime/js/40_testing.js:199:7)
at async Object.runTests (deno:runtime/js/40_testing.js:244:7)
failures:
write only
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out (37ms)
期待通り read
パーミッションが無いことにより失敗しています。
ここで、テストケースごとに指定するパーミッションは、プロセスに対して与えるパーミッションを超えることができない ということに注意してください。
つまり、上の例では、テストの実行コマンドとして deno test --allow-read --allow-write
というように read
と write
のパーミッションを付与しています。このような場合に、テストケースに渡す permissions
で net: true
というような指定はできない、ということです。
テストケースに渡す permissions
が省略された場合には、起動コマンドに渡されたパーミッションがそのまま使われます。
この機能は Unstable としての提供です。使用するためには --unstable
フラグが必要です。
テストランナーの出力が改善された
テスト実行時の出力で、そのモジュールがいくつのテストケースを含んでいるのかと、そのテストがどのファイルに書かれたものなのかが表示されるようになりました。
running 4 tests from file:///dev/deno/cli/tests/unit/tty_test.ts
test consoleSizeFile ... ok (11ms)
test consoleSizeError ... ok (4ms)
test isatty ... ok (4ms)
test isattyError ... ok (3ms)
running 6 tests from file:///dev/deno/cli/tests/unit/rename_test.ts
test renameSyncSuccess ... ok (17ms)
test renameSyncReadPerm ... ok (5ms)
test renameSyncWritePerm ... ok (6ms)
test renameSuccess ... ok (13ms)
test renameSyncErrorsUnix ... ok (34ms)
test renameSyncErrorsWin ... ignored (1ms)
...
(これまでは running 4 tests from ...
の行がなかった)
ドキュメント内の型チェックができるようになった
コード内のドキュメントにコード例を書いたものの、その後コードを更新するときにドキュメントの更新を忘れてしまった、ということがよくあります。
そのようなドキュメントとコードの乖離を防ぐため、ドキュメント内に書いたコードブロックの型チェックを行うことができるようになりました。
/**
* ```
* import { example } from "./test_docs.ts";
*
* console.assert(example() == 42);
* ```
*/
export function example(): string {
return "example";
}
この例では、example
関数は string
を返すような実装になっているのに対し、ドキュメント内では number
を返すことになっていて、実装とドキュメントの乖離が生まれてしまっています。
以下のように --doc
をつけることで、型チェックを行ってくれて、実装とドキュメントの乖離に気づくことができます。[2]
$ deno test --doc https://deno.com/v1.10/test_docs.ts
Check file:///dev/test_docs.ts:2-7
error: TS2367 [ERROR]: This condition will always return 'false' since the types 'string' and 'number' have no overlap.
console.assert(example() == 42);
~~~~~~~~~~~~~~~
at file:///dev/test_docs.ts:2-7.ts:3:16
将来的には、ドキュメント内のコードブロックで型チェックのみならず通常のテストと同等のことが行えるようになる予定です。Rust みたいですね。
--watch
に対応した
Deno が提供しているツールのうちいくつかは --watch
フラグをつけることでファイルの変更を監視してくれるようになります。例えば deno run --watch foo.ts
とすると foo.ts
の変更を検知して自動で再実行をしてくれるようになります。
これまで --watch
はテストランナーには対応していなかったのですが、v1.10 からはテストでも --watch
が使えるようになりました。
Worker.postMessage
が「構造化複製アルゴリズム」をサポート
2. Deno は 1.0 リリース時点から Web Worker をサポートしており、別スレッドでスクリプトを実行することができましたが、ワーカースレッドとメインスレッドの間でメッセージをやり取りする際の方式に問題があり、送受信できるデータに制限がありました。
v1.10 からは 構造化複製アルゴリズム が使われるようになり、この問題は解決しました。
例えば、以下のように循環した構造を持つオブジェクトをワーカーに送ると、以前はエラーになっていました:
const obj = { hello: "world" };
obj.self = obj;
const worker = new Worker(
"data:application/javascript,self.onmessage = (e) => self.postMessage(e.data);",
{ type: "module" },
);
worker.onmessage = (e) => {
console.log("Received event:", e.data);
};
worker.postMessage(obj);
v1.10 では問題なく動きます。
3. Web Storage API サポート
Web Storage API が使えるようになりました。localStorage
と sessionStorage
を、ブラウザで使うのと同じような使用感で使うことができます。
localStorage
は最大 5 MB のデータを保存できて、プロセスを再起動してもデータは残り続けます。一方、sessionStorage
に保存したデータは、そのプロセスが終了するまでの間だけ生存し続けます。
Web Storage API を利用するにあたって、パーミッションの付与は必要ありません。
また、データの保存の際、ブラウザ上だとサイトのオリジン単位で分けられた空間に保存されます(例えば https://example.com と http://example.com だと別のストレージに保存される扱いになる)が、Deno でこの「オリジン」を指定するために、起動時に --location
オプションで値を渡す必要があります。
const key = Deno.args[0];
if (key === undefined) {
// コマンドライン引数が渡されなかったら、
// 今 localStorage に保存されているエントリの数を表示
console.log(localStorage.length);
} else {
const value = Deno.args[1];
if (value === undefined) {
// コマンドライン引数が1つだけ指定されていたら、
// それをキーとして値を取得し、表示する
console.log(localStorage.getItem(key));
} else {
// コマンドライン引数が2つ指定されたら、
// それらをキー・バリューの組み合わせとして保存する
localStorage.setItem(key, value);
console.log(`${key}: ${value} is saved`);
}
}
$ deno run --location https://example.com ./kv.ts
0
$ deno run --location https://example.com ./kv.ts foo bar
foo: bar is saved
$ deno run --location https://example.com ./kv.ts foo
bar
$ deno run --location https://example.com ./kv.ts
1
$ deno run ./kv.ts # `--location` を渡さないとエラー
error: Uncaught ReferenceError: Access to "location", run again with --location <href>.
console.log(localStorage.length);
^
at get (deno:extensions/web/12_location.js:365:17)
at createStorage (deno:extensions/webstorage/01_webstorage.js:91:28)
at localStorage (deno:extensions/webstorage/01_webstorage.js:178:24)
at file:///tmp/hostname.ts:6:15
deno-fmt-ignore-file
ディレクティブ
4. Markdown ファイルへのフォーマッタ適用を抑制する Deno は組み込みのフォーマッタ deno fmt
コマンドを備えています。
- JavaScript
- TypeScript
- JSON
- Markdown
に対応したフォーマッタです。
基本的にはフォーマットを機械的に適用してしまうのが望ましいですが、一部ファイルにフォーマットを適用したくない、という場合もあるかと思います。
そのようなときには、ファイルの先頭に // deno-fmt-ignore-file
というディレクティブコメントをつけることで、deno fmt
の適用対象外とすることができます。
しかしこのディレクティブは今まで JavaScript / TypeScript でしか使えませんでした。
v1.10 からは Markdown ファイルでもこのディレクティブが使えるようになりました。
<!-- deno-fmt-ignore-file -->
をファイルの先頭に付ければ OK です。
5. 共有Wasmメモリの有効化をサポート
共有Wasmメモリを使うことができるようになりました。これは Chrome と Firefox ではすでにデフォルトで有効になっている機能で、ついに Deno でも利用可能となった形です。
以下のように shared
を true
にして WebAssembly.Memory
コンストラクタに渡すことで、共有Wasmメモリが生成され、これをデータストアとした SharedArrayBuffer が使えるようになります。
const memory = new WebAssembly.Memory({
initial: 1,
maximum: 10,
shared: true,
});
console.assert(memory.buffer instanceof SharedArrayBuffer);
Wasm のスレッドサポートはまだ Deno では利用できませんが、優先度高く対応していく予定です。
Wasm スレッドについては以下の記事が参考になります。
6. リモート import maps が使えるように
Deno は v1.8 から import maps に対応しましたが、今回のバージョンからは "リモート import maps" が使えるようになりました。
つまり、import map のファイルがローカルのファイルシステムに存在している必要はなくなったということです。
以下のようにHTTP越しで import map ファイルを指定できます。
$ deno install --import-map=https://example.com/import_map.json -n example https://example.com/mod.ts
7. plugin API のアップデート
Deno は Rust で書かれたプラグインを読み込むという機能をもっています。ネイティブプラグインと呼ばれていますが、かなりの不安定機能で、セキュリティ的な懸念もあることから、いったん削除することも議論されていました。
そんな中ではありますが、1.9.0 の目玉アップデートだった serde_v8
を利用し、ネイティブプラグインがリファクタリングされました。
このリファクタリングによって、ネイティブプラグインが提供する Op をサードパーティコードなしで呼び出せるようになりました。[3]
また、Rustのオブジェクトが保存されている ResourceTable
という場所にプラグインからアクセスができるようにもなりました。
新しいネイティブプラグインの実装例は こちら(test_plugin
) にあります。
--unstable
フラグが削除
8. Deno CLI の機能を使う際に必要だった Deno ではいくつかの機能は Unstable として提供されていて、将来的にその機能が削除されたり、別の名前に変わったり、インターフェースが変わったりする可能性がありますよということが示されています。
そのような機能を使う際には実行コマンドに --unstable
フラグをつける必要があります。
例えば、1.10.1 現在では Deno.hostname
というAPIは Unstable として提供されているため、以下のようなスクリプトを
console.log(Deno.hostname());
--unstable
なしで実行しようとすると、エラーになります。
$ deno run hostname.ts
Check file:///tmp/hostname.ts
error: TS2339 [ERROR]: Property 'hostname' does not exist on type 'typeof Deno'. 'Deno.hostname' is an unstable API. Did you forget to run with the '--unstable' flag?
console.log(Deno.hostname());
~~~~~~~~
at file:///tmp/hostname.ts:1:18
また、このようにスクリプトの実行時に必要な --unstable
の他にも、Deno CLI に備わっているツールを実行するときにも --unstable
が必要なことがありました。
例えば Deno に組み込まれている TypeScript / JavaScript リンターの deno_lint を使うときには、
$ deno lint --unstable foo.ts
というようにする必要がありました。
1.10 からは、この「CLIツールを起動するときに必要だった --unstable
」が不要になりました。
$ deno lint foo.ts
Unstable な API を含んだスクリプトを実行する際には依然として --unstable
が必要です。
おわり
以上、1.10.1 の紹介でした。
次のマイナーバージョン 1.11.0 のリリースは 2021 年 6 月 8 日の予定です。
過去のリリース情報
1.9.0
1.8.0
1.7.0
1.6.0
-
詳細は https://deno.land/manual/getting_started/permissions#permissions を参照 ↩︎
-
手元で試してみたのですが、動かないですね……🤔 ↩︎
-
ネイティブプラグインについてほとんどドキュメントがなく、また筆者の前提知識もないので、間違った訳になっている可能性があります。 ↩︎
Discussion