Node.js コアモジュールの import/require には `node` schemeがつけられる

Node.js アドベントカレンダーの 3 日目の記事です。空きを埋める形で始めました。

qiita.com

www.codegrid.net

CodeGrid でも書かせていただきましたが、 Node.js で ES Module / CommonJS を使ってコアライブラリのロードをする際、 node から始まる scheme を付けることが可能になっています。

nodejs.org

// ESM
import fs from "node:fs/promises";
// CJS
const http = require("node:http");

これにはいくつかのメリットがあります。基本的につけておくことが望ましいです。 今回はメリットをいくつか紹介します。まだこれがデファクト・スタンダードになっている訳ではありませんが、これから付けてもらうように推奨していきたいと思います。

メリット1: Node.js コアモジュールであることが明示される

Node.js のコードを始めてみたときに、このモジュールが Node.js コアから提供されているのかそれとも npm から提供されているのかイマイチよく分からなかった経験ありませんか。 querystring というモジュールがコアからも npm から 3rd party library としても提供されているので、昔誤って npm install querystring してしまうこともありました。これをインストールしたとしてもコアモジュールのほうがロードされるので、余計なものが入ってしまうだけではあるのですが、なるべく避けたい事態ですね。

今回からは node:querystring で初めておくことで、コアモジュールからのロードであることが明示されます。意図しないダウンロードも防げるでしょう。

メリット2: Node.js の将来のコアライブラリが既存のライブラリと被ることを避けられる

http2 モジュールを作るときに最初 require("https").http2 のような形で提供するかどうかを議論になったのですが、これはもともと http2 モジュールが取られていたからで、メジャーバージョンアップで提供する際に require("http2") にする形に落ち着きました。

こういうことは将来的にも起き得る可能性があります。つまり、 Node.js コアモジュールとそれ以外との名前が将来的にかぶってしまうと面倒なことになります。 http2 のときはまだそこまで流行っていないライブラリでしたが、今後流行っているライブラリで起きた場合はエコシステムに影響が生まれます。

そういう事も考慮して node scheme を付けておくことで、 3rd party ライブラリとの差別化を最初から図れるメリットがあります。

細かいところ

これ以外にも細かい所としては、 ES Modules は本来 import 文にかけるのは URL を書く仕様になっています。この node: から始まることで URL valid な文字列をちゃんと記述することができるようになります。今の書き方は Node.js 独自の拡張です。(ただこの独自拡張も import maps などの新しい仕様により、仕様側が Node.js 独自拡張もカバーする形になるかもしれませんが)

さらに細かいところ

require 構文の中で node scheme を付けた場合、 require.cache で中身を差し替えるハック は使えなくなります。これを使って色々 Node.js のコアモジュールを差し替えているモジュールがあった場合、うまく動かなくなる可能性があります。 ES Modules の場合は require.cache を使っておらず、そもそも改変はできないようになっています。

// このように書いた所で、 node:http モジュールは変更できない。
require.cache[require.resolve('node:http')] = function() { console.log("http modified") };

まとめ

node scheme はつけていこう。