Skip to content

Upgrade React on Rails/Shakapacker and standardize on Rspack v2#702

Merged
justin808 merged 8 commits intomasterfrom
jg-codex/upgrade-ror164-rspack2
Mar 23, 2026
Merged

Upgrade React on Rails/Shakapacker and standardize on Rspack v2#702
justin808 merged 8 commits intomasterfrom
jg-codex/upgrade-ror164-rspack2

Conversation

@justin808
Copy link
Member

@justin808 justin808 commented Mar 17, 2026

Summary

  • upgrade react_on_rails gem to 16.4.0 and shakapacker gem to 9.7.0
  • upgrade npm react-on-rails to 16.4.0 and shakapacker to 9.7.0
  • move to Rspack v2 prerelease (@rspack/core / @rspack/cli = 2.0.0-beta.7) and enforce rspack-only bundler path
  • refresh Procfile and README wording for rspack-first usage

Notes

  • npm currently has no @rspack/core 2.0.0-rc.*; this uses the latest published v2 prerelease (2.0.0-beta.7).
  • targeted Jest specs pass locally.
  • full yarn build:dev is blocked locally unless Ruby 3.4.6 is available (Gemfile requires 3.4.6).

Validation

  • yarn jest client/__tests__/webpack/bundlerUtils.spec.js
  • yarn jest client/__tests__/swc-config.spec.jsx

Summary by CodeRabbit

  • New Features

    • Dev and asset tooling switched to Rspack with integrated HMR/react-refresh support.
  • Dependencies

    • Bumped react-on-rails, shakapacker, Rspack packages; added Rspack react-refresh plugin; Node runtime updated.
  • Documentation

    • README and setup docs updated for Rspack workflows, Rails 8 targets, and ES2024 guidance.
  • Tests

    • Test suites adapted to Rspack-only behavior.
  • Chores / Breaking Changes

    • Dev watch commands no longer swallow rm failures (may surface non-zero exits).

Note

Medium Risk
Build pipeline and dependency upgrades (including @rspack/* v2 beta) can change asset compilation/HMR behavior and may break local/dev or CI builds if assumptions differ. Runtime/business logic is largely untouched, but SSR/client bundling paths are now stricter (Rspack-only) and will fail fast on misconfig.

Overview
Upgrades the React-on-Rails stack to stable react_on_rails/react-on-rails 16.4.0 and shakapacker 9.7.0, and moves the JS bundler to @rspack/core/@rspack/cli 2.0.0-beta.7 (plus @rspack/plugin-react-refresh).

Standardizes the build configuration on Rspack-only: bundlerUtils now throws unless assets_bundler: rspack, client/server webpack configs load the bundler exclusively via getBundler(), and dev react-refresh wiring is delegated to Shakapacker’s Rspack dev-server integration.

Updates developer workflows and docs to be Rspack-first (Procfiles, README copy/links), bumps ControlPlane Docker Node version to 22.12.0, adjusts .gitignore to commit .claude/commands, and adds a new .claude/commands/address-review.md helper command for PR review triage/replies.

Written by Cursor Bugbot for commit e23c883. This will update automatically on new commits. Configure here.

@coderabbitai
Copy link

coderabbitai bot commented Mar 17, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Migrates the project from Webpack to Rspack: bumps dependencies, updates Procfiles/README, enforces Rspack-only bundler behavior via refactored bundler utils and config changes, updates tests, adds a .claude command doc, adjusts .gitignore, and bumps Node version in a Dockerfile. (49 words)

Changes

Cohort / File(s) Summary
Dependency & Metadata
Gemfile, package.json
Bumped Ruby gem and npm package versions toward Rspack; added @rspack/plugin-react-refresh; removed a webpack-only react-refresh plugin; updated package description to reference Rspack.
Procfiles / Dev process
Procfile.dev, Procfile.dev-prod-assets, Procfile.dev-static, Procfile.dev-static-assets
Replaced Webpack labels/comments with Rspack wording; changed process keys/commands to rspack:; removed some `
Bundler utilities
config/webpack/bundlerUtils.js
Removed multi-bundler detection; enforced assets_bundler === 'rspack'; getBundler() now loads/caches @rspack/core; isRspack() simplified and getCssExtractPlugin() returns Rspack plugin.
Client/Server build configs
config/webpack/client.js, config/webpack/development.js, config/webpack/server.js
Centralized bundler resolution via getBundler(); removed conditional webpack/@rspack requires and react-refresh-webpack-plugin wiring; removed development-only plugin callback.
Tests
client/__tests__/webpack/bundlerUtils.spec.js
Updated tests to Rspack-only expectations: removed webpack-specific mocks, asserted Rspack plugin returns and error paths for non-rspack configs; added caching assertion.
Docs & tooling
README.md, .claude/commands/address-review.md, .gitignore
README rewritten to reference Rspack/Shakapacker and updated dev workflows; added .claude command documentation; .gitignore tweaked to preserve .claude/commands/address-review.md.
Build environment
.controlplane/Dockerfile
Bumped build ARG NODE_VERSION from 22.3.0 to 22.12.0.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped from webpack to rspack's door,

configs aligned and tests asked for more.
Procfiles whisper "rspack" in the night,
docs and Docker tuned to run just right.
A tiny rabbit cheers: builds take flight!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Upgrade React on Rails/Shakapacker and standardize on Rspack v2' accurately summarizes the main changes: upgrading key dependencies (React on Rails, Shakapacker) and standardizing on Rspack v2, which are the core focus of the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jg-codex/upgrade-ror164-rspack2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

🚀 Quick Review App Commands

Welcome! Here are the commands you can use in this PR:

/deploy-review-app

Deploy your PR branch for testing

/delete-review-app

Remove the review app when done

/help

Show detailed instructions, environment setup, and configuration options.


@github-actions
Copy link

github-actions bot commented Mar 17, 2026

🏗️ Building Docker image for PR #702, commit 64bb72f

📝 View Build Logs

🎮 Control Plane Console

@github-actions github-actions bot requested a deployment to review-app March 17, 2026 23:51 In progress
@greptile-apps
Copy link

greptile-apps bot commented Mar 17, 2026

Greptile Summary

This PR promotes the tutorial project from a dual webpack/Rspack setup to a standardised Rspack-only stack: it promotes react_on_rails gem/npm to 16.4.0 (from rc.9), shakapacker to 9.7.0, and moves to @rspack/core + @rspack/cli at 2.0.0-beta.7 (Rspack v2 prerelease). @pmmmwh/react-refresh-webpack-plugin is replaced by @rspack/plugin-react-refresh, all Procfiles and README copy are updated to reflect the Rspack-first story, and bundlerUtils.js now hard-enforces Rspack with a clear runtime error if the config deviates.

Key observations:

  • config/webpack/bundlerUtils.js is cleanly simplified and the new ensureRspack() guard is well-tested.
  • config/webpack/client.js still contains an unreachable require('webpack') ternary branch and an empty if block that are dead code and inconsistent with the rspack-only intent; the centralised getBundler() helper from bundlerUtils is not used here.
  • config/webpack/development.js has a leftover empty if (inliningCss) {} block that does nothing and should be removed.
  • Using a beta (2.0.0-beta.7) version of @rspack/core/@rspack/cli is noted in the PR description; consumers should be aware of possible instability until a stable v2 is published.

Confidence Score: 4/5

  • Safe to merge; changes are coherent and well-tested, with only minor dead-code leftovers in two config files.
  • The core logic changes in bundlerUtils.js are correct and fully covered by the updated spec. Gem/npm version bumps are straightforward promotions from rc to stable. The only deductions are: (1) client.js still carries a stale require('webpack') ternary and an empty if block that are dead code, and (2) development.js has a leftover empty if (inliningCss) block; neither causes a runtime issue but they reduce clarity. The use of an Rspack v2 prerelease (2.0.0-beta.7) is noted by the author.
  • config/webpack/client.js — contains dead-code webpack fallback and an empty if block that should be cleaned up.

Important Files Changed

Filename Overview
config/webpack/bundlerUtils.js Simplified from dual webpack/rspack detection to Rspack-only enforcement; adds ensureRspack() guard that throws a clear error if assets_bundler is not 'rspack'. Logic is correct and well-tested.
config/webpack/client.js Retains an unreachable require('webpack') fallback and an empty dead-code if block; should use getBundler() from bundlerUtils for consistency with the rspack-only stance.
config/webpack/development.js Removes ReactRefreshWebpackPlugin wiring; leaves an empty if (inliningCss) block that is now dead code and can be deleted.
client/tests/webpack/bundlerUtils.spec.js Tests updated to match rspack-only semantics; webpack-path tests converted to "throws" assertions; coverage is solid for all exposed functions.
package.json Bumps react-on-rails to 16.4.0 (from rc.9), shakapacker to 9.7.0, upgrades @rspack/core and @rspack/cli to 2.0.0-beta.7, removes @pmmmwh/react-refresh-webpack-plugin, and adds @rspack/plugin-react-refresh.
Gemfile Promotes react_on_rails from 16.4.0.rc.9 to 16.4.0 and shakapacker from 9.6.1 to 9.7.0; straightforward dependency promotion.
README.md Updates all Webpack references to Rspack, removes the dual-bundler performance comparison table, adds a new "JS Development without Rails using the Rspack Dev Server" section, and fixes a stale Procfile link.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["shakapacker.yml\nassets_bundler: rspack"] --> B["bundlerUtils.js\nensureRspack()"]
    B -- "=== rspack" --> C["require @rspack/core\ncached in _cachedBundler"]
    B -- "!== rspack" --> D["throw Error\nconfigured for Rspack only"]
    C --> E["getBundler()"]
    C --> F["getCssExtractPlugin()\nCssExtractRspackPlugin"]
    E --> H["client.js\nnew bundler.ProvidePlugin"]
    F --> I["CSS extraction in webpack configs"]
    A --> G["isRspack()\nreturns true"]
    K["@rspack/plugin-react-refresh"] --> L["Shakapacker handles HMR\nno extra wiring in development.js"]
Loading

Comments Outside Diff (1)

  1. config/webpack/client.js, line 7-9 (link)

    P2 Stale webpack fallback / inconsistent with bundlerUtils

    The inline ternary still falls back to require('webpack') and its comment still says "Auto-detect bundler". Since the project is now Rspack-only:

    1. require('webpack') will never execute (the ternary branch is unreachable), but it is misleading for future readers.
    2. This duplicates the centralised detection logic in bundlerUtils.getBundler(), which already enforces the Rspack-only constraint with a clear error message.

    Consider replacing with getBundler():

Last reviewed commit: "Upgrade to react_on_..."

@claude
Copy link

claude bot commented Mar 17, 2026

PR Review: Upgrade React on Rails/Shakapacker and standardize on Rspack v2

Overall: The direction is good - promoting Rspack to the sole supported bundler and upgrading all libraries to stable releases. A few code issues worth addressing before merging.

Issue 1: Dead code in config/webpack/client.js

Lines 7-9 still use the old dual-bundler detection with a require webpack fallback, bypassing bundlerUtils.getBundler() enforcement. If assets_bundler is misconfigured, this silently loads webpack instead of throwing the clear error message introduced in bundlerUtils.js. Lines 26-28 are an always-false empty block - since rspack is the only supported bundler, the condition config.assets_bundler !== 'rspack' can never be true. Suggested fix: replace lines 7-9 with const { getBundler } = require('./bundlerUtils'); const bundler = getBundler(); and delete the empty if block.

Issue 2: Dead code in config/webpack/development.js

The if (inliningCss) block now contains only a comment with no logic. Remove the block, the inliningCss import, and the developmentEnvOnly callback - then call module.exports = webpackConfig() directly without a callback.

Issue 3: Inconsistency between getBundler() and isRspack() in bundlerUtils.js

getBundler() throws when assets_bundler !== 'rspack', but isRspack() silently returns false. Any code guarding a webpack path via isRspack() will silently execute webpack logic rather than failing fast. Since webpack support is dropped, isRspack() should call ensureRspack() first and return true, or be removed outright. The test "returns false when assets_bundler is webpack" is testing behavior that arguably should throw under the new model.

Issue 4: Pinned prerelease for @rspack/core and @rspack/cli

Pinning to 2.0.0-beta.7 is acknowledged in the PR notes, but for a tutorial repo focused on teaching best practices a beta dependency raises the risk of undocumented breaking changes confusing learners. Consider adding a README note and a tracking issue to bump to stable once published.

@github-actions
Copy link

github-actions bot commented Mar 17, 2026

🎉 ✨ Deploy Complete! 🚀

🌐 ➡️ Open Review App

Deployment successful for PR #702, commit 5945fe5

🎮 Control Plane Console
📋 View Completed Action Build and Deploy Logs

@github-actions
Copy link

github-actions bot commented Mar 17, 2026

🚀 Deploying to Control Plane...

Waiting for deployment to be ready...

📝 View Deploy Logs
🎮 Control Plane Console

@github-actions github-actions bot requested a deployment to review-app March 17, 2026 23:56 In progress
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
config/webpack/client.js (1)

7-9: 🛠️ Refactor suggestion | 🟠 Major

Route both client.js and server.js through getBundler() for consistent bundler resolution.

Both config/webpack/client.js (lines 8-9) and config/webpack/server.js (lines 9-10) have direct bundler requires that bypass the centralized config/webpack/bundlerUtils.js guard. The shared getBundler() path now owns the Rspack-only contract, but these entry points still resolve the bundler directly. Refactor both files to use getBundler() instead to enforce a single source of truth.

♻️ Proposed cleanup for client.js
 const environment = require('./environment');
+const { getBundler } = require('./bundlerUtils');
 
-const bundler = config.assets_bundler === 'rspack'
-  ? require('@rspack/core')
-  : require('webpack');
+const bundler = getBundler();

Apply the same pattern to server.js.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@config/webpack/client.js` around lines 7 - 9, The client and server
entrypoints currently bypass the centralized bundler resolution by directly
requiring webpack/rspack; import and use the getBundler() helper from
config/webpack/bundlerUtils instead. Replace the existing ternary/require logic
that assigns bundler with: add an import/require for getBundler and set const
bundler = getBundler(), and remove the direct
require('@rspack/core')/require('webpack') code in both client.js and server.js
so bundler resolution is centralized through getBundler().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Procfile.dev-prod-assets`:
- Line 10: The watcher command in Procfile.dev-prod-assets calls
`react_on_rails:locale && rm -rf public/packs/* || true && bin/shakapacker -w`,
and the `|| true` currently masks failures from `react_on_rails:locale` so the
watcher (`bin/shakapacker -w`) still starts on locale errors; update the command
so that failures in `react_on_rails:locale` are not ignored (either remove the
`|| true` entirely or move it to only apply to `rm -rf public/packs/*`),
ensuring `react_on_rails:locale` runs with `&&` before starting `bin/shakapacker
-w` and that the watcher only starts when locale/build prep succeeds.

In `@Procfile.dev-static`:
- Line 11: The current rspack command chain lets the watcher start even if
`bundle exec rake react_on_rails:locale` fails because of the `|| true`; update
the command so rake failures abort startup but still tolerate `rm -rf
public/packs/*` errors. Concretely, remove the `|| true` after the rake step and
instead isolate the cleanup with a tolerant grouping such as wrapping `rm -rf
public/packs/*` in a subshell or grouping with its own `|| true` (e.g. `(rm -rf
public/packs/* || true)`), keeping `&& bin/shakapacker -w` so `bin/shakapacker
-w` only runs if the rake step succeeds; target the rspack Procfile dev entry
containing `bundle exec rake react_on_rails:locale`, `rm -rf public/packs/*`,
and `bin/shakapacker -w`.

In `@Procfile.dev-static-assets`:
- Line 10: The rspack process command currently masks failures by using "&& rm
-rf public/packs/* || true && bin/shakapacker -w" so bin/shakapacker -w runs
even if react_on_rails:locale fails; remove the "|| true" and ensure the command
is strictly chained with "&&" (i.e., run "bundle exec rake react_on_rails:locale
&& rm -rf public/packs/* && bin/shakapacker -w") so that a failing
react_on_rails:locale prevents the watcher (bin/shakapacker -w) from starting.

In `@README.md`:
- Line 33: Update the bullet text that reads "Optimizing your front end setup
with Rspack + Shakapacker for React on Rails, including SSR and code splitting."
to use the hyphenated compound adjective "front‑end setup" (i.e., change "front
end setup" to "front-end setup") so the sentence reads "Optimizing your
front-end setup with Rspack + Shakapacker for React on Rails, including SSR and
code splitting."

---

Outside diff comments:
In `@config/webpack/client.js`:
- Around line 7-9: The client and server entrypoints currently bypass the
centralized bundler resolution by directly requiring webpack/rspack; import and
use the getBundler() helper from config/webpack/bundlerUtils instead. Replace
the existing ternary/require logic that assigns bundler with: add an
import/require for getBundler and set const bundler = getBundler(), and remove
the direct require('@rspack/core')/require('webpack') code in both client.js and
server.js so bundler resolution is centralized through getBundler().
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c9a6d853-4ace-410b-8057-737d07e5d723

📥 Commits

Reviewing files that changed from the base of the PR and between 1e191ab and 64bb72f.

⛔ Files ignored due to path filters (2)
  • Gemfile.lock is excluded by !**/*.lock
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (11)
  • Gemfile
  • Procfile.dev
  • Procfile.dev-prod-assets
  • Procfile.dev-static
  • Procfile.dev-static-assets
  • README.md
  • client/__tests__/webpack/bundlerUtils.spec.js
  • config/webpack/bundlerUtils.js
  • config/webpack/client.js
  • config/webpack/development.js
  • package.json

@claude
Copy link

claude bot commented Mar 18, 2026

PR Review: Upgrade React on Rails/Shakapacker and standardize on Rspack v2

Overall this is a clean upgrade graduating react_on_rails and shakapacker from RC to stable. A few issues worth addressing:

Bugs / Code Quality

1. config/webpack/client.js bypasses the Rspack enforcement guard

client.js has its own inline bundler detection (lines 7-9) that silently falls back to requiring webpack instead of calling getBundler() from bundlerUtils.js. If someone sets assets_bundler: webpack in shakapacker.yml, client.js loads webpack silently while bundlerUtils.js would throw. The enforcement is inconsistent — this file should use getBundler() from bundlerUtils.js instead.

2. Dead empty if block in config/webpack/client.js (lines 26-28)

The condition if (devBuild && isHMR && config.assets_bundler !== 'rspack') can never be true since rspack is now enforced, and the block body is empty. Delete the entire block.

3. Dead empty if block in config/webpack/development.js (lines 11-14)

The if (inliningCss) block body is a no-op comment. Nothing happens regardless of inliningCss. Remove the block; move the comment to file level if helpful.

Dependency Risk

4. @rspack/core 2.0.0-beta.7 is a prerelease

Since this is a tutorial/reference repo that many developers clone as a starting point, consider adding a visible callout in the README near the version targets table so readers are not caught off-guard by breaking changes as Rspack 2.x stabilizes.

Minor / Documentation

5. Heroku link URL still targets Rails 7

The display text was updated but the href still points to getting-started-with-rails7. Either update the URL to a Rails 8 guide or restore the version-specific wording in the link text.

6. isRspack() semantics are misleading in an rspack-only repo

isRspack() returns false for non-rspack bundlers rather than throwing, while getBundler() throws. Any caller that branches on isRspack() === false to reach a webpack fallback is now silently wrong. Consider removing isRspack() or aligning it to throw on non-rspack configs like the other helpers.

What is good

  • Graduating from RC to stable 16.4.0 is the right call.
  • The ensureRspack() guard in bundlerUtils.js with a clear error message is a good DX improvement.
  • Test coverage correctly reflects the new rspack-only contract.
  • Procfile renames (webpack: to rspack:) reduce developer confusion.
  • README link fix (Procfile.static to Procfile.dev-static) was a real bug.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Autofix Details

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: React Fast Refresh plugin installed but never configured
    • Added @rspack/plugin-react-refresh to config/webpack/development.js and push it into the client plugins list when development HMR styling is enabled.
  • ✅ Fixed: Empty conditional block is dead code
    • Removed the empty if (devBuild && isHMR && config.assets_bundler !== 'rspack') no-op block from config/webpack/client.js.

Create PR

Or push these changes by commenting:

@cursor push 62ada50be9
Preview (62ada50be9)
diff --git a/config/webpack/client.js b/config/webpack/client.js
--- a/config/webpack/client.js
+++ b/config/webpack/client.js
@@ -23,8 +23,4 @@
   }),
 );
 
-if (devBuild && isHMR && config.assets_bundler !== 'rspack') {
-  // Rspack is the only supported bundler for this repo; no webpack refresh plugin wiring.
-}
-
 module.exports = environment;

diff --git a/config/webpack/development.js b/config/webpack/development.js
--- a/config/webpack/development.js
+++ b/config/webpack/development.js
@@ -4,13 +4,13 @@
 process.env.NODE_ENV = process.env.NODE_ENV || 'development';
 
 const { inliningCss } = require('shakapacker');
+const ReactRefreshRspackPlugin = require('@rspack/plugin-react-refresh');
 
 const webpackConfig = require('./webpackConfig');
 
-const developmentEnvOnly = (_clientWebpackConfig, _serverWebpackConfig) => {
+const developmentEnvOnly = (clientWebpackConfig, _serverWebpackConfig) => {
   if (inliningCss) {
-    // Rspack HMR/refresh is handled by Shakapacker + @rspack/plugin-react-refresh.
-    // No extra development-only plugin wiring is needed here.
+    clientWebpackConfig.plugins.push(new ReactRefreshRspackPlugin());
   }
 };

@github-actions
Copy link

github-actions bot commented Mar 18, 2026

🎉 ✨ Deploy Complete! 🚀

🌐 ➡️ Open Review App

Deployment successful for PR #702, commit 5945fe5

🎮 Control Plane Console
📋 View Completed Action Build and Deploy Logs

@github-actions
Copy link

github-actions bot commented Mar 18, 2026

🎉 ✨ Deploy Complete! 🚀

🌐 ➡️ Open Review App

Deployment successful for PR #702, commit 29ad680

🎮 Control Plane Console
📋 View Completed Action Build and Deploy Logs

@claude
Copy link

claude bot commented Mar 23, 2026

Review Summary

Overall this is a clean, well-scoped upgrade. The gem/npm versions are now at stable releases (dropping the -rc suffix), the Rspack-only simplification reduces conditional complexity, and the test coverage for the new strict behavior is solid.

One actual bug (3 files)

rm -rf without || true in Procfiles — The || true guard was intentionally protecting against rm -rf public/packs/* returning non-zero when the directory is empty or absent (fresh checkout, first run). Without it, the && chain aborts before bin/shakapacker -w starts. This affects Procfile.dev-prod-assets, Procfile.dev-static, and Procfile.dev-static-assets. See inline comments with suggested fixes.

Minor inconsistency

isRspack() always returns true without reading config.assets_bundler. This means it diverges from getBundler(), which enforces the config via ensureRspack(). Any caller using isRspack() as a gate (rather than getBundler()) won't catch misconfiguration. Options: delegate to the config check, or remove the export if it has no remaining internal callers. See inline comments.

Noted (no action needed)

  • @rspack/core 2.0.0-beta.7 is pinned to a beta — acknowledged in the PR description. Exact pinning (no ^) is appropriate here given the beta status.
  • getBundler() calls ensureRspack() on every invocation including cache hits — trivially O(1) and not a real issue.

@github-actions
Copy link

github-actions bot commented Mar 23, 2026

🎉 ✨ Deploy Complete! 🚀

🌐 ➡️ Open Review App

Deployment successful for PR #702, commit b22539a

🎮 Control Plane Console
📋 View Completed Action Build and Deploy Logs

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@README.md`:
- Around line 199-201: Update the README entry that lists bundler files so the
filename matches the codebase: replace the referenced "webpack.config.js" with
"webpackConfig.js" (as required by files like config/webpack/development.js
which do require('./webpackConfig')). Edit the README section that currently
names webpack.config.js and ensure the list reads "webpackConfig.js - Main
Shakapacker entry point" to avoid confusion.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e1a10280-ac9e-4c09-8a3b-726b93d396df

📥 Commits

Reviewing files that changed from the base of the PR and between c7c4a4c and b22539a.

📒 Files selected for processing (5)
  • README.md
  • client/__tests__/webpack/bundlerUtils.spec.js
  • config/webpack/bundlerUtils.js
  • config/webpack/client.js
  • config/webpack/development.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • client/tests/webpack/bundlerUtils.spec.js
  • config/webpack/client.js

@github-actions
Copy link

github-actions bot commented Mar 23, 2026

🎉 ✨ Deploy Complete! 🚀

🌐 ➡️ Open Review App

Deployment successful for PR #702, commit a7f8094

🎮 Control Plane Console
📋 View Completed Action Build and Deploy Logs

@claude
Copy link

claude bot commented Mar 23, 2026

PR Review: Upgrade React on Rails/Shakapacker + Rspack v2

Overall this is a clean, well-scoped upgrade. The dependency bumps to stable react_on_rails 16.4.0 / shakapacker 9.7.0 are straightforward, and the Rspack-only enforcement in bundlerUtils.js is clearly implemented with good test coverage. The Procfile shell-grouping fix ((rm -rf ... || true)) is a correct improvement over the previous operator-precedence bug.

A few issues worth addressing before merge:

Build/Deployment Risk

@rspack/core and @rspack/cli in devDependencies — these are required to compile assets during deployment (bin/shakapacker / assets:precompile). Most production deployment platforms (Heroku, ControlPlane, Docker multi-stage builds) strip dev deps before running the asset precompile step. Moving these to dependencies prevents a silent production build failure.

Orphaned webpack packages — webpack, webpack-cli, and webpack-assets-manifest remain in dependencies despite being unused in an Rspack-only project. See inline comment on package.json.

Correctness / Fragility

Plugin filter by constructor name in server.jsplugin.constructor.name !== 'WebpackAssetsManifest' will silently become a no-op if Rspack's manifest plugin has a different class name. See inline comment.

client.js sass-loader null-guard — .get('sass').use.find(...) has no null check and will throw a TypeError if the loader is absent. See inline comment.

Prerelease dependency

@rspack/core: 2.0.0-beta.7 is a beta. For a tutorial repo that people actively clone as a reference, it is worth adding a README note pointing to the Rspack v2 migration guide so readers know to check for breaking changes when stable is released.

Minor

  • config/webpack/server.js:3devBuild variable holds a string, not a boolean; misleading name. See inline comment.
  • bundlerUtils.js:45isRspack() is always true now; the false branch and tests imply Webpack is still a valid runtime option. See inline comment.
  • README.md prerequisites list still says Node v22.3.0 but Dockerfile was bumped to 22.12.0. Consider aligning to >=22.12.0.
  • client.js:3 comment says "Set by shakapacker dev-server for both webpack and rspack" — update to Rspack-only wording.

@github-actions
Copy link

github-actions bot commented Mar 23, 2026

🎉 ✨ Deploy Complete! 🚀

🌐 ➡️ Open Review App

Deployment successful for PR #702, commit e23c883

🎮 Control Plane Console
📋 View Completed Action Build and Deploy Logs

@claude
Copy link

claude bot commented Mar 23, 2026

Review: Upgrade React on Rails/Shakapacker and standardize on Rspack v2

Overall this is a clean, well-scoped upgrade. The move from RC versions to stable react_on_rails/react-on-rails 16.4.0 and shakapacker 9.7.0 is the right call, and removing the Webpack fallback path simplifies the config meaningfully. A few things worth considering before merge:

⚠️ Beta prerelease in a tutorial repo

@rspack/core and @rspack/cli are pinned to 2.0.0-beta.7. The PR notes that no RC is available yet for v2. Since this repo is used as a canonical example by teams adopting React on Rails, pinning to a pre-RC beta carries real risk: breaking changes between beta iterations are expected per semver pre-release conventions, and any user who follows the tutorial and runs yarn install after a beta bump could get a broken setup. Consider either:

  • Holding off on the v2 upgrade until an RC or stable is available, keeping v1 stable (1.5.x)
  • If v2 is required now, add a prominent note in the README that this is a beta dependency and may not be production-ready

isRspack() is now vestigial

With the project locked to Rspack-only, isRspack() will always return true in any valid configuration (anything that doesn't throw first in getBundler()). Exporting it creates a misleading API surface suggesting there is a non-Rspack code path. If there are no external callers, removing it would clarify intent.

Null safety improvement in client.js

The defensive null check added for sassLoader before accessing .use is a good catch — the original would throw if shakapacker's loader list changed. Well done.

Procfile rm -rf parenthesis fix ✓

Wrapping the rm -rf in parentheses so it doesn't swallow the exit code of the rest of the && chain is a correctness improvement. Good change.

target: 'web' in server bundle (pre-existing)

server.js builds the SSR bundle with target: 'web'. For a bundle executed in Node.js via execjs, target: 'node' or async-node is normally the appropriate target. This is pre-existing and out of scope for this PR, but worth tracking in a follow-up issue.

Comment on lines +47 to +48
"@rspack/cli": "2.0.0-beta.7",
"@rspack/core": "2.0.0-beta.7",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pinning to a pre-RC beta (2.0.0-beta.7) in a tutorial that serves as the canonical React on Rails example is risky. Semver pre-release conventions allow breaking changes between beta iterations, so a fresh yarn install after any upstream beta bump could break the tutorial for new users.

If v2 is needed before a stable/RC is available, consider using a range like >=2.0.0-beta.7 <2.0.0 to get patch-level beta fixes while staying on this major, and add a README callout that the v2 dependency is pre-release.

* @returns {boolean} True if using Rspack, false if using Webpack
* @returns {boolean} True when assets_bundler is rspack
*/
const isRspack = () => config.assets_bundler === 'rspack';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isRspack() is now redundant given the Rspack-only enforcement in getBundler(). In any valid configuration it always returns true; in an invalid one, the next getBundler() call throws anyway. Keeping it exported implies a non-Rspack path still exists, which could mislead future contributors. Consider removing it (and its export) — call sites can simply use true or be removed.

@@ -1,16 +1,17 @@
const devBuild = process.env.NODE_ENV === 'development';
// Set by shakapacker dev-server for both webpack and rspack.
const isHMR = process.env.WEBPACK_DEV_SERVER === 'TRUE';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable name isHMR and env var WEBPACK_DEV_SERVER are both Webpack-flavored while the project is now Rspack-only. The comment accurately notes that Shakapacker sets this env var for both bundlers, which is reassuring, but a new reader may be confused by the mismatch. A follow-up rename to isDevServer (or similar) would reduce friction for tutorial readers.

@justin808 justin808 merged commit 98f833a into master Mar 23, 2026
21 checks passed
@justin808 justin808 deleted the jg-codex/upgrade-ror164-rspack2 branch March 23, 2026 01:24
@github-actions
Copy link

github-actions bot commented Mar 23, 2026

✅ Review app for PR #702 was successfully deleted

View Completed Delete Logs

Control Plane Organization

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e23c8832f6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


## Javascript Development without Rails using the Rspack Dev Server

Start the full development stack with `foreman start -f Procfile.dev`, then open <http://localhost:4000> to iterate on the JavaScript client with hot reloading.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Point the new dev-server docs at a real port

Following this new quick-start now sends contributors to localhost:4000, but Procfile.dev only starts Rails on port 3000 and the Shakapacker dev server is configured for port 3035 in config/shakapacker.yml. There is no service bound to 4000 anywhere in the repo, so a fresh setup appears broken even when both processes started correctly.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant