【Vue】npm run buildでModule not found: Can't resolve 'child_process'【Webpack】

280, 2020-01-25

目次

エラー発生

VueのプロジェクトをWebpackを使ってビルドしようとした。具体的にはwebpack.config.jsのあるディレクトリでnpm run buildを実行した。npm run buildpackage.jsonで↓のように定義されている。

{
    ...
    "scripts": {
        "build": "webpack"
    }
    ... 
}

このnpm run buildを実行したところ↓のようなエラーが飛んできた。

...
Module not found: Can't resolve 'child_process'
...
Module not found: Error: Can't resolve 'fs' 
...
Module not found: Error: Can't resolve 'module'
...

child_processfsが見つからないというエラーだが、npm installで該当のモジュールをインストールしても進展しなかった。

試行錯誤

ググってみると↓のログを見つける

このログにはpackage.jsonに↓を加えろという書き込みがあった。

"browser":{
    "child_process": false
}

しかし加えてみてもエラーが発生した。
さらに私はネットの海をさまようことになる。

解決かと思いきや……

ググっていると↓の記事を見つける。

この記事に従い、webpack.config.jsに↓の設定を加えた。

target: 'node'

するとエラーが出なくなった。
記事によると、node.jsを使っている場合は↑の設定が必要らしい。

しかし中々わかりづらいエラーだ。npm installで沈黙すべきエラー内容だったが、最終的にtarget: 'node'を追記することで沈黙した。エラー解決は奥が深いと思った(ダンジョン的な意味で)。

またエラー

しかしtarget: 'node'に設定すると、ビルドしたバンドルをブラウザで実行したときにコンソールに↓のようなエラーが出た。

Uncaught ReferenceError: require is not defined

requireが使えないというエラーだ。バンドルをブラウザで実行するときはtarget: 'web'に設定する必要があるらしい。今回はtarget: 'node'に設定しているのでこのようなエラーが出るということになる。

戦争です

ネットの海をさまよう

↓の記事を見つける。

この記事によるとfs: ‘empty’target: ‘node’を設定する方法はどちらも間違っているとのこと。
ブラウザで実行するはずのスクリプト内でnode.jsのモジュール、つまりfsを使ってしまっているので、target: 'web'でビルドしたときにfsモジュールをインポートできなくてエラーになるという感じらしい。
これを解決するにはfsモジュールを使っている所を変更しなければいけないとのこと。
ちなみにwebpackはデフォルトではtarget: 'web'でビルドを実行するらしい。

詰んでるー!

↓の記事を見つける。

この記事ではwebpack.config.jsnode: { fs: 'empty' }を設定してこのエラーを回避していた。
先ほどの記事ではこの設定も間違いということだが実験してみる価値はありそうだ。
webpack.config.jsに↓の設定を加える。こうすることで各モジュールはモックになり、空の動作をするらしい。理屈ではブラウザで実行するときはnode.jsのモジュールは使わないので、この設定にしても問題ないという理屈だ。

  node: {
    fs: 'empty',
    child_process: 'empty',
    module: 'empty',
  }

バンドルをビルドしてブラウザで実行してみると↓のようなエラーが出た。

LoaderRunner.js:6 Uncaught TypeError: Cannot read property 'bind' of undefined

これは↓のコードが動かないというエラーだ。

var readFile = fs.readFile.bind(fs);

fs.readFileundefinedなので、それに続くbindを呼び出せない。
fsfs: 'empty'でモックにしているので使うことは出来るが、readFileなどのメンバ変数はモックしてくれないらしい。そのため、このような参照エラーになる。

ということでfs: 'empty'は問題が出るらしい。

疑問

疑問なのは、なぜライブラリがnode.js側のモジュールを使おうとしているのかというところだ。
これはつまり、ライブラリ自体がnode.jsのモジュールということになるのではないか?

さらに疑問は尽きない。今回のプロジェクトは別の既存プロジェクトのコピーで、ソースコードは丸々コピーになっている。
そのためnpm installしてビルドすれば成功するはずだったのだ。それなのにこのようなエラーが出ている。これは不思議である。

念のためnpmでインストールされているパッケージをすべてアップデートしたが結果は変わらなかった。

さらにネットの海をさまよう

やはりネットには大きく分けて二つの解決方法があるらしい。fs: 'empty'target: 'node'だ。しかし、これら二つの方法では今回は解決しない。
fs: 'empty'は類似でexternal: { fs }を追記する方法もあったが、これもエラーになった。
私の背は丸くなり、キーボードを打つ手は震え、ぶつぶつと独り言を唱えるようになっていた。目はかすみ、ここがどこかもわからず、ネットの海をさまよう。次第に私はネットの海を信じなくなっていた。この広大な海のどこにも答えなどないのだ。それなのに人は海の中に答えを求める。私もその一人であった……。

解決

エラー表示に着目すると、くだんのエラーが飛んできてる所はどこも同じファイルからだった。
私のプロダクトの場合だとhttp.jsというファイルからエラーが発生している。

ERROR in ./node_modules/y18n/index.js
Module not found: Error: Can't resolve 'fs' in '/aaa/src/npm/node_modules/y18n'
 @ ./node_modules/y18n/index.js 1:9-22
 @ ./node_modules/cacache/lib/util/y.js
 @ ./node_modules/cacache/locales/en.js
 @ ./node_modules/cacache/index.js
 @ ./node_modules/terser-webpack-plugin/dist/TaskRunner.js
 @ ./node_modules/terser-webpack-plugin/dist/index.js
 @ ./node_modules/terser-webpack-plugin/dist/cjs.js
 @ (webpack)/lib/WebpackOptionsDefaulter.js
 @ (webpack)/lib/webpack.js
 @ ./src/core/http.js
 @ ./src/aaa/main.js

これはfschild_processmoduleも同じだ。そこでhttp.jsの中身を見てみることにした。
↓がhttp.jsの中身だ。

import axios from 'axios'
import { AJAX_TIMEOUT_MS } from '@/aaa/settings.js'
import { WebpackOptionsApply } from 'webpack';                                                                          

let http = axios.create({
  baseURL: '/api/v1/',
  timeout: AJAX_TIMEOUT_MS, // ms
});

http.defaults.xsrfHeaderName = 'X-CSRFToken';
http.defaults.xsrfCookieName = 'csrftoken';

http.interceptors.response.use(response => {
  return response
}, error => {
  return Promise.reject(error)
})

export default http;

先ほどのエラーではhttp.jsのさらにwebpack.jsからエラーが飛んできてる。
つまり↓の行からだ。

import { WebpackOptionsApply } from 'webpack'; 

はて。なんだこのコードは。ひょっとしてwebpacknode.js用のモジュールだから、ブラウザ上でインポートしてここからエラーが発生しているのでは?
このコードは死んでいて、どこにも使われていないコードだ。私はこの行を消した。

するとnpm run buildが成功し、ブラウザでもプロダクトが表示された。
不要なnode.js用のモジュールのインポートでエラーが発生していたということか……。なるほどわかりづらい。

だが解決できた。よかったよかった。

おめでとう!

スポンサーリンク

スポンサーリンク

スポンサーリンク

スポンサーリンク