Webpacker 3.x -> 5.x-> jsbundling-rails

# はじめに

※この文章はベータ版です。

既存のプロジェクトで Rails5.2 + Webpacker 3.5.5 ->
Rails6.1 + Webpacker5.2.1 -> Rails6.1 + jsbundling-rails
(-> Rails7.0 + jsbundling-rails)したので、手順etc を
メモしておきます。

::: note info
Rais6 -> Rails 7 する際には、Webpacker -> jsbundling-rails、
turbo-links -> turbo など、Rails7 からの正式採用なものの
Rails6 でも動作する gem への移行、Rails6 では動作するものの
Rails7以降への対応が怪しい gem の Rails7対応済/対応中の
gemへの移行、(例：Refile -> Shrine)をインクリメンタルに
行なってから、Rails6 -> Rails7 することをオススメします。
:::

# Rails5.2 -> Rails6.1 

## プロジェクトの rails & Webpacker gem を更新する

プロジェクトのルート・パスに移動後、gems.rb(Gemfile) を編集し、
バンドルされた gem を更新します。

```
% vim gems.rb
gem 'rails', '~> 6.1.0'
gem 'webpacker', '~> 5.2.1'
% bundle update
```

表示されるメッセージで gem の依存関係を確認し、依存関係に問題が
なくなるように gems.rb(Gemfile) を編集し、`bundle update` を
実行します。この作業をエラーが出なくなるまで繰り返します。

## アップデートタスクを実行する

```
% bundle exec rails app:update
```

プロジェクトをGit etcでリポジトリでバージョン管理していない場合には
バックアップをとってから実行します。

実行により、いくつかのファイルのコンフリクトが指摘され、上書き
するかどうか訊いてきますが、全て、上書きします。

上書きされたファイルとリポジトリ上(or バックアップ)にある対応
するファイルの差分をチェックし、上書きされたファイルに必要な記述を
復元します。

# Webpacker 3.5.5 -> 5.2.1

```
% bundle exec rails webpacker:install
```

実行により、いくつかのファイルのコンフリクトが指摘され、上書きするか
どうか訊いてきますが、全て、上書きします。

上書きされたファイルとリポジトリ上(or バックアップ)にある対応する
ファイルの差分をチェックし、上書きされたファイルに必要な記述を復元
します。

## Vue.js まわりのアップデート (オプション)

```
% bundle exec rails webpacker:install:vue
```

他のフレームワークを利用している場合には、対応した
タスクがあれば、それを実行します、対応したタスクがない
場合には自前で更新します。

### babel-loader の設定変更 (オプション)
Vue.jsでHTMLテンプレートのコンパイルをブラウザでの実行に行う場合
には`config/webpack/environment.js` に次の設定のどちらかを追加します。

```
// Vue.js フル版（Compiler入り）
environment.config.resolve.alias = { 'vue$': 'vue/dist/vue.esm.js' } // Vue2
environment.config.resolve.alias = { 'vue$': 'vue/dist/vue.esm-bundler.js' } // Vue3
```

この設定がないときは、コンパイラなしの
`vue/dist/vue.runtime.esm.js (Vue2)` / `vue.runtime.esm-bundler.js (Vue3)`
が、読み込まれます。
(Vue.jsでHTMLテンプレートの実行時コンパイルを行なっている場合、
エラーになります。)


## rails-erb-loaderのアップデート (オプション)

```
% bundle exec rails webpacker:install:erb
```

## node_modules の更新

```
% yarn install
```

表示されるメッセージで NPM モジュールの依存関係を確認し、必要な NPM モジュールが
あれば`yarn add` でインストールしてから、再度、`yarn install` を実行
します。この手順をエラーが出なくなるまで繰り返します。

## .babelrc の削除

Webpackerの3.xでは必要でしたが、Webpacker4.x 以降では、`babel.cofig.js`、`postcss.config.js`、`.postcssrc.yml`
に置き換わっているので、削除します。

## バンドル

```
% bin/webpack
```

## 動作確認

```
% bundle exec rails s
```

## rails-ujs -> @rails/ujs (オプション)

```
% yarn add @rails/ujs
```

Sprocketsを利用している場合は、
`app/assets/javascripts/application.js`の
`rails-ujs`を`@rails/ujs`に変更します。

```
//= require @rails/ujs
```

Webpackerを利用しているorする場合は、`app/javascript/packs/application.js`
に、次の２行を追加します。

```
import Rails from '@rails/ujs';
Rails.start();
```

Webpackerでの管理に新規に移行する場合には、erbにある
`javascript_include_tag 'application'` を
`javascript_pack_tag 'application'` に
変更します。

## WebPackerの 設定変更 (オプション)

### SplitChunk の有効化 (オプション)

`config/webpack/environment.js` に次の設定を追加します。

```
environment.splitChunks()
```
erbにある `javascriot_pack_tag` を `javascript_packs_with_chunks_tag`
に変更します。

### node_modules を babel-loader の対象から除外する(オプション)

`config/webpack/environment.js` に次の設定を追加します。

```
environment.loaders.delete('nodeModules')
```

Webpacker4.x以降では、デフォルトでは、`node_modules`
ディレクトリにあるファイルが babel-loader 経由での(Babelの)
トランパイルの対象になっています。

この追加により、Webpacker3と同様にWebpacker4以降でも
babel-loaderが`node_modules`ディレクトリを無視する
挙動になります。

Webpacker4以降でブラウザで動作確認を行なった際に、`node_modules`
下にあるNPMモジュールでエラーが発生している場合、この設定変更を試すこと
をオススメします。

`node_modules` ディレクトリにあるファイルがbabel-loader経由
でのトランパイルの対象になっていることにより、思わぬ変換が行われて
いることが原因である可能性があります。

例えば、`node_modules` ディレクトリにあるバンドル対象のファイル内
に `module.exports =`の記述がある場合、WebPacker4.x以降の
デフォルト設定では、

```
Cannot assign to read only property 'exports' of object
```
エラーが発生することがあります。

自作の NPMモジュールを`node_modules`ディレクトリで管理していて
クライアントサイドから利用しているので Babel でのトランスパイルが
必要etc、の理由で、babel-loader のトランスパイル対象から
`node_modules`ディレクトリを外せない場合には、次のようにして、
エラーが発生している任意の NPM モジュールを babel-loader での
トランスパイルの対象から外すことができます。

```
const nodeModulesLoader = environment.loaders.get('nodeModules')
if (!Array.isArray(nodeModulesLoader.exclude)) {
  nodeModulesLoader.exclude = (nodeModulesLoader.exclude == null)
    ? []
    : [nodeModulesLoader.exclude]
}
nodeModulesLoader.exclude.push(/任意のNPMモジュール/)
```

#### 追記

Webpackerは、6.0.0から、デフォルトでは、`node_modules`
ディレクトリにあるファイルは babel-loader 経由での(Babelの)
トランパイルの対象ではなくなります。

https://github.com/rails/webpacker/blob/master/CHANGELOG.md#600---2021-tbd

https://github.com/rails/webpacker/pull/2624

#### 追記

Rails7.0 から、Webpacker が Rails 本体から分離され、改名、有志の
手により Shakapacker として開発・メンテナンスされていくことになり
ました。

Shakapackerは、いつまで開発・メンテナンスされていくのか、Railsの
バージョンアップにどこまで追従されていくのか、不透明のため、一時的
に Webpacker -> Shakapacker するにしても、どこかの時点で、
jsbundling-rails への移行に踏み切るのが無難と思われます。
(継続的に機能追加・メンテナンスされていくWEBアプリの場合)

# Webpacker 5.x -> jsbundling-rails

jsbundling-rails は Rails6.x に対応しています。

公式の手順通りにすれば、移行できます。

https://techracho.bpsinc.jp/hachi8833/2022_02_03/115289

# (オマケ) turbo-links -> turbo

Turbo も Rails6.x に対応しています。

https://techracho.bpsinc.jp/hachi8833/2022_01_25/115082

Turbo Frame / Turbo Stream を利用する場合、Stimulus との
併用が視野に入ってきます。(Hotwire) 

# (オマケ) Webpack -> Vite/esbuild etc

jsbundling-rails では、フロントエンドのビルドツール
として、Webpack だけでなく esbuild etcもサポートしています。

::: note info
esbuild は goで書かれていて、高速に動作します。
:::

Vite/esbuild への移行をオススメします。

Vite は、jsbundling-rails ではサポートされていませんが、
vite_rails gemを利用すれば、railsに導入できます。

::: note info
これからのRails系のフロントエンド開発では、webpack から
Vite/esbuild への移行が進んでいくと考えられます。

JSガッツリなフロントエンド開発では、Vite(現行の最新版
(2025/06現在)ではesbuildを内部で利用)
or esbuild or Rspack への移行が進んでいるようです。

esbuild は 高速ではあるものの、アセット最適化に制限が
あるため、パフォーマンス上必要なアセット最適化がesbuildの
機能ではパ必要十分に行えないプロジェクトには向いて
いません。

そのようなプロジェクトでは、Viteへの移行(Viteの採用)を
オススメします。

:::
https://qiita.com/yuppe0328/items/771ee8a4fb153730270a

https://vitejs.dev

https://esbuild.github.io

https://zenn.dev/righttouch/articles/86457bf2908379#esbuild

Webpacker の話に戻すと、Webpacker -> jsbuilding-rails
で esbuild への移行を行いたい場合でも、
Webpacker -> jsbuilding-rails + webpack ->
jsbuilding-rails + esbuild、 と、ワンクッション挟んでの
移行をオススメします。

jsbuilding-rails + webpack においても、esbuild-loader
を利用して、babel-loader を置き換えることで、esbuild に
移行した場合ほどではありませんが、ビルド速度を改善できます。
(jsのトランスパイルを babel -> esbuild することで、ビルドを
高速化します。)

https://github.com/privatenumber/esbuild-loader

Vite/esbuild への移行が難しい場合には、Webpack
との互換性の高い Rspack への移行も視野に入ってきます。

Vite/esbuild 及び そのプラグイン？ でオルタナティブの提供
されていないwebpack の loader? を利用していて、webpack →
Vite/esbuild できない場合でも、Rspackならオルタナティブ
(のloader？)が提供されている可能性が高いです。

Webpack + esbuild-loader より Rspackのほうが
ビルド速度が高速です。

http://rspack.dev

推敲中...