はじめに
React学習中に出会った以下エラーについて、原因と解消方法の共有です。
Module not found: Can't resolve 'fs'
目次
🧷 原因と解消方法
原因
上記エラーは、通常、Node.jsのファイルシステムモジュールである fs
を、ブラウザ環境などNode.jsのAPIが利用できない環境で使用しようとした場合に発生するそうだ。
Next.jsはサーバーサイドとクライアントサイドの両方で動作するため、コードの実行環境によって利用できるAPIが異なる。
そのため対策としては、
👉 クライアントサイドの JS コンテキストでfs
モジュールが参照されないようにする必要があるとのこと。
解消方法
next.config.mjs
へ以下(ここから〜ここまでの部分)を追加
import { withSentryConfig } from '@sentry/nextjs';
/** @type {import('next').NextConfig} */
const nextConfig = {
/** ここから */
reactStrictMode: true,
swcMinify: true,
webpack: (config, { webpack }) => {
config.experiments = { ...config.experiments, topLevelAwait: true };
config.externals['node:fs'] = 'commonjs node:fs';
config.resolve.fallback = {
...config.resolve.fallback,
fs: false,
'stream/web': false,
async_hooks: false,
net: false,
tls: false,
'util/types': false,
worker_threads: false,
perf_hooks: false,
console: false,
diagnostics_channel: false,
};
config.plugins.push(
new webpack.NormalModuleReplacementPlugin(
/^node:(crypto|events|stream|util)/,
(resource) => {
resource.request = resource.request.replace(/^node:/, '');
}
)
);
return config;
},
/** ここまで */
};
config.externals["node:fs"] = "commonjs node:fs";
この設定は、node:fs
モジュールをWebpackのバンドルの外部にあるものとして扱う。
これにより、クライアントサイドのJavaScriptバンドルに fs
モジュールが含まれなくなり、本エラーの発生を防ぐことができる。
config.resolve.fallback = { ...config.resolve.fallback, fs: false, };
resolve.fallback
は、Webpackがモジュールを解決できなかった場合に試みる代替パスを指定する設定。
ここで fs: false
と設定することで、Webpackが fs
モジュールを見つけられなかった場合に、フォールバックを試みることなく、そのモジュールが存在しないものとして扱う。
これは、クライアントサイドのコードで誤って fs
を参照しようとした場合に、ビルドエラーを抑制する効果があると考えられる。
config.plugins.push(new webpack.NormalModuleReplacementPlugin(/^node:/, (resource) => { resource.request = resource.request.replace(/^node:/, ''); }, ));
この設定は、node: プレフィックスで始まるモジュールIDを、プレフィックスなしの同じIDに置き換えるWebpackプラグインを追加している。
Node.jsの新しいAPIは node: スキームでインポートすることが推奨されていますが、一部の環境やライブラリではまだこの形式に対応していない場合がある。
この設定により、例えば require('node:fs')
という記述が require('fs')
として扱われるようになり、互換性の問題を解消する可能性がある。