server-only パッケージをインポートしたやつをどうにかして動かす
ReactやNext.jsにおいて、server-only
というパッケージをimportすることで、該当のパッケージをインポートした処理はServer側の処理からのみインポート可能になります。つまり use client
などをつけたコンポーネントからは利用できません。逆にclient-only
というのもありますね。詳細はNext.jsの記事などが参照になります。
例えばなのですが運用で使うスクリプトを実行する時に、server-only
パッケージをインポートしているライブラリが依存に入ってしまっていると、下記のエラーが発生してしまいます。これをどうにかしようという話です。
Error: This module cannot be imported from a Client Component module. It should only be used from a Server Component.
結論からするとnode
に--conditions=react-server
というオプションを付与して実行することで解決します。node
以外で実行する場合、例えばtsx
等の場合はNODE_OPTIONS='--conditions=react-server'
などで代替可能です。
深堀り
server-onlyの仕組みは下記の記事が詳しいです。
https://quramy.medium.com/server-component-と-client-component-で依存モジュールを切り替える-7d65c8b2074f
重要なのはnodeのsubpath importsという機能です。その中でもConditional exportsという機能が重要です。
server-only
の package.json
を見ると下記のような定義がされています。server-only
自体はGitHubで見つからないので、node_modulesの中から引っ張ってきています。
{
// ...
"exports": {
".": {
"react-server": "./empty.js",
"default": "./index.js"
}
}
}
default
というのは条件に引っかからなかった時のデフォルトの挙動です。つまりreact-server
という何らかしらの条件に引っかかる場合はempty.js
が、そうではない場合はdefault
が呼ばれます。
ちなみにempty.js
は文字通り空っぽで、index.js
はエラーを吐くだけのファイルです。
throw new Error(
"This module cannot be imported from a Client Component module. " +
"It should only be used from a Server Component."
);
上記を見るに、react-server
という条件であれば問題なくインポートできるようです。この条件を指定するのが--conditions
オプションです。node --conditions=react-server
のようなコマンドを打つことで、server-only
の挙動を変更でき、動かすことが可能です。