@nuxt/postcss8 使用時 "Cannot find module 0" エラーが発生する場合とその対処
現象
autoprefixer を v10 にバージョンアップする時、postcssをv8以上に更新する必要が生じた。
そこで、 postcss8の移行ガイド に従って以下を試みた。
- Nuxt.js を
>= 2.15.3に更新 - @nuxt/postcss8 を追加
その後、 nuxt generate によって静的ファイルを生成しようとしたところ、掲題のエラーによって失敗した。
╭───────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ ✖ Nuxt Fatal Error │
│ │
│ Error: Cannot find module '0' │
│ Require stack: │
│ - /Users/path/to/app/node_modules/@nuxt/core/dist/core.js │
│ │
╰───────────────────────────────────────────────────────────────────────────────────────────────╯
発生時の状況
package.json の依存関係定義
{
"dependencies": {
"@nuxtjs/axios": "^5.3.6",
"cross-env": "^7.0.0",
"nuxt": "2.15.7"
},
"devDependencies": {
"@nuxt/postcss8": "1.1.3",
"@nuxtjs/eslint-config": "^6.0.0",
"@vue/test-utils": "^1.0.0-beta.27",
"autoprefixer": "^10.0.0",
"babel-core": "7.0.0-bridge.0",
"babel-eslint": "^10.0.1",
"babel-jest": "^27.0.0",
"eslint": "^7.0.0",
"eslint-config-prettier": "^8.0.0",
"eslint-config-standard": ">=12.0.0",
"eslint-loader": "^4.0.0",
"eslint-plugin-import": ">=2.16.0",
"eslint-plugin-jest": ">=22.3.0",
"eslint-plugin-node": ">=8.0.1",
"eslint-plugin-nuxt": ">=0.4.2",
"eslint-plugin-prettier": "^3.0.1",
"eslint-plugin-promise": ">=4.0.1",
"eslint-plugin-standard": ">=4.0.0",
"eslint-plugin-vue": "^7.0.0",
"jest": "^27.0.0",
"nodemon": "^2.0.0",
"postcss": "^8.3.5",
"prettier": "^2.0.0",
"tailwindcss": "^2.0.0",
"vue-jest": "^3.0.3"
}
}
nuxt.config.js でのモジュール指定
{
buildModules: [
'@nuxt/postcss8'
]
}
'@nuxt/postcss8' をコメントアウトすると掲題のエラーは発生しなくなった。
そのため、 '@nuxt/postcss8' が原因と判断。
対処法
nuxt.config.js の build.postcss.plugins 指定を配列からオブジェクト形式に変更することで、掲題の現象は発生しなくなった。
before
import path from 'path'
// (略)
export default {
// (略)
build: {
postcss: {
plugins: [
require('tailwindcss')(
path.join(__dirname, 'tailwind.js')
),
require('autoprefixer')
]
}
}
}
after
import path from 'path'
// (略)
export default {
build:
postcss: {
plugins: {
tailwindcss: require('tailwindcss')(
path.join(__dirname, 'tailwind.js')
),
autoprefixer: require('autoprefixer')
}
}
}
}
原因
nuxt.config.js におけるPostCSS用プラグインの指定方法と、 @nuxt/postcss8 によるプラグインの加工が食い違っていたことによる。
@nuxt/postcss8 による config の書き換え
@nuxt/postcss8 は、適用時に build.postcss.plugins 指定を書き換えて、 プラグインの順番を並べ替えている [1]。
この時、 '@nuxt/postcss8' は defu を利用してオプションをマージしているが、 defu(baseObj, defaults) によって build.postcss.plugins が以下のように書き換わる。
{
build: {
postcss: {
plugins: {
'0': { /* プラグイン略 */ },
'1': { /* プラグイン略 */ },
'autoprefixer': {}
}
}
}
}
この時 postcss-loader は plugins の 各キーをプラグイン名と見て解釈する ので、 '0' という名前のプラグインを参照しようとするが、そのような名前のプラグインは存在しないのでエラーとなった。
defu によるマージ
今回のケースでは、 defu(baseObj, defaults) を実行した時のパラメータが baseObj: Array defaults: Object という組み合わせだったパターンに該当する。
defu は baseObj の各 key を走査して、 defaults[key] が 存在しない場合 は key と key に対応する値を defaults に挿入することでマージを行う。
この時、 baseObj: Array であるから走査によって得られる key は 0, 1, 2, ... といった添字になるため、 defaults に挿入される key もまた "0", "1", "2", ... といった内容になる。結果、上記のように数字が添字として入った build.postcss.plugins が発生する。
これを防ぐため、そもそも nuxt.config.js における build.postcss.plugins の指定を配列からオブジェクト形式に変更することで、問題が解消した。
懸念
postcss-loader の設定方法
postcss-loader を見ると、 plugins のキーをオブジェクトで指定する方法は deprecated, will be removed in the next major release であり、基本的には配列を用いることが推奨されている。従って、 @nuxt/postcss8 を用いるためにプラグイン指定をオブジェクトにした場合、今後のバージョンアップに際して問題となる可能性がある。
nuxt と postcss-loader のズレ?
他方、 nuxt では Arrayではなくオブジェクト形式での指定を推奨しようとしている?
postcss と nuxt で是とする方向が食い違っているように見える、しかも postcss は次期バージョンでオブジェクト方式を廃止しようとしている点が気がかり。
このI/F差分は、nuxt の plugin によって今後継続して埋めていくことになるのだろうか。
-
並び替えは
order(name)に従って行われる。このorder(name)はどうやら nuxtによる独自拡張の模様。 ↩︎
Discussion