背景
webpackerの開発が終了しており、後継であるshakapackerへの移行をする必要があります。
しかしながら、webpackerからshakapackerへの移行方法については、ネット記事が多数存在しますが、個別具体性が高く効率的に移行を進めることは困難でした。
そこで、本記事ではwebpackerからshakapackerへの移行に関する私の記録を残します。
本記事も個別具体性が高いかもしれませんが、これからwebpackerからshakapackerへの移行を試みる方々の参考になれば嬉しいです。
バージョン
Ruby:3.2.4
Rails:6.1.7.7
bundler:2.5.3
node:22.11.0
npm:10.9.0
yarn:4.0.0
webpack:4.46.0
※shakapackerは、yarn3系もしくは4系と互換性があります。shakapackerのインストール前に、yarnのバージョンを更新しておきましょう。
※webpacker利用者の中にはnode18系を利用している方も多いかと思います。shakapackerへの移行を機に、node22系に更新しましょう。
開発環境
macOS: Sonoma14.7.1
エディター: Cursor@VSCode
webpackerからshakapackerへの移行手順
1. shakapackerのインストール
公式ドキュメントを参考にして、shakapackerをインストールします。
bundle add shakapacker --strict
./bin/bundle install
./bin/rails shakapacker:install
2. webpackerの削除
以下のコードにより、webpackerを削除します。
bundle remove webpacker
yarn remove @rails/webpacker
必要なBabelをインストールし、webpack-dev-serverを削除します。
yarn add @babel/core @babel/plugin-transform-runtime @babel/preset-env @babel/runtime babel-loader compression-webpack-plugin terser-webpack-plugin webpack webpack-assets-manifest webpack-cli webpack-merge
yarn add -D webpack-dev-server
3. webpack.config.jsの修正
webpack.config.jsは2種類存在します。
①プロジェクトファイル直下のwebpack.config.js
②app>config>webpack直下のwebpack.config.js
※webpack.config.jsが存在しない場合は、新規でファイルを作成してください。
プロジェクト直下のwebpack.config.js(①)は通常、shakapackerとは無関係で、独立したwebpackプロジェクト用に使用されることが多いです。
shakapackerは、webpackの設定ファイルとしてconfig/webpack/webpack.config.js(②)を使用します。
参考までに、プロジェクト直下のwebpack.config.js(①)とconfig/webpack/webpack.config.js(②)のコードは以下のとおりです。
# プロジェクト直下のwebpack.config.js(①)
const { env, webpackConfig, merge } = require('shakapacker') // shakapackerからmergeをインポート
const { existsSync } = require('fs')
const { resolve } = require('path')
const webpack = require('webpack') // Webpackをインポート
// CSS関連の設定
const cssConfig = {
resolve: {
extensions: ['.css', '.scss', '.sass'], // CSSやSASSファイルの拡張子を解決
},
}
// 環境ごとの設定を読み込む関数
const envSpecificConfig = () => {
const path = resolve(__dirname, `${env.nodeEnv}.js`)
if (existsSync(path)) {
console.log(`Loading ENV specific webpack configuration file ${path}`)
return require(path)
} else {
console.log(`WARNING: Using default webpack configuration. Did not find a Env specific file at path ${path}`)
return {}
}
}
// 共通設定(以前のenvironment.jsから移行)
const commonConfig = {
plugins: [
new webpack.ProvidePlugin({
$: 'jquery/src/jquery',
jQuery: 'jquery/src/jquery',
}),
],
}
// 環境ごとの設定、共通設定、CSS設定をマージ
module.exports = merge(webpackConfig, commonConfig, cssConfig, envSpecificConfig())
# app>config>webpack直下のwebpack.config.js(②)
// See the shakacode/shakapacker README and docs directory for advice on customizing your webpackConfig.
const { generateWebpackConfig } = require('shakapacker');
const { merge } = require('webpack-merge'); // webpack設定をマージするためのライブラリ
const webpack = require('webpack');
const path = require('path');
// 基本的なWebpack設定を生成
const webpackConfig = generateWebpackConfig();
// jQueryをグローバルスコープに提供するための設定
const customConfig = {
plugins: [
new webpack.ProvidePlugin({
$: 'jquery', // `$`としてjQueryを使用可能に
jQuery: 'jquery', // `jQuery`として使用可能に
jquery: 'jquery', // `jquery`としても使用可能に
Popper: ['popper.js', 'default'], // 必要に応じてPopper.jsも追加
}),
],
};
// エントリポイントを追加(既存コード)
webpackConfig.entry = {
application: path.resolve(__dirname, '../../app/javascript/application.js'),
maps_autocomplete: path.resolve(__dirname, '../../app/javascript/maps_autocomplete.js'),
};
// Shakapackerの基本設定とカスタム設定をマージしてエクスポート
module.exports = merge(webpackConfig, customConfig);
補足① プロジェクト直下のwebpack.config.js(①)について
以下のコードが、environment.jsの役割を担っています。したがって、environment.jsファイルを削除します。
// 環境ごとの設定を読み込む関数
const envSpecificConfig = () => {
const path = resolve(__dirname, `${env.nodeEnv}.js`)
if (existsSync(path)) {
console.log(`Loading ENV specific webpack configuration file ${path}`)
return require(path)
} else {
console.log(`WARNING: Using default webpack configuration. Did not find a Env specific file at path ${path}`)
return {}
}
}
補足② app>config>webpack直下のwebpack.config.js(②)について
アプリのjsファイルではjQueryを使用しています。そのため、以下のコードのように「jQueryをグローバルスコープに提供するための設定」をコーディングしています。
const customConfig = {
plugins: [
new webpack.ProvidePlugin({
$: 'jquery', // `$`としてjQueryを使用可能に
jQuery: 'jquery', // `jQuery`として使用可能に
jquery: 'jquery', // `jquery`としても使用可能に
Popper: ['popper.js', 'default'], // 必要に応じてPopper.jsも追加
}),
],
};
jsファイルは「app>javascript>application.js」の他にも、「app>javascript>maps_autocomplete.js」が存在します。maps_autocomplete.jsは個人的にカスタマイズしているファイルです。maps_autocomplete.jsをapplication.jsに統合するとシンプルになりますが、maps_autocomplete.js内のコード(主にGoogleMap表示関連のコード)は別管理したいので、以下のコードのようにエントリポイントを設定しています。
// エントリポイントを追加(既存コード)
webpackConfig.entry = {
application: path.resolve(__dirname, '../../app/javascript/application.js'),
maps_autocomplete: path.resolve(__dirname, '../../app/javascript/maps_autocomplete.js'),
};
4. development.js, test.js, production.jsの修正
development.js, test.js, production.jsのコードは、以下のとおりです。必要に応じてカスタマイズしてください。
const { webpackConfig } = require('shakapacker')
// 必要に応じてカスタマイズを追加
// 例: webpackConfig.devtool = 'cheap-module-source-map'
module.exports = webpackConfig
5. webpacker.ymlからshakapacker.ymlを作成する。
config>webpacker.ymlをもとに、
config>shakapacker.ymlを作成します。
私の場合はPerplexity(chatGPTでもたぶん大丈夫です)に『現在の「webpacker.yml」のコードは以下のとおりです。このコードをshakapacker.ymlのコードに修正してください。』と依頼、shakapacker.ymlのコードを生成しました。
# config>shakapacker.yml
default: &default
source_path: app/javascript
source_entry_path: /
public_root_path: public
public_output_path: packs
cache_path: tmp/cache/shakapacker
webpack_compile_output: true
nested_entries: false
# Additional paths webpack should lookup modules
# ['app/assets', 'engine/foo/app/assets']
additional_paths: []
# Reload manifest.json on all requests so we reload latest compiled packs
cache_manifest: false
# Extract and emit a css file
extract_css: false
static_assets_extensions:
- .jpg
- .jpeg
- .png
- .gif
- .tiff
- .ico
- .svg
- .eot
- .otf
- .ttf
- .woff
- .woff2
extensions:
- .jsx
- .mjs
- .js
- .sass
- .scss
- .css
- .module.sass
- .module.scss
- .module.css
- .png
- .svg
- .gif
- .jpeg
- .jpg
development:
<<: *default
compile: true
# Reference: https://webpack.js.org/configuration/dev-server/
dev_server:
https: false
host: localhost
port: 3035
public: localhost:3035
hmr: false
# Inline should be set to true if using HMR
inline: true
overlay: true
compress: true
disable_host_check: true
use_local_ip: false
quiet: false
pretty: false
headers:
'Access-Control-Allow-Origin': '*'
watch_options:
ignored: '**/node_modules/**'
test:
<<: *default
compile: true
# Compile test packs to a separate directory for testing purposes.
public_output_path: packs-test
production:
<<: *default
# Production depends on precompilation of packs prior to booting for performance.
compile: false
# Extract and emit a CSS file for production builds.
extract_css: true
# Cache the manifest.json for performance in production.
cache_manifest: true
なお「nested_entries: false」は必ず設定してください。app>javascript直下のファイルのみを対象とするためです。「nested_entries: false」を設定しないと、app>javascript配下のサブディレクトリも全て対象となってしまいます。
shakapacker.ymlでは「source_entry_path: /」を設定します。このタイミングで、app>javascript>packs配下のファイルを、app>javascript配下に移動します。
6. package.jsonの修正
package.jsonに以下のコードを書いてください。
"browserslist": [
"defaults"
]
"babel": {
"presets": [
"./node_modules/shakapacker/package/babel/preset.js"
]
}
「"./node_modules/shakapacker/package/babel/preset.js"」が重複で書かれている場合がありますが、重複は避けましょう。
次いで、プロジェクト直下の「.browserslist」を削除しておきます。
7. cssおよびsassの設定
以下のコードを実行します。
yarn add css-loader style-loader mini-css-extract-plugin css-minimizer-webpack-plugin sass sass-loader
8. コンパイルの実行
以下のコードを実行します。
bin/rails assets:clobber assets:precompile
9. 古いbinstubの削除
以下のコードで、古いbintubをgitの対象外とします。
git rm bin/webpack bin/webpack-dev-server
10. webpackerのヘルパーの修正
「javascript_pack_tag」と「stylesheet_pack_tag」は、各ファイルに一度しか使えません。基本形は、公式ドキュメントのとおりです。
<%= javascript_pack_tag 'application' %>
<%= stylesheet_pack_tag 'application' %>
私の場合は、「application.html.erb」で「application.js」と「maps_autocomplete.js」の2つのファイルを読み取る必要がありました。
コーディングは、以下のとおりです。
<%= javascript_pack_tag 'application', 'maps_autocomplete', type: 'module', 'data-turbolinks-track': 'reload' %>
「'data-turbolinks-track': 'reload'」が必要かは不明ですが、念のため、webpacker使用時のコードを残しています。
※画像パスも「asset_pack_path('media/images/hoge')」から「asset_pack_path('static/hoge')」に修正する必要があるかもしれませんので、ご注意ください。
11. サーバーの起動
最後に「bin/rails s」で、サーバーを立ち上げてみてください。
まとめ
webpackerからshakapackerへの移行方法を紹介させていただきました。
コンパイル時にエラーが発生したりして、やってみると結構大変です。
エラーに関しては、Perplexityを多用すると解消できます。しかしながら、細かいことまで自分で理解していかないと解消しきれないエラーも出ますので、ゆっくりでもいいので確実に移行を進めてみましょう!