2023年10月17日にリリースされたNode.js v21の主な変更点を紹介します。
この記事を書いている時点ではv21.4.0が最新版ですので、v21.0.0からv21.4.0までの変更点で注目の機能をまとめています。
- fetchとWebStreamsが安定版へ
- WebSocketの実験的な実装
- navigator.language/languages の追加
- V8 11.8 による新しい JavaScript の機能
- ESM をデフォルト化するフラグ
- 実行ファイルがESMなのか自動判定する実験的機能
- import.meta.dirname/filename の追加
- テストランナーがglobでのテストファイル指定に対応
- 警告(Warning)を無効化するフラグの追加
- その他の変更点
- まとめ
- 参考記事
fetchとWebStreamsが安定版へ
Webアプリケーションの開発者ならfetchを一度は見たり使ったことがあるかと思います。
Node.jsでは、fetchはv16.15.0とv17.5.0で実験的に追加されました。
v18.0.0からは--experimental-global-fetch
のようなフラグを付けなくても使えるようになりましたが、まだ実験的な機能でした。
v21.0.0からは実験的な機能ではなくなりました。
88%のテストを通過しているため、まだ完全ではありませんが、安定版へと昇格しました。
また、WebStreamsも安定版になりました。
Web Streams API は Web(ブラウザ)の Stream API と同じインターフェイスや仕様に沿った新しい Stream の API です。 元々 Node.js には Stream API がありましたが、ブラウザとの互換性がない API でした。 Node.js は Web との互換性を重視するようになり、Web の API を積極的に Node.js 本体に実装しています。 そのため、従来の Node.js の Stream API は残しつつも、Web 互換な Stream API を実装しました。
詳しくはNode.js v18の変更点の記事をご覧ください。
WebSocketの実験的な実装
Webフロントエンド開発者ならWebSocketを一度は聞いたり使ったりしたことがあると思いますが、Node.jsではまだ実験的な実装となっています。
実験的な機能のため、--experimental-websocket
フラグが必要です。フラグを付けて実行したときだけWebSocket
オブジェクトがグローバルからアクセスできるようになっています。
$ node --experimental-websocket index.js
// --experimental-websocket フラグがないとエラーになる const socket = new WebSocket("ws://localhost:8080");
navigator.language/languages の追加
navigator.language
とnavigator.languages
はブラウザの言語設定を取得するAPIです。
Node.jsは多言語をサポートしています。
navigator.language
を使えば、実行中のNode.jsのプロセスで利用されている言語を取得することができます。
console.log(navigator.language);
// en-US
navigator.languages
を使えば、実行中のNode.jsのプロセスで利用可能な言語の一覧を取得することができます。
console.log(navigator.languages);
// ["en-US", "zh-CN", "ja-JP"]
V8 11.8 による新しい JavaScript の機能
JavaScriptエンジンであるV8が11.8にアップデートしました。
それにより以下の新しいJavaScriptの機能が追加されています。
- Array grouping
- ArrayBuffer.prototype.transfer
- WebAssembly extended-const expressions
ここでは、使い所が多いと思われるArray groupingについて紹介します。
TC39のproposalに記載の以下のコードを読んでいただくとわかりやすいと思います。 github.com
const array = [1, 2, 3, 4, 5]; // `Object.groupBy` は任意のキーで値をグループ化します。 // 以下は奇数(odd)と偶数(even)でグループ化しています。 Object.groupBy(array, (num, index) => { return num % 2 === 0 ? 'even': 'odd'; }); // => { odd: [1, 3, 5], even: [2, 4] }
ESM をデフォルト化するフラグ
Node.jsは歴史的経緯からECMAScript標準であるES Modules(ESM)をデフォルトで有効にしておらず、CommonJS(CJS)をデフォルトで有効にしています。
ただし、package.jsonに"type": "module"
を記述することでESMをデフォルトで有効にすることができたり、ファイルの拡張子が.mjs
の場合はESMとして扱われたりします。
しかし、package.jsonに"type": "module"
を記述されておらず、拡張子が.js
の場合はESMなのかCJSユーザーの明確な指定がないため、"ambiguous" files(曖昧なファイル)とされています。
そこで、Ambiguous fileは前述のとおりCJSとして扱われていますが、v21.0.0で実験的に導入された--experimental-default-type
フラグを使うことで"ambiguous" filesをESMとして扱うことができます。以下のようにフラグをつけて実行すると、"ambiguous" fileがESMとして扱われます。
$ node --experimental-default-type=module index.js
ESMのデフォルト化については以下の記事が詳しいです。
実行ファイルがESMなのか自動判定する実験的機能
もう1つESM周りの変更点としては、v21.1.0から--experimental-detect-module
フラグが実験的に追加されています。
このフラグをつけると、package.jsonのtype
が設定されていない場合でも.js
の拡張子のファイルの中身を見て、ESMかCJSかを判別してくれます。
$ node --experimental-detect-module index.js
この判別処理に伴い実行時のパフォーマンスが低下する可能性があるため、Node.jsチームとしてはpackage.jsonのtype
を設定することを推奨しています。
import.meta.dirname/filename の追加
ESMを利用すると、CJSでは利用可能だった__dirname
や__filename
が利用できなくなります。
そこで、以下のようなコードを書く必要がありました。
// /path/to/file/index.js から実行 import url from 'url' import path from 'path' const __filename = url.fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); console.log(__filename); // /path/to/file/index.js console.log(__dirname); //path/to/file
これは煩わしいので、従来の__dirname
や__filename
と同じように現在のディレクトリとファイル名にアクセスできるimport.meta.dirname
とimport.meta.filename
が追加されました。
上記のコードは以下のように書き換えることができます。
// /path/to/file/index.js から実行 // const __filename = url.fileURLToPath(import.meta.url); import.meta.filename; // => /path/to/file/index.js // const __dirname = path.dirname(__filename); import.meta.dirname; // => /path/to/file
テストランナーがglobでのテストファイル指定に対応
v18.0.0でNode.js本体にテストランナーが追加されました。 shisama.hatenablog.com
JestやVitestなどのライブラリを使わなくてもNode.js本体のテストモジュールを利用することで以下のようなテストを記述することができます。
import test from 'node:test'; import assert from 'assert/strict'; test('top level test', async (t) => { await t.test('subtest 1', (t) => { assert.strictEqual(1, 1); }); await t.test('subtest 2', (t) => { assert.strictEqual(2, 2); }); });
モックやタイマーといった機能も備えており、ちょっとしたテストならカバーできると思います。詳しくはAPIの仕様をご確認ください。
このテストランナーですが、v21.0.0からはglobでのテストファイル指定に対応しました。
$ node --test **/*.test.js
これまで--test
などのコマンドラインオプションは存在していましたが、以下のように個別のファイル名やディレクトリ名を指定することしかできませんでした。
node --test test1.js test2.mjs custom_test_dir/
globを使ったファイル指定ができるようになったことでテストの実行はより簡単になりました。
警告(Warning)を無効化するフラグの追加
Node.jsを実行したときに、非推奨(Deprecated)な機能や実験的(Experimental)な機能を使っていると実行時に警告が出ます。
その警告を無効化するための--disable-warning
フラグが追加されました。
以下のようにフラグをつけて実行すると、特定の警告を警告が出なくなります。
$ node --disable-warning=DEP0025 index.js
上記のようにコードを指定することで特定の非推奨な機能の警告を無効化することができます。
非推奨機能のコードについては以下のリンクをご確認ください。
また、非推奨な機能や実験的な機能の使用に対する警告を一括で無効化するためのオプションも用意されています。
以下は非推奨な機能の警告を無効化するためのオプションです。
$ node --disable-warning=DeprecationWarning index.js
以下は実験的な機能の警告を無効化するためのオプションです。
$ node --disable-warning=ExperimentalWarning index.js
開発者が意図的に非推奨な機能や実験的な機能を使っている場合は、警告を無効化することで警告が出なくなります。
しかし、意図していない場合は、警告を無効化することで問題が発生する可能性があります。なので、警告を無効化する場合は注意が必要です。
その他の変更点
fs.writeFile
関数に'flush'
オプションが追加されたり、パフォーマンスの改善されたり、他にもさまざまな変更点があります。
詳しくは以下のリンクをご確認ください。
まとめ
Node.js v21はfetch
やWebStream
の安定リリース、WebSocketの追加、ESM周りの改善のための実験的な機能が実装、import.meta
にfilename
とdirname
が追加されるなど、Webとの互換性やESM利用に向けた改善機能が追加されました。
Node.js v21はバージョンが奇数なのでサポート期間は短く、2024年6月までとなっています。 日程についての最新の情報は以下のリポジトリをご確認ください。
また、この記事で紹介した機能のサンプルコードは以下のリポジトリにあります。参考になれば幸いです。
最後までお読みいただきありがとうございました。不備や質問がございましたら、@shisama_までメンションするかブコメなどでコメントください。