Node.js v19がリリースされました 🎉
この記事では Node.js v19 の主な変更点を抜粋して紹介します!
- HTTP(S)/1.1 KeepAlive by default
- V8 10.7
- ファイル変更時に自動的にプロセス再起動するwatchモード (experimental)
- --experimental-specifier-resolution フラグの削除
- Web Crypto API が stable に昇格
- ShadowRealm (experimental)
- Deprecations and Removals
- まとめ
HTTP(S)/1.1 KeepAlive by default
Node.js から送信されるすべてのHTTP(S)接続において Keep-Alive
を使用することになります。これによりハンドシェイクを減らすことができパフォーマンスの改善に繋がります。
Keep-Aliveについては色々解説記事があるので、ここでは割愛します。
もともと Node.js の http.Agent は keepAlive
プロパティを渡せるようになっていますが、指定しない場合、つまりデフォルトでは false
となります。このデフォルト値が Node.js v19 からは true
になります。
また、サーバの処理にHTTP Keep-Aliveを使用しているアイドル状態のクライアントを server.close()
の呼び出し時に自動的に切断する処理も追加されています。つまり、Keep-Alive
を使ってサーバに接続されているがリクエストを送信していないようなクライアントとの接続を切断します。
V8 10.7
現在 Stage 3 の Intl.NuberFormat V3 が使えるようになります。
Intl.NumberFormat
は昔からある API ですが、この API 群にformatRange
, formatRangeToParts
, selectRange
といった新しい関数が追加されます。
Proposal に書かれているサンプルコードからの引用ですが、次のように指定した範囲がフォーマットされます。
const nf = new Intl.NumberFormat("en-US", { style: "currency", currency: "CHF", maximumFractionDigits: 0, }); nf.formatRange(3, 5); // "CHF 3–5"
const nf = new Intl.NumberFormat("en-US", { style: "currency", currency: "EUR", signDisplay: "always", }); nf.formatRange(2.999, 3.001); // "~+€3.00"
その他に追加された API については前述の TC39 の Proposal を見てください。
ちなみに Intl.NumberFormat V3 は Google Chrome では既に 106 から利用可能です。
ファイル変更時に自動的にプロセス再起動するwatchモード (experimental)
https://github.com/nodejs/node/pull/44366
こちらは Node.js v18.11.0 でも利用できる機能ですが、最近追加された良い機能なので紹介します。
JestとかWebpackとかでファイルを変更したときに自動的に再実行とかされる--watchモードのような機能です。
Node.jsに新しい起動フラグとして--watch
と--watch-path
が追加されました。
Node.js を実行するときに次のように --watch
フラグを付与するとwatchモードとして起動します。
node index.js --watch
実行中のプログラムの依存関係にあるファイル、つまりimportして使っているファイルに変更があったときに自動でNode.jsのプロセスが再起動します。
たとえば、次のようにエントリーポイントである index.js が module A を import し、module A が module C を importしていたとします。
このプログラムを node index.js --watch
で起動します。
もし module C に変更があれば、起動しているNode.jsのプロセスは再起動します。
--watch フラグを有効にしておくことで、Node.jsのサーバをローカルで起動して開発しているときに、サーバの処理を変更後に再起動をわざわざ行わなくても良くなります。
また、--watch-path
フラグも追加されています。
こちらは次のように指定したフォルダ内のファイルを監視するフラグです。
次の例では ./src
と ./tests
を監視しています。
node --watch-path=./src --watch-path=./tests index.js
起動しているエントリーポイントの依存関係にあるファイルだけでなく、上記のようにテストファイルを変更したときに再起動するといったことが可能になります。
Next.js を使っていたりすると当たり前のような機能ですが、そういったwatchモードをサポートしていない技術スタックで開発していたり、ちょっとしたスクリプトを書いたりするときは便利ですね。
ちなみにこちらはNode.js v16やv18の最新版でも利用可能です。
--experimental-specifier-resolution フラグの削除
CJS では、require('./foo')
とした場合、.js
や .cjs
が自動的に補完して読み込みます。
しかし、ESM では、仕様に基づき拡張子の指定が必須です。
以下のように拡張子が付いていない場合エラーになります。
// index.js import './foo'; // Error [ERR_MODULE_NOT_FOUND]
ただ、モジュールローディングをする際に、以下のように--experimental-specifier-resolution=node
を指定すると拡張子を自動で補完してくれるようになり、前述のindex.jsからfoo.jsを読み込むことができます。
node --experimental-specifier-resolution=node index.js
この便利そうな--experimental-specifier-resolution=node
ですが、Node.js v19 で非推奨となります。
代わりに Loaders API を使用すべしとのことです。
Loaders API を使うことで Custom Loader を自ら作成することができます。
以下の記事では CSS を import
で読み込むための Custom Loader の説明がされています。
この記事では次のような Custom Loader 用のファイルを作成しています。
/* loader.mjs */ import { URL } from "url"; import { readFile } from "fs/promises"; /** * This function loads the content of files ending with ".css" to an ECMAScript Module * so the default export is a string containing the CSS stylesheet. */ export async function load(url, context, defaultLoad) { if (url.endsWith(".css")) { const content = await readFile(new URL(url)); return { format: "module", source: `export default ${JSON.stringify(content.toString())};`, } } return defaultLoad(url, context, defaultLoad); }
そして以下のように実行時に --experimental-loader
フラグで Custom Loader を指定します。
node --no-warnings --experimental-loader ./loader.mjs index.mjs
そうすると次のようなコードが含まれる ESM な JavaScript ファイルが実行できます。
import styles from "./styles.css" assert { type: "css" };
このように Custom Loader を定義できるようになったため、--experimental-specifier-resolution=node
が削除されることになりました。
Web Crypto API が stable に昇格
Web 標準な Crypto API が Node.js v15 で Experimental な機能として追加されました。
古くから Node.js には Crypto API がありますが、Web との互換性はありませんでした。
最近の Node.js は Web互換を重要視しており、Web標準の API を積極的に追加しています。
この Web Crypto API もその1つです。
WebCrypto API は JavaScript で暗号化や復号、署名やその検証等の処理を行うことができる Web 標準の API です。
以下は Web Crypto API を使った公開鍵と秘密鍵の生成のサンプルコードです。
const { subtle } = require("node:crypto").webcrypto; const generateRsaKey = async () => { const { publicKey, privateKey } = await subtle.generateKey( { name: "RSASSA-PKCS1-v1_5", modulusLength: 4096, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256", }, true, ["sign", "verify"] ); return { public: publicKey, private: privateKey, }; };
その他の API についてはドキュメントをご確認ください。
ShadowRealm (experimental)
執筆時点で Stage 3 の ShadowRealm の初期実装が追加されました。まだ experimental です。
ShadowRealm の Proposal は以下です。
ShadowRealm については以下の記事がわかりやすいです。
ざっくり言うと、グローバルを汚染しないために新しいJavaScript実行コンテキストを作成することができるAPIです。
前述の @petamoriken さんの記事からの抜粋ですが、以下のようにShadowRealmのコンテキスト内のグローバルと通常のグローバルではコンテキストが異なります。
const realm = new ShadowRealm(); // 異なるグローバル環境を持つ globalThis.foo = "foo"; realm.evaluate(`globalThis.foo = "bar";`); console.log(foo); // "foo" console.log(realm.evaluate(`foo`)); // "bar"
コンテキストが異なるため、他のJavaScriptの実行には影響がないので、ユーザーが入力したスクリプトなんかもセキュアに実行できそうです。
そういったユースケースがあるためか、Salesforce が推しています。
前述のpetamorikenさんの記事でも紹介されていますが、ShadowRealm ができることは Node.js の vm
モジュールですでに実現可能です。
const vm = require("vm"); const result = vm.runInNewContext(` function add(a, b) { return a + b; } add(2, 3); `); console.log(result); // 5
ですが、ShadowRealm は ECMAScript に提案された仕様のため、Webブラウザと互換があります。
まだ Experimental なので、今後どうなるかわかりませんが、Webブラウザでも Node.js でも動かしたいようなケースではこちらを使うほうが良いかもしれません。
Deprecations and Removals
package.json の imports と exports に // を指定することを非推奨
package.json でモジュールのimport/exportのパスを紐付ける機能があります。
そのパスで //
(ダブルスラッシュ)を含めるパスの指定が非推奨となりました。
また、最初と最後に /
があるパターンもダメなようです。
以下は元となった issue からの抜粋です。これらのパターンはすべて invalid であり、非推奨となります。
{ "./x": ".//x/.js" }
- invalid double slash in target{ "./*": "./x*" }
forimport 'pkg/x/z.js
- invalid matched pattern starting with a/
{ "./*": "./x*" }
forimport 'pkg/xz/
- invalid matched pattern ending with a/
{ "./*": "./*x" }
forimport 'pkg//x'
- invalid matched pattern starting with a/
process.exit() の引数に特定の型以外を渡すことを非推奨
process.exit()
の引数および process.exitCode
に代入する値に undefined
、null
、整数、'1'
のような整数文字列以外を渡すことが非推奨となりました。
まとめ
Node.js v19 の変更点を抜粋して紹介しました。
紹介した内容の他にもNode.jsのビルド関連での変更や fs
のパラメータの型チェックなどが入っています。最近は TypeScript を使う人が増えたので、影響範囲は少ないかもしれませんが気になる人はご確認ください。
Keep-Alive
がデフォルト true
にするのは良い変更だと思います。
コネクションを確立しつづけることになるので、Keep-Alive
を明示的に指定していないアプリケーションでは影響があるかもしれないので検証が必要かもしれません。
影響がわからないから変更したくない場合は明示的に false
を指定しておいても良いかと思います。
個人的には--watch
フラグも良いと思いました。
現在主流のフレームワークやバンドラー、テストランナーには当たり前のように実装されている機能で開発には欠かせなくなってきています。
Node.jsのプログラムを書く時も同じような開発体験を得られるのは良いですね。
あとは Web Crypto API が stable になったり、ShadowRealm が実装されたり、Webブラウザとの互換性を高める機能の追加の流れは今後も続きそうです。
最後に自分の話をすると、久々に Node.js にコミットした変更が Node.js v19 に含まれています。
結構ギリギリでマージされたので入らないかもと思っていましたが入って良かったです。
AbortController
の signal
に null
を指定した場合にエラーをスローするように変更しました。これは AbortController
の仕様に合わせるための変更です。
同僚の @nissy_dev が Node.js に WPT のテストを追加したときに気づきました。
ありがとうにっしー。
最後までお読みいただきありがとうございました。不備や質問があれば、お手数ですが@shisama_までお願いします。