【Docker】Rails 5 × Webpack 5で環境構築(失敗談)

前回のセクションでは無事簡単なSPAを作る事ができましたが、セクション3で作ったRails 5 環境での作成は頓挫してしまいました・・・

その失敗した所を説明する前に知識としてwebpackとwebpackerの説明を段階を追ってしたいと思います。環境構築サイトの記事に書いてあるコマンドだけを頼りにやってるとこの違いを理解しておらず、ハマってしまう事があります。(それが私です・・・)

Webpack と Webpacker の違いって何?

Webpack

先月(2020/10)出たWebpack 5 にも言及している数少ないサイトで説明されています。

最新版で学ぶwebpack 5入門 JavaScriptのモジュールバンドラ

前回記事でRailsサーバの中にVue.jsを構築する手順を紹介しましたがその役目を果たしています。

もちろんVueだけでなく、これ一つでTypeScriptもReactも入れられますね。

Webpacker

これはWebpackそのものでなく、Railsがバージョン5.1から対応を開始したWebpackやその周辺プラグイン等(Webpack CLI等)もセットにしたお得な便利ツールみたいなのものです。前回のセクションで説明したRails 5 と Rails 6 の違いはRails 6 はこのWebpackerが標準でインストールされます。Rails 6 では最初から入った状態で開発ができるため、その後のセットアップが非常に楽です。

Rails 6の変更点と新機能

実際、多くのRails + Vueを使用した記事の大半はWebpackerの使用を前提としたものが多いです。ただ一見便利そうなこのWebpackerにも欠点があります。

欠点を謳ったサイトは以下3つがあります。(かなりつまづいた所だったので調べまくった)

Steb by Step で剥がす Webpacker

Rails環境でJS , CSSをwebpackで完全に管理する

今日から簡単!Webpacker 完全脱出ガイド

この3つを読んでいるとある共通点が見えてきます。

  • Webpacker の仕様がブラックボックスのためパッと見、何が行われているかわからないので学習コストが増す
  • WebpackerがサポートしているWebpackのバージョンが決まっているため、Webpackやそれに付随するプラグインの最新バージョンを入れてしまうと何が起こるかわからない
  • 独自でWebpackを入れる場合、webpack.config.jsを作るのだがこの書き方はWebpackerと合っていないため、更にとまどう

以上でしょうか。例えば以下のファイルをご覧ください。このpackage.jsonは前回のセクションで作ったチュートリアルSPAのものです。

package.json

{
  "name": "app_name",
  "private": true,
  "dependencies": {
    "@rails/actioncable": "^6.0.0",
    "@rails/activestorage": "^6.0.0",
    "@rails/ujs": "^6.0.0",
    "@rails/webpacker": "4.3.0",
    "axios": "^0.21.0",
    "postcss-selector-parser": "^6.0.4",
    "turbolinks": "^5.2.0",
    "vue": "^2.6.12",
    "vue-loader": "^15.9.5",
    "vue-router": "^3.4.9",
    "vue-template-compiler": "^2.6.12"
  },
  "version": "0.1.0",
  "devDependencies": {
    "tailwindcss-multi-column": "^1.0.2",
    "webpack-dev-server": "^3.11.0"
  }
}

このファイルはプラグインやツール等を自分でインストールしたら表示されるものです。このリストを見ると@rails/webpacker 4.3.0はインストールされていますが、webpackの表示はありません。じゃあWebpackってインストールされていないの?って思うじゃないですか。

しかしこのDocker.logを見てください。

web_1  | Started GET "/" for 172.19.0.1 at 2020-11-15 07:21:43 +0000
web_1  | Cannot render console from 172.19.0.1! Allowed networks: 127.0.0.0/127.255.255.255, ::1
web_1  | Processing by HomeController#index as HTML
web_1  |   Rendering home/index.html.erb within layouts/application
web_1  | [Webpacker] Compiling...
web_1  | [Webpacker] Compiled all packs in /app_name/public/packs
web_1  | [Webpacker] Hash: 39ccc483ecafe99ee4d1
web_1  | Version: webpack 4.44.2
web_1  | Time: 36263ms
web_1  | Built at: 11/15/2020 7:22:38 AM
web_1  |                                      Asset       Size       Chunks                         Chunk Names
web_1  |     js/application-9afcbb5693aa87623e69.js    124 KiB  application  [emitted] [immutable]  application
web_1  | js/application-9afcbb5693aa87623e69.js.map    139 KiB  application  [emitted] [dev]        application
web_1  |       js/hello_vue-b66452b6321a8c702218.js    266 KiB    hello_vue  [emitted] [immutable]  hello_vue
web_1  |   js/hello_vue-b66452b6321a8c702218.js.map    312 KiB    hello_vue  [emitted] [dev]        hello_vue
web_1  |                              manifest.json  689 bytes               [emitted]
web_1  | Entrypoint application = js/application-9afcbb5693aa87623e69.js js/application-9afcbb5693aa87623e69.js.map
web_1  | Entrypoint hello_vue = js/hello_vue-b66452b6321a8c702218.js js/hello_vue-b66452b6321a8c702218.js.map
web_1  | [./app/javascript/app.vue] 1.14 KiB {hello_vue} [built]

これはWebpackerを入れた状態でHello_vueを表示させようとしたときに出てきたログです。

その中をよく見たらWebpackerのコンパイル終了後Version: webpack 4.44.2と表示されています。そうなんです。webpackは確かに入っているのですが、言ってしまえばWebpackerの一部になってしまっている。と言った方がいいかもしれません。(こういう所がブラックボックス)

今やWebpackのバージョンは5になっていますがWebpacker依存の開発をしているとこのバージョンの追従ができなくなってしまうし、周辺の新プラグインによって原因不明のエラー等の悪影響が出る可能性もあります。それって怖くありませんか?

この後はRails 5 でその関連で影響で出たエラーをご紹介しますね。

大量のエラーリスト

CLI for webpack must be installed.

api_1      | [Webpacker] Compiling...
api_1      | [Webpacker] Compilation failed:
api_1      | CLI for webpack must be installed.
api_1      |   webpack-cli (https://github.com/webpack/webpack-cli)
api_1      |
api_1      | We will use "yarn" to install the CLI via "yarn add -D".
api_1      | Do you want to install 'webpack-cli' (yes/no):
api_1      | Completed 500 Internal Server Error in 4709ms (ActiveRecord: 0.0ms)

webpack-cli未インストールエラーです。Rails 6 ならこれもセットでインストールされていますがRails5は標準でないので怒られてしまいます。ここからyarn add -D webpack-cliで入れるのですがここから迷走地獄がスタート

Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema.

このエラーが出続けます。今思えばCLIやwebpackのバージョン違いが原因だったのかなぁと思っていますが・・・

api_1      | [webpack-cli] Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema.
api_1      |  - configuration.node should be one of these:
api_1      |    false | object { __dirname?, __filename?, global? }
api_1      |    -> Include polyfills or mocks for various node stuff.
api_1      |    Details:
api_1      |     * configuration.node has an unknown property 'dgram'. These properties are valid:
api_1      |       object { __dirname?, __filename?, global? }
api_1      |       -> Options object for node compatibility features.
api_1      |     * configuration.node has an unknown property 'fs'. These properties are valid:
api_1      |       object { __dirname?, __filename?, global? }
api_1      |       -> Options object for node compatibility features.
api_1      |     * configuration.node has an unknown property 'net'. These properties are valid:
api_1      |       object { __dirname?, __filename?, global? }
api_1      |       -> Options object for node compatibility features.
api_1      |     * configuration.node has an unknown property 'tls'. These properties are valid:
api_1      |       object { __dirname?, __filename?, global? }
api_1      |       -> Options object for node compatibility features.
api_1      |     * configuration.node has an unknown property 'child_process'. These properties are valid:
api_1      |       object { __dirname?, __filename?, global? }
api_1      |       -> Options object for node compatibility features.
api_1      |
api_1      | Completed 500 Internal Server Error in 41643ms (ActiveRecord: 0.0ms)

どうやってもこのエラーが取れなかったので記事を漁りながらwebpack.config.jsを作ったり手動でwebpackをインストールしてWebpackerを使うのやめようという流れになってきます。(まだこの時はwebpackerは別のものと知らなかったが…)

しかしWebpackのコンパイルでもこれが出てしまったので最初はこのサイトを見て同じようにやったがとれず、

Modules “net” and “tls” not found when running webpack under sheets API directory

これも何日もの苦しみ仲間記事

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

自分も何時間もネットの海をさまよい、最終的に公式サイトに消し方が書いてありました。バージョンが変わって書き方が変わったのかしら?これでwebpack側のコンパイルは通過。

しかし、webpack.config.js はWebpackerは干渉せず、上記のエラーがとれなかったのでWebpackerを辞める決意します。そのうちに上記であげた3つの欠点を示しているサイトに行きつきます。

2021/07/04追記

この記事を作っていてネットの海をさまよっていた頃、stackoverflowにて上記エラーで同じように迷っていたっぽい外国人ユーザがいらっしゃったのですが、この時解決していたのでtadanoneasという名で回答してあげたら、更に別のもっとベテランっぽい方がコメントの検証をしてくれたらしく、対応の裏付けがとれました。

https://stackoverflow.com/questions/64742478/rails-6-how-to-resolve-actionviewtemplateerror-webpacker-cant-find-applica

TypeError: The ‘compilation’ argument must be an instance of Compilation

これも新しいバージョンで発生するコンパイルエラー?どこを見て参照したか忘れたが開発モードをつけてWebpackをコンパイルしたらエラーとれたっぽい。

yarn run v1.22.5
$ /myapp/node_modules/.bin/webpack --mode development
[webpack-cli] Compilation finished
asset index.js 2.23 KiB [emitted] (name: main)
./src/index.js 1 bytes [built] [code generated]
webpack 5.4.0 compiled successfully in 14492 ms
Done in 30.41s.
root@0c13cd32dabc:/myapp# yarn bulid
yarn run v1.22.5
error Command "bulid" not found.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
root@0c13cd32dabc:/myapp# yarn build
yarn run v1.22.5
$ webpack
[webpack-cli] Compilation finished
asset index.js 270 bytes [emitted] [minimized] (name: main)
./src/index.js 1 bytes [built] [code generated]
webpack 5.4.0 compiled successfully in 15517 ms
Done in 32.86s.
root@0c13cd32dabc:/myapp#

    <%= javascript_pack_tag 'hello_vue' %>
    <%= stylesheet_pack_tag 'hello_vue' %>

Processing by HelloController#index as HTML
api_1      |   Rendering hello/index.html.erb within layouts/application
api_1      |   Rendered hello/index.html.erb within layouts/application (5.2ms)
api_1      | Completed 200 OK in 150ms (Views: 150.0ms | ActiveRecord: 0.0ms)

ただ、この時200OKは返ってきたのに画面何も映らなかった。またもネットの海をさまよい、Webpackerを頼らない場合、手動でwebpack-manifest-pluginを入れてハッシュ化する必要があるらしい。 やってみると。

TypeError: Cannot read property ‘length’ of undefined

うわぁぁぁぁぁぁぁぁぁぁぁぁぁぁぁ(発狂)

[webpack-cli] TypeError: Cannot read property 'length' of undefined
    at /myapp/node_modules/webpack-manifest-plugin/lib/plugin.js:129:39
    at Array.reduce (<anonymous>)
    at ManifestPlugin.<anonymous> (/myapp/node_modules/webpack-manifest-plugin/lib/plugin.js:116:26)
    at Hook.eval [as callAsync] (eval at create (/myapp/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:14:1)
    at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (/myapp/node_modules/tapable/lib/Hook.js:18:14)
    at Compiler.emitAssets (/myapp/node_modules/webpack/lib/Compiler.js:763:19)
    at process.nextTick (/myapp/node_modules/webpack/lib/Watching.js:80:21)
    at process._tickCallback (internal/process/next_tick.js:61:11)
e

3% setup watch run webpack-cli[webpack-cli] Compilation starting...
[webpack-cli] ReferenceError: file is not defined
    at /myapp/webpack-manifest-plugin.js:131:7
    at Array.reduce (<anonymous>)
    at ManifestPlugin.<anonymous> (/myapp/webpack-manifest-plugin.js:117:26)
    at Hook.eval [as callAsync] (eval at create (/myapp/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:14:1)
    at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (/myapp/node_modules/tapable/lib/Hook.js:18:14)
    at Compiler.emitAssets (/myapp/node_modules/webpack/lib/Compiler.js:763:19)
    at process.nextTick (/myapp/node_modules/webpack/lib/Watching.js:80:21)
    at process._tickCallback (internal/process/next_tick.js:61:11)
error Command failed with exit code 2.

今度は早速webpack-manifest-pluginでエラーか。。これもやはりWebpackが新しい影響っぽい。そしてまたネットの海をさまよい・・・以下の記事で解決。

webpack @ 5 #186をサポート

その他、ここにはWebpack最新版を入れることによって出た影響が記載されています。

Webpack v5LINプラン

やっぱり何も表示されなかった。

結局上の記事通り、やったらちゃんとハッシュファイルは作られた。そしてビューヘルパーの実装に伴ってハッシュファイルが探索され、200OKが最終的に返ってきたのにやっぱりHello Vue!すら映らなかったので最終的に断念してしまいました・・・。

今回ダメだったpackage.jsonwebpack.config.jsの中身です。色々入れすぎてカオスになっており、もう色々悪干渉しすぎて多分どうしようもできないです・・・・

package.json

{
  "name": "SampleWebpack",
  "version": "1.0.0",
  "author": "neas",
  "license": "MIT",
  "private": true,
  "scripts": {
    "build": "webpack --watch --progress --mode=development --config webpack.config.js",
    "watch": "webpack --watch",
    "dev": "webpack serve --config webpack.config.js"
  },
  "devDependencies": {
    "@babel/core": "^7.12.3",
    "@babel/preset-env": "^7.12.1",
    "babel-core": "^6.26.3",
    "babel-loader": "^8.2.1",
    "css-loader": "^5.0.1",
    "file-loader": "^6.2.0",
    "mini-css-extract-plugin": "^1.3.1",
    "node-sass": "^5.0.0",
    "npx": "^10.2.2",
    "optimize-css-assets-webpack-plugin": "^5.0.4",
    "sass-loader": "^10.1.0",
    "terser-webpack-plugin": "^5.0.3",
    "vue-loader": "^15.9.5",
    "vue-template-compiler": "^2.6.12",
    "webpack": "^5.4.0",
    "webpack-cli": "^4.2.0",
    "webpack-dev-server": "^3.11.0",
    "webpack-manifest-plugin": "^2.2.0",
    "webpack-node-externals": "^2.5.2"
  },
  "dependencies": {
    "vue": "^2.6.12"
  },
  "babel": {
    "presets": [
      "@babel/preset-env"
    ]
  },
  "browserslist": [
    "defaults"
  ]
}
webpack.config.js

const VueLoaderPlugin = require('vue-loader/lib/plugin');
var nodeExternals = require('webpack-node-externals');
const ManifestPlugin = require('./webpack-manifest-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const path = require('path');

module.exports = {
	context: path.resolve(__dirname, 'app/javascript/packs'),
	entry:
	{
		application: './application.js',
		'application-stylesheet': './application.css',
		hello_vue: './hello_vue.js',
	},
	output: {
		path: path.resolve(__dirname, 'public/packs',),
		filename: '[name]-[hash].js'
	},
	target: 'node',
	mode: 'production',
	module: {
	 rules: [
	  {
	     test: /\.(css|sass)$/,
	     use: ['vue-style-loader', MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
	  },
          {
	     test: /\.vue$/,
             exclude: /node_modules/,
	     loader: "vue-loader",
	     options:{
		extractCSS: true,
	     },
	  },
	  {
	     test: /\.js$/,
	     loader: 'babel-loader',
	     options: {
	        presets: [
	           '@babel/preset-env',
	        ],
	     },
	  },
          {
		  test: /\.node$/,
		  loader: "node-loader"
	  },
	  {
		test: /\.(png|jpg|gif)$/,
		use: [
		   {
			loader: 'file-loader',
			options: {
				outputPath: 'images/',
				name: '[name]-[hash].[ext]',
				 },
		   },
		],
	   },
	 ],
    },
    externals: [nodeExternals()],
    plugins: [
 	new VueLoaderPlugin(),
	    new ManifestPlugin({
		    writeToFileEmit: true
	    }),
	    new MiniCssExtractPlugin({filename: '[name]-[hash].css'}),
    ],
};

まとめ

  • Rails 5 はバージョンの新しいWebpackをサポートしていないのでWebpackerの運用をオススメしない。
  • Rails 6 ならWebpackerの運用で楽っちゃあ楽だがこれもサポートしているバージョンには制限があるので標準でインストールされてしまうとはいえ、webpack単体に切り替えることを検討した方がよい
  • 10/10に出たwebpack バージョン 5 はWebpackerもそうだがwebpack-manifest-pluginなどのプラグインも正式にサポートできていない。正直、上記別ファイル作る対策も無理やり感がハンパないため、導入するには注意が必要。もう少し経ってからインストールするのが良いかも。

~12/3追記~

お役に立てるかわかりませんが環境構築失敗バージョンでもよければgitHubにあげました~

vuejsフォルダは未使用です。

https://github.com/tadanoneas/test/tree/missmaster