9
0

Rails アプリの webpacker を shakapacker に移行した

Last updated at Posted at 2023-12-01

ツクリンク プロダクト部 Advent Calendar 2023 の2日目です。

概要

webpacker はもうメンテナンスされないので shakapacker に移行した。手順通りやればうまくいくけど、ハマったポイントもある。

webpacker はもうメンテされない

Webpacker has been retired 🌅

webpacker READMEより

webpacker はもうメンテナンスされません。最後のコミットから1年くらい経っていて、READMEにも大きく書いてありました。理由は DHH さんが詳しく語っています。もう2年前ですね。

ひいては webpacker が依存している webpack もアプデできないことになります。 webpack に脆弱性があった場合こまったことになる!

そして webpacker の正規最新バージョンが使用している webpack は、サポート中の Node.js だと OpenSSL に関連したエラーが出て使えません。 具体的なバージョン情報を箇条書きでまとめると以下のようになります。

移行先

webpacker が示した移行先は3つあります。

特に推奨されているのは上2つですが、 shakapacker は webpacker の正式の後継 gem なのでほぼコードそのままで移行でき、 shakapacker が示す手順ドキュメント通りやればだいたいは動きます。とりあえず webpack の最新に追従しておきたい、って思った時はこの手段を取れると思います。今回は shakapacker への乗り換えで行ってみましょう。

Official, actively maintained successor to rails/webpacker.ShakaCode stands behind the long-term maintenance and development of this project for the Rails community.

shakapacker の README.md より

手順

テスト書く

webpacker の移行にかぎらない一般的な手順ですがいちばん重要なことなので。

webpacker の使用箇所のテストケースを網羅的に書きます。新機能は何もないはずなのでリグレッションテストになるはず。 E2E の自動テストがあれば最高だけど、 E2E テストをちゃんと書くのは超大変かつ結構不安定になりがちなので、手動のテストケースも準備しておくといいと思います。

特に古いブラウザでも動かしたいって時はこの手順がいちばん大変かもしれません。反対に最新のブラウザで動けばいい場合は適当でもなんとかなるかも。

webpacker 5 -> shakapacker 6 に移行する

shakapacker が示す移行手順書があるので、この通りやります。
Upgrading from Webpacker v5 to Shakapacker v6

丁寧なドキュメントなのでこれで大体動くようになりますが、いくつかハマったのでそれらの解決方法を書きます。

javascript_pack_tag は一度しか呼べなくなる

javascript_pack_tag はひとつのリクエストにつき(1つのHTMLにつき)一度しか呼べなくなります。二つ以上の webpacker のエントリーポイントある場合は append_javascript_pack_tag をつかって統合することになりますが、それぞれのエントリーポイント(<script>)に別々の属性をつけることができなくなっています。 なので、以下のようなレガシーブラウザに対応した type=module nomodule による読み替えがあった場合動かなくなります(参考: ES Modules への橋渡しとしての nomodule 属性)。

<% # こういうコードはあきらめるしかない %>
<%= javascript_pack_tag 'some_script_for_modern_browsers', type: 'module' %>
<%= javascript_pack_tag 'some_script_for_legacy_browsers', nomodule: true %>

shakapacker のコード確認しても <script> タグの属性値としてセットされる options を複数保持する方法がなさそうなので正攻法ではあきらめるしかなさそうです

  def javascript_pack_tag(*names, defer: true, **options)
    if @javascript_pack_tag_loaded
      raise "To prevent duplicated chunks on the page, you should call javascript_pack_tag only once on the page. " \
      "Please refer to https://github.com/shakacode/shakapacker/blob/master/README.md#view-helpers-javascript_pack_tag-and-stylesheet_pack_tag for the usage guide"
    end

    append_javascript_pack_tag(*names, defer: defer)
    non_deferred = sources_from_manifest_entrypoints(javascript_pack_tag_queue[:non_deferred], type: :javascript)
    deferred = sources_from_manifest_entrypoints(javascript_pack_tag_queue[:deferred], type: :javascript) - non_deferred

    @javascript_pack_tag_loaded = true

    capture do
      concat javascript_include_tag(*deferred, **options.tap { |o| o[:defer] = true })
      concat "\n" if non_deferred.any? && deferred.any?
      concat javascript_include_tag(*non_deferred, **options.tap { |o| o[:defer] = false })
    end
  end

lib/shakapacker/helper.rb#L98-L115
javascript_pack_tag_queue にはいったエントリーポイント名を受け取った options で出力している。 一度しか呼べないチェックがあるので options を使い分けることはできなそう

  def append_javascript_pack_tag(*names, defer: true)
    update_javascript_pack_tag_queue(defer: defer) do |hash_key|
      javascript_pack_tag_queue[hash_key] |= names
    end
  end

lib/shakapacker/helper.rb#L182-L186
append_javascript_pack_tag はそもそも options をうけとらない

babel.config.js をいじっている場合は削除しちゃダメ

Remove babel.config.js if you never changed it. Configure your package.json to use the default:

"babel": {
 "presets": [
   "./node_modules/@rails/webpacker/package/babel/preset.js"
 ]
}

See customization example the Customizing Babel Config for React configuration.

if you never changed

を見落としていて動かなくなりました。たとえば独自に staging 環境などを追加してる場合にハマると思います。元のファイルを shakapcaker のデフォルト preset.js に則って直しましょう。

node_modules 以下がデフォルトでコンパイル対象から除外される

どうしても古いブラウザの対応が必要なときなど、 node_modules 以下の npm package のファイル群もコンパイルしたくなると思いますが、 shakapacker はデフォルトの設定で node_modules 以下をコンパイルの対象から外しています。

package/rules/jscommon.js
const { resolve } = require('path')
const { realpathSync } = require('fs')
const {
  source_path: sourcePath,
  additional_paths: additionalPaths
} = require('../config')

const inclusions = [sourcePath, ...additionalPaths].map(p => {
  try {
    return realpathSync(p)
  } catch (e) {
    return resolve(p)
  }
})

module.exports = {
  include: inclusions,
  exclude: [
    {
      // exclude all node_modules from running through babel-loader
      and: [resolve('node_modules')],
      // Do not exclude inclusions, as otherwise these won't be transpiled
      not: [...inclusions]
    }
  ]
}

package/rules/jscommon.js

なので、以下のような設定を webpacker.yml に書いておく必要があります。 additional_paths は上記の not: [...inclusion] になるので、こっちの設定の方が強いです。

config/webpacker.yml
  additional_paths: [
    'node_modules'
  ]

style-loader が mini-css-extract-loader で置き換えられた

style-loadermini-css-extract-loader で置き換えられたので、 JS ファイル中で import していた CSS が JS として読めなくなっています。 dev_server 設定だとどちらかを選べますが、 本番環境と開発環境でコードが大きく変わるのも面倒が多いので stylesheet_pack_tag を適宜呼ぶようにしました。

開発環境でリロードしてもスクリプトが新しくならないんだけど

shakapacker 6 だと開発環境でもスーパーリロードしないとキャッシュが効いてしまう問題があります。これはスクリプトの URL に hash がつかなくなったためで、これは shakapacker 7 の useContentHash オプションで修正できるようになってます。

see also https://github.com/shakacode/shakapacker/issues/88

Slow setup for development って警告が出るんだけど

Rails のログを見てると以下のような遅すぎ警告ログが流れるようになってます

Shakapacker::Compiler - Slow setup for development

Prepare JS assets with either:
1. Running `bin/shakapacker-dev-server`
2. Set `compile` to false in shakapacker.yml and run `bin/shakapacker -w`

これは webpacker の時から元々遅かったのを警告出すようにしたみたいです。なので無視しててもいいですが、言われた通りに bin/shakapacker -w とか使うと高速です。

see also https://github.com/shakacode/shakapacker/pull/6
webpacker の方の時にすでに指摘されていたのを shakapacker で巻き取ったものみたい
https://github.com/rails/webpacker/pull/3233

がんばろう

ほかにもアプリケーションによって色々はまりどころが出ると思いますが、 shakapacker のドキュメントとコードを読んで地道に解決していきましょう。 shakapacker 自体がどんどん薄くなる方針っぽいので、割と解決しやすいと思います。

テストする

まずは rails assets:precompile が通る状態になることです。

precompile できるようになったら最初に作ったテストケースを開発環境と本番に準ずる環境で実施します。テストが落ちたら移行ガイドと設定を見直します。古いブラウザをサポートしている場合はそのテストも忘れずに(というか古いブラウザをサポートしなくていい場合はコンパイルが通ってる時点で大体ちゃんと動く)。

あとは開発環境のコンパイルが通るか、 dev-server 周りの設定がうまくいっているか確認します。

終わり

中期的には Rails に乗るという意味で shakapacker をはがすのが良さそうですね。今回の手順でテストケースができたので、他の手を取るのも簡単になったはずです!

9
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
0