皆さんこんにちは。最近、玄米生活を始めました、おっくんです。
去る 2021 年 11 月 9 日に Deno 1.16 がリリースされました。 今回も、リリースノートを参考に 変更事項の気になるところを紹介します。
実行環境
Docker イメージ denoland/deno:centos(確認時点では Deno 1.16.1 でした)
Deno 1.16
Deno 1.16 での変更事項をDeno 1.16 リリースノートを元に確認します。
fetch API が、ファイル URL をサポートするようになりました
これまで Deno では、 fetch を web 標準の API として、ネットワークリソースにアクセスするためのみに使用してきました。
また、ローカルファイルアクセスには、Deno.readfile()
などのメソッドが使用されてきました。
このリリースでは、file プロトコル を使用して fetch でローカルファイルにもアクセスできるようになります。
既存の API Deno.readfile
でのファイルアクセスと比較しながら紹介します。
[これまでのやり方]
// Deno.readfile を使うパターン const loadBytes = await Deno.readFile("./textfile.md"); console.log(loadBytes); // => Uint8Array(18) [ // 72, 101, 108, 108, 111, // 32, 227, 129, 168, 227, // 130, 137, 227, 131, 169, // 227, 131, 156 // ] const text = new TextDecoder().decode(loadBytes); console.log(text); // => Hello とらラボ
[Deno 1.16からできるやり方]
// fetch API + file URL を使うパターン const url = new URL("./textfile.md", import.meta.url); console.log(url); // => URL { // href: "file:///usr/src/app/textfile.md", // origin: "null", // protocol: "file:", // username: "", // password: "", // host: "", // hostname: "", // port: "", // pathname: "/usr/src/app/textfile.md", // hash: "", // search: "" // } const result = await fetch(url); console.log(result); // => Response { // body: ReadableStream { locked: false }, // bodyUsed: false, // headers: Headers {}, // ok: true, // redirected: false, // status: 200, // statusText: "OK", // url: "file:///usr/src/app/textfile.md" // } const text = await result.text(); console.log(text); // => Hello とらラボ
実行にあたっては、ローカルファイルへのアクセスが必要なので、--allow-read
の読み取り権限付与が必要です。
先に示した例の場合、次のように実行できます。
deno run --allow-read app.ts # もしくは、特定のファイルに対してのみ読み取り可能とする deno run --allow-read=//usr/src/app/textfile app.ts # file:/ は不要
fetch API でのローカルファイルアクセスは、チャンクとして読み込みされます。 このことで、大きなファイルを返却する HTTP サーバーを作成するときなど、ファイル全体を一度すべてメモリに持つ必要がありません。
Deno での、fetch API でファイルアクセスする仕様は、Firefox の仕様をベースにしているとのことです。
その他仕様として、
- 「ファイルが無い」場合、次のようにエラーになります。
Uncaught (in promise) TypeError: NetworkError when attempting to fetch resource.
- 「指定されたパスがディレクトリだった」場合、次のようにエラーになります。
Uncaught (in promise) TypeError: NetworkError when attempting to fetch resource.
ブラウザで、file://
などとアクセスするとディレクトリが参照できるので、この点はFirefox の仕様との差異になります。(Windows環境の場合C:/
などでローカルドライブ参照できます。) - レスポンスヘッダーには、
content-length
を含みません。レスポンスボディがストリームであるため、最終的な長さがわからないことが原因です。 - レスポンスヘッダーには、
content-type
ヘッダーは設定されていません。
ファイル拡張子から、コンテンツタイプを知りたい場合、https://deno.land/xのmedia_typesを使用するように勧められています。
最後に、media_typesを使用したコンテンツタイプの取得を行ってみます。
import { lookup } from "https://deno.land/x/media_types@v2.10.2/mod.ts"; const url = new URL("./textfile.md", import.meta.url); console.log(lookup(url.pathname)); // => text/markdown
新しい JSX トランスフォーム のサポート
JSX トランスフォームについては、丁寧な解説が React 公式のブログにあります。 かいつまんで書くと、「JSX を JavaScript のコードに変換する機能」のことです。 この機能が React 17 で、更新され任意のライブラリを参照できるようになりました。 この新しい JSX トランスフォームを、Deno 1.16 からサポートするようになります。
Deno の公式マニュアルの Deno での JSX の構成に書かれている記述を確認すると、デフォルトでは以下の設定が使用されていることがわかります。
{ "compilerOptions": { "jsx": "react", "jsxFactory": "React.createElement", "jsxFragmentFactory": "React.Fragment" } }
この JSX トランスフォーム設定を開発者が切り替えることができます。 JSX トランスフォームをpreactに切り換える2つの方法を紹介します。
やり方1 @jsxImportSource プラグマで記述
/** @jsxImportSource https://esm.sh/preact */ export Welcome({ name }) { return ( <div> <h1>Welcome {name}</h1> </div> ); }
やり方2 --config を使って設定する
[config.json]
{ "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "https://esm.sh/preact" } }
deno run --config config.json hogehoge.ts
のように実行します。
実行すると多数の型定義に関するエラーが発生するかと思います。 Deno での JSX の構成に書かれた制限事項として、 「インポートまたは、エクスポートがない JSX モジュールが型チェック時に正しく動作しない」ことが書かれています。 この動作の不具合は、TypeScript のバグとして報告されています。
github.com 内容としてはJavaScriptにトランスパイルされた結果は、_jsx があることを期待しているが、実際にはインポートされていないのが原因です。
対応として、次の二つの方法が示されています。
- ファイルに export{} を追加する
- --no-check フラグを使用する
先に示した使用方法の関連として、インポートマップの使用方法が記載されているのでこちらも紹介します。
以下のように config.json、importmap.json を用意します。
//[config.json] { "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "preact" } }
//[importmap.json] { "imports": { "preact/jsx-runtime": "https://cdn.skypack.dev/preact/jsx-runtime?dts", "preact/jsx-dev-runtime": "https://cdn.skypack.dev/preact/jsx-dev-runtime?dts" } }
次のように実行することができます。
deno run --config config.json --import-map importmap.json hogehoge.jsx
この時、importmap に記載されたソースコードの取得先には?dts
をつけて型情報を含むことで、--no-check
の付与をせずに済んでいます。
では、config.json
に"jsxImportSource": "https://cdn.skypack.dev/preact/jsx-runtime?dts"
と記述すると、問題は解決することができそうに感じますが、https://cdn.skypack.dev/preact/jsx-runtime?dts/jsx-runtime'
というアドレスで取得を試みるので、この方法では解決できません。
不安定な新たな signal listener API が追加されました
OS からのシグナルを取得する、API が追加されました。不安定なAPI として、--unstable を実行時に要求します。 この API は、既存の Deno.signals API と置き換えられます。 リリースノートでは次のように紹介されています。
const listener = () => { console.log("SIGTERM!"); }; // Starts listening to SIGTERM Deno.addSignalListener("SIGTERM", listener); // Stops listening to SIGTERM Deno.removeSignalListener("SIGTERM", listener);
Error.cause が、コンソールに表示されるようになりました
Deno 1.13 で導入された、Error.cause プロパティにエラーの原因を載せて、デバッグに活かすことができました。 が、コンソールには表示されていませんでした。 Deno 1.16 からは、エラーがスローされるとき、Error.cause がコンソールに表示されるようになっています。
Deno 1.15.3 での動作と比較してみましょう。
// Deno 1.15.3 > throw new Error("main error", { cause: new TypeError("caused by this") }) Uncaught Error: main error at <anonymous>:2:7 // Deno 1.16.1 > throw new Error("main error", { cause: new TypeError("caused by this") }) Uncaught Error: main error at <anonymous>:2:7 Caused by TypeError: caused by this at <anonymous>:3:12
Deno 1.16.1 では、Error.cause に設定された caused by this
という文言が、コンソールに表示されています。
明示的に TLS 接続のハンドウェイクを実行できるようになりました
暗号化された接続をする前に、TLS 接続を確立する必要があります。 この TLS 接続の確立には、TLS ハンドシェイクの実行が必要です。 ほとんどのユーザーは、ハンドシェイクの詳細を気にする必要はなく、読み書きの接続をしたときに自動的に行われます。
Deno.TlsConn に、ハンドシェイクを明示的に行うための handshake() メソッドが追加されています。 このメソッドは、 Promise を返します。 もし、ハンドシェイクがすでに成立している時は、Promise はすぐに resolve を返します。 ハンドシェイクの実行中と完了していないときは、完了した時点で Promise がresolve を返します。
deno doc を参照すると、インターフェイスについてのドキュメントを確認できます。
Deno.startTls が安定化しました
Deno は、Deno.connectTls Deno.startTls の二つの TLS 接続方法を提供しています。
- Deno.connectTls:TCP 接続が開いたなら、すぐに TLS 接続を開始します。
- Deno.startTls:プレーンテキストの TCP 接続が接続されていた上で、TLS 接続が必要な時に切り替えます。
Deno 1.16 では、Deno startTls が安定化し、安定した Deno 用 SMTP ドライバーが作成できるようになりました。 また、Deno 用の安定した PostgreSQL と MySQL のドライバーにも使用できます。
postgres ドライバーは、安定版で動作するようになりました。 次のコードがリリースノートで紹介されています。
import { Client } from "https://deno.land/x/postgres@v0.14.0/mod.ts"; const client = new Client({ user: "user", database: "test", hostname: "psql.example.com", port: 5432, tls: { enforce: true, caCertificates: [await Deno.readTextFile("/path/to/ca.crt")], }, }); await client.connect(); const result = await client.queryObject("SELECT id, name FROM people"); console.log(result.rows); // [{id: 1, name: 'Carlos'}, ...]
テスト用のパーミッション指定が安定化しました
Deno 1.10 で、以下の様にテストに対してパーミッションを許可する機能が導入されていました。
// test.ts // Deno 1.10.1 で確認済み Deno.test({ name: "write", permissions: { write: true, read: false }, // <= permissions プロパティで指定 async fn() { await Deno.writeTextFile("./foo.txt", "Write!"); console.log(await Deno.readTextFile("./foo.txt")); }, });
この機能が安定し --unstable が不要になりました。 以下のように実行すると、実行時のパーミッションとしてファイルの読み書きを許可していますが、 permissions プロパティ で読み込みを禁止しているので、エラーになります。
$ deno test --allow-read --allow-write --unstable test.ts
また、実行時に許可していない場合、permissions プロパティで許可することはできないので、 この点には注意が必要です。
localStorage を使うとき、--location が不要になりました
Deno 1.10 で localStorage を使用できるようになりました。
これまで、--location example.com
のように指定することが必要でしたが、
Deno 1.16 から、--location を付与せずに、localStorage を使用することができます。
どういったキーが使用されるのかは、以下のルールに従います。
- --location を使用して指定:与えられたオリジンに基づいて使用するキーが定まります。
http://example.com/a.ts
とhttp://example.com/b.ts
では、同じストレージになりhttp://example.com/
とhttps://example.com/
は異なったストレージになります。 --config を使用して指定:--config を指定されると異なったストレージが使用されます。
deno run --config deno.jsonc a.ts
とdeno run --config deno.jsonc a.ts
では、同じストレージになりdeno run --config deno.jsonc a.ts
とdeno run --config tsconfig.jsonc a.ts
は異なったストレージになります。設定しない:設定をしない場合、メインモジュールの絶対パスに基づいてストレージが定まります。Deno REPL では、Deno REPL を開始したカレントディレクトリに基づいてストレージが定まります。このため、同じパスで複数回指定無く実行すると、永続化されたデータが残った状態で開始されます。
以下のソースを実行して確認してみます。
const key = "key"; const value = "value"; // localStorage console.log(`key:${localStorage.getItem(key)}`); localStorage.setItem(key, value); console.log(`key:${localStorage.getItem(key)}`);
$ deno run web_strage.ts Check file:///usr/src/app/web_strage.ts key:null key:value $ deno run web_strage.ts key:value key:value $ deno run --location http://example.com web_strage.ts key:null key:value $ deno run --location http://example.com web_strage.ts key:value key:value # オリジン が異なるので、別のストレージ $ deno run --location http://example-1.com web_strage.ts key:null key:value $ deno run --config config.json web_strage.ts key:null key:value $ deno run --config config.json web_strage.ts key:value key:value # --config が異なるので、別のストレージ $ deno run --config config-1.json web_strage.ts key:null key:value
localStrage を含む、web storage API についての詳しい情報は、マニュアルに記載があります。
AbortSignal への理由設定をサポートします
WHATWG が、AbortSignal の理由を設定する仕様を採用しました。Deno は、この仕様を採用した最初のプラットフォームになります。 リリースノートでは、以下のソースが紹介されています。
const abortController = new AbortController(); abortController.abort(); console.log(abortController.signal.reason); // => DOMException: The signal has been aborted const abortController = new AbortController(); const reason = new DOMException("The request timed out", "TimeoutError"); // <= 理由を設定 abortController.abort(reason); console.log(abortController.signal.reason); // => DOMException: The request timed out <= 指定した The request timed out が取得できます
Deno 向けパッケージを Node 向けパッケージに変換するツールの提供
Deno 向けモジュールを npm パッケージとして、公開するためのdntが導入されます。 dnt を使用して npm の公開された例として、deno_license_checkerがあります。
dnt によって、Deno ファーストなコードを Node 環境で使うことができます。 ソースは、Deno 公式リポジトリで管理されていますが、サードパーティモジュールとして管理されているのがちょっと面白いです。
その他
V8 が 9.7 に更新されました
数多くのパフォーマンス改善とバグフィックスが含まれています。 機能の追加として次のものが導入されます。
- findLast findLastIndex が Array クラスに追加されます。
- WebAssembly の参照型をサポートするようになりました。
WebAssembly の参照型については、The
wasm-bindgen
Guideにある参照型の解説が助けになるかと思います。
Web Streams API が改善されました
- ReadableStreamBYOBReader をサポートします。
- WritableStreamDefaultController.signal をサポートします。
- ReadableStream.getIterator が廃止されます。
まとめ
Deno 1.16 は、dnt とfetch APIのファイルURL対応、localStorage を使うときに --location が不要になったのがエポックなリリースだと感じました。 特に dnt は、これから Deno 向けモジュールがより増えていく契機になるかもしれません。
また次のリリースも追いかけていきます。
P.S.
採用情報
■募集職種
yumenosora.co.jp
カジュアル面談も随時開催中です
■お申し込みはこちら!
news.toranoana.jp
■ToraLab.fmスタートしました!
メンバーによるPodcastを配信中!
是非スキマ時間に聞いて頂けると嬉しいです。
anchor.fm
■Twitterもフォローしてくださいね!
ツイッターでも随時情報発信をしています
twitter.com