🛠

Rail6からWebpackerを剥がしてみた

2022/12/13に公開

補足

  • やったことを思い出すための自分用のログという位置付けです 📝
    • Webpacker と直接関係ないことも作業ログとして書いてます
  • 理解が浅いところがあります 🙇‍♂️
  • とりあえずできたって感じなのでベストな方法ではないと思います 💡

ゴール

  • Webpacker -> Webpack に置き換えること
  • Heroku にデプロイして動作に問題がないこと

環境など

  • Rails 6.1.4
  • webpack 5.73.0
  • tailwind 3.1.6
  • React 18.2.0
    • Rails の view に埋め込んで使っている
  • Heroku にデプロイしているアプリケーション

なぜ剥がすのか

  • Jest を導入したくなったことがきっかけ
  • Webpacker だとレールから外れたカスタマイズが難しく、Jest 導入前に脱却しておこうと考えたから
    • Webpacker を導入してから外すまでをふりかえる - 弥生開発者ブログ
      • Webpacker の場合は、独自でラップされたクラスを経由してカスタマイズする必要があります

      • Webpacker が提供していない範囲で webpack のカスタマイズが必要になってくると、結局は webpack.config.js 相当のものを引き剥がして書き変える必要が出てきます

      • Webpacker 側のパッケージが更新されるまでは、独自で入れたいパッケージが更新できない・あるいは追加できない

  • 今まで脳死で Webpacker を使っていて JS のビルドに関する知識が乏しく、勉強になりそうだから

やったこと

やったことを時系列順に書きます。

1. Switch from Webpacker 5 to jsbundling-rails with webpackの手順通りにやってみる

  • jsbundling-rails のセットアップ
  • Webpacker に関するファイルの削除

起動はできたが React を使っている部分が表示されない

2. ということで gem を使わない方法に切り替えてみる

  • webpack を使った Rails 上での React 開発 - クックパッド開発者ブログ
    • Reactに関わるモジュールバンドリング(複数ファイルの結合)、ソースコードの変換、ビルドしたコードの配置まではwebpackで行い、ファイルへのフィンガープリント付与などはこれまで通りSprocketsに任せます。

  • この対応に伴いapp/javascript/packs/ のファイルを client/~ に移動させた
  • React 部分表示された!

3. ついでに tailwindcss-rails の gem も剥がす

  • yarn install tailwindcssし、tailwind の設定を webpack.config.js に追記

CSS が反映されない・・・なぜだ

  • tailwind.config.jscontent を設定していなかった
    • content で指定したファイルがビルドされるようになっている
    • 余計なビルドを避ける仕組み

tailwind.config.js の extend に定義したスタイルが適用されない

  • tailwind の extend で定義してるものは application.html.erb で使っていたのでビルドの対象になってなかった
    • application.html.erb 内でスタイル付けするのをやめて、jsx のコンポーネントに移した

4. Heroku に node.js のビルドパックを追加してデプロイする

デプロイを試みるも謎エラー発生

  • [ERR_MODULE_NOT_FOUND]: Cannot find package 'babel-plugin-macros'
    • webpacker 時代から残っていた webpack の設定ファイルbabel.config.js を参照してしまっていた?
      • ここにbabel-plugin-macrosが記述されている
      • ファイル削除して再 push

別のエラーになった

18:18:17 webpack.1 | ERROR in ./client/src/app.css (./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js!./client/src/app.css)
18:18:17 webpack.1 | Module build failed (from ./node_modules/postcss-loader/dist/cjs.js):
18:18:17 webpack.1 | TypeError: Cannot read property 'config' of undefined
  • この対応に伴いapp/javascript/packs/ のファイルを client/~ に移動させた をしたとき、 tailwind.config.jsposts.config.js を移動させていなかった
    • 移動させた
    • デプロイ成功!
    • しかし画像が表示されていない

5. 画像が Heroku 上で表示されない問題を解消する

  • ローカルでは表示されている
  • 画像読み込みには file-loader を使用している
    • 出力先を public 配下にしたら表示された
    • 出力先が assets 配下だとフィンガープリントが付くから表示されない?
  • file-loader のかわりに url-loader を使ったら表示された
    • url-loader は dataUrl として画像がバンドルされる
    • file-loader は画像ファイルそのものが出力される
  • webpack5 から AssetModules という、file-loaderurl-loader の代替となる機能が使えるようになったらしいので採用した
    • ファイルサイズの閾値を設定しておくと、file-loaderurl-loader を使い分けてくれる

最終的な設定ファイルはこちら

webpack.config.js

const path = require('path');

module.exports = {
  entry: {
    page1: './client/src/entrypoints/page1.js',
    page2: './client/src/entrypoints/page2.js',
    // ...
  },
  output: {
    path: path.resolve(__dirname, './app/assets/javascripts/webpack'),
    filename: '[name].js',
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        use: {
          loader: 'babel-loader',
        },
        exclude: /node_modules/,
      },
      {
        test: /\.(png|jpg|gif|svg)$/,
        type: 'asset',
        // 8kb以下の画像はバンドルし、8kb以上の画像は public/images に出力する
        parser: {
          dataUrlCondition: {
            maxSize: 8 * 1024, // 8kb
          },
        },
        generator: {
          filename: '[hash][ext][query]',
          outputPath: '../../../../public/images/',
          publicPath: 'images/',
        },
      },
      {
        test: /\.(css|scss)$/,
        use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'],
      },
    ],
  },
};

post.css.config

module.exports = {
  plugins: ['tailwindcss'],
};

tailwind.config.js

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ['./client/src/**/*.{html,js}'],
  theme: {
    extend: {
      backgroundColor: {
        primary: '#e40b20',
      },
      backgroundImage: {
        base: "url('../images/email-pattern.png')",
      },
      animation: {
        bg: 'bg 40s infinite linear',
      },
      keyframes: {
        bg: {
          '0%': { backgroundPosition: '0 0' },
          '100%': { backgroundPosition: '360px -360px' },
        },
      },
    },
    minHeight: {
      // フッターを除いた高さ
      main: 'calc(100vh - 148px)',
    },
  },
  plugins: [],
};

Discussion