megutech

自身の備忘録として主にWEBサーバー周りの技術について投稿しています。

Can't resolve 'fs'

browser向けjsの環境構築中、環境によって変えたい値を.envで管理したいという事でdotenv-webpackをインストールしたのだが、bundle作成時にエラーが出てつまったので共有をしておく。

環境

Package Ver
webpack 4.41.0
webpack-cli 3.3.9
webpack-dev-server 3.8.1
dotenv-webpack 1.7.0

経緯

他のプロジェクトでもdotenv-webpackをブラウザ向けに使っていたこともあり、特に何も考えずにdotenv-webpackをインストールしてビルドした。

$ npm install dotenv-webpack

webpack.config.js

const path = require('path');
const Dotenv = require('dotenv-webpack');

module.exports = {
  // ...
  plugins: [
    new Dotenv({ systemvars: true })
  ],
  // ...
};

package.json

{
  // ...
  "script": {
    // ...
    "dev": "webpack-dev-server",
    // ...
  },
  // ...
}
$ npm run dev

すると以下のエラーが発生した。

ERROR in ./node_modules/dotenv/lib/main.js
Module not found: Error: Can't resolve 'fs' in '/xxx/xxx/node_modules/dotenv/lib'
 @ ./node_modules/dotenv/lib/main.js 24:11-24
 @ ./src/api/config.ts
 @ ./src/api/encode.ts
 @ ./src/api/index.ts
 @ ./src/index.ts

よくある間違った解法

Can't resolve 'fs'などでググるwebpack.config.jsのtargetをnodeに変更しましょうという記事が出てくるが、これはサーバーサイドの時の話である。

ブラウザ向けの場合はこの設定をしてしまうと動かない。ではどうすればいいかというと、fsモジュールを使わないようにするしかない。モジュール内で使われているのであれば代替モジュールを検討するほかなくなる。

しかしそれは非常に困る。だって他のプロジェクトではdotenv-webpackちゃんと動いてるし。

解決

css-loaderのissueに同じような問題が上がっていたので、それを参考にwebpack.config.jsnode: { fs: 'empty'}を追加した。

webpack.config.js

const path = require('path');
const Dotenv = require('dotenv-webpack');

module.exports = {
  // ...
  plugins: [
    new Dotenv({ systemvars: true })
  ],
  node: {
    fs: 'empty'
  },
  // ...
};

詳細は公式ドキュメントに記載されているが、要約するとNode用モジュールをポリフィルもしくはモックして、ブラウザーでも実行できるようにする!という事らしい。

そしてemptyは空のオブジェクトを提供するという意味なので、dotenv内のfsは何もしない空モジュールという事になる。

ブラウザ用ではfsなんて使わないので、これで問題無。無事解決。良かった良かった。