Skip to content

chore: update React on Rails and Shakapacker RC test matrix#740

Draft
justin808 wants to merge 8 commits into
masterfrom
jg-codex/rc-react-on-rails-16-7-0-rc-0
Draft

chore: update React on Rails and Shakapacker RC test matrix#740
justin808 wants to merge 8 commits into
masterfrom
jg-codex/rc-react-on-rails-16-7-0-rc-0

Conversation

@justin808
Copy link
Copy Markdown
Member

@justin808 justin808 commented May 22, 2026

Refs shakacode/react_on_rails#3360

Release matrix

React on Rails 16.7.0 RC

Published and registry-verified release target:

  • npm: react-on-rails@16.7.0-rc.1
  • npm: react-on-rails-pro@16.7.0-rc.1
  • npm: react-on-rails-pro-node-renderer@16.7.0-rc.1
  • npm: create-react-on-rails-app@16.7.0-rc.1
  • gems: react_on_rails 16.7.0.rc.1, react_on_rails_pro 16.7.0.rc.1

Current app-update blocker:

  • The npm packages are now visible and the rc dist-tags point at 16.7.0-rc.1.
  • react-on-rails-pro@16.7.0-rc.1 is published with dependencies: { "react-on-rails": "workspace:*" }.
  • This Rails app uses Yarn v1, and Yarn v1 cannot resolve the published workspace:* dependency from npm, so yarn add react-on-rails-pro@16.7.0-rc.1 fails with Couldn't find any versions for "react-on-rails" that matches "workspace:*".
  • The React on Rails app bump should wait for a corrected published package/version where react-on-rails-pro depends on the concrete matching npm version instead of workspace:*.
  • react-on-rails-rsc remains React-versioned and is not part of the React on Rails product-version bump.

Shakapacker 10.1.0 RC

Published release target:

  • npm: shakapacker@10.1.0-rc.2
  • npm: shakapacker-webpack@10.1.0-rc.2
  • npm: shakapacker-rspack@10.1.0-rc.2
  • gem: shakapacker 10.1.0.rc.2

Implementation notes:

  • Bumps the app to shakapacker@10.1.0-rc.2 and the shakapacker gem to 10.1.0.rc.2.
  • Adds shakapacker-rspack@10.1.0-rc.2 explicitly.
  • Keeps explicit Rspack peer dependencies in package.json, because this project uses Yarn v1 and Yarn v1 does not auto-install required peer dependencies.
  • Updates Rspack packages to the v2 stable line required by the Shakapacker RC notes.
  • Keeps assets_bundler: webpack for now. Testing assets_bundler: rspack still fails in the React Server Components plugin with contextModuleFactory.resolveDependencies is not a function, so Webpack remains the working build path while the RSC/Rspack compatibility issue is unresolved.

Changelog notes studied

React on Rails 16.7.0.rc.1 notes checked:

  • webpackHelpers export for reactDomClientWarning.
  • Deterministic dev ports via REACT_ON_RAILS_BASE_PORT / CONDUCTOR_PORT.
  • Renderer cache pre-seeding, rolling-deploy adapter work, async props, and incremental RSC rendering.
  • Length-prefixed streaming protocol and renderer/cache path changes.
  • jwt 2.x compatibility restoration, RSC client manifest restoration, Fastify security bump, TanStack Router hydration fixes, nested package-root doctor fixes, and generated pack serialization fixes.

Shakapacker 10.1.0-rc.2 notes checked:

  • New supplemental packages: shakapacker-webpack and shakapacker-rspack.
  • shakapacker-rspack depends on shakapacker ~10.1.0-rc.2 and declares required Rspack peer dependencies.
  • Node engine floor is ^20.19.0 || >=22.12.0.
  • Rspack fresh installs target Rspack v2.
  • Supplemental packages moved core bundler dependencies to required peers.
  • Fixes around config helper binstubs, webpack-dev-server defaults, Rspack React Refresh v2 loading, peer ranges, local-path version checks, Rspack Sass detection, and ESM binstubs.

Validation

Local validation run against the prepared Shakapacker/Rspack dependency update:

  • bin/conductor-exec bundle check
  • bin/conductor-exec yarn install --frozen-lockfile
  • bin/conductor-exec yarn check --verify-tree
  • bin/conductor-exec yarn jest client/__tests__/webpack/bundlerUtils.spec.js
  • bin/conductor-exec yarn build:test
  • git diff --check

Additional React on Rails rc.1 install check:

  • npm registry now resolves all four 16.7.0-rc.1 packages.
  • bin/conductor-exec yarn add react-on-rails-pro@16.7.0-rc.1 react-on-rails-pro-node-renderer@16.7.0-rc.1 fails because the published react-on-rails-pro package depends on react-on-rails via workspace:*.

CI status on the currently pushed PR commit:

  • JS CI
  • Lint CI
  • Rspec CI
  • deploy
  • CodeRabbit
  • claude-review failed because Claude Code hit a session limit; the failure is external to the app/test suite.

Follow-up before final review

  • Publish a corrected React on Rails npm package/version where react-on-rails-pro depends on a concrete react-on-rails version rather than workspace:*.
  • After that, update this app to the corrected React on Rails package set and push the prepared dependency changes so GitHub Actions runs against the final RC matrix.

Note

Medium Risk
Dependency RC upgrades affect server rendering and RSC streaming; spec changes reflect a protocol shift that must match production behavior.

Overview
Bumps the app to React on Rails / React on Rails Pro 16.7.0.rc.2 on both Ruby (react_on_rails, react_on_rails_pro) and npm (react-on-rails-pro, react-on-rails-pro-node-renderer), with lockfile refreshes for related gems and the node renderer’s Fastify peer.

Updates spec/requests/server_components_spec.rb so RSC streaming tests no longer assume one JSON object per line. Parsing now walks a length-prefixed frame format: optional blank line separators, a metadata line with tab-separated hex length, raw content bytes, and payloadType-aware decoding into the existing html field expectations.

Reviewed by Cursor Bugbot for commit 516ce28. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 22, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4b452a52-73a6-44be-8e78-e53cab8df57e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jg-codex/rc-react-on-rails-16-7-0-rc-0

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
Copy Markdown

Review app commands

  • +review-app-deploy - create or redeploy this PR's review app.
  • +review-app-delete - delete this PR's review app and temporary resources.
  • +review-app-help - show setup details and workflow behavior.

For setup details, comment +review-app-help.

Comment thread package.json Outdated
Comment on lines +85 to +86
"react-on-rails-pro": "16.7.0-rc.0",
"react-on-rails-pro-node-renderer": "16.7.0-rc.0",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The yarn.lock file was intentionally left at 16.6.0 for these packages, which means the committed lockfile is inconsistent with package.json. Any CI step using --frozen-lockfile (e.g. yarn install --frozen-lockfile) will fail because the lockfile doesn't satisfy the new 16.7.0-rc.0 requirement. The lockfile should be regenerated by running yarn install locally and committing the result—even for RC versions—so the repo is in a coherent, reproducible state.

Comment thread Gemfile.lock Outdated
psych (>= 4.0.0)
tsort
react_on_rails (16.6.0)
react_on_rails (16.7.0.rc.0)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The PR description notes that Gemfile.lock was manually edited (only version strings were updated, not via bundle update react_on_rails_pro). Modern Bundler versions store per-gem checksums in Gemfile.lock; if those weren't updated to match the new gem tarballs, bundle install will fail with a checksum mismatch error. Please regenerate this file with bundle update react_on_rails react_on_rails_pro and commit the result.

Copy link
Copy Markdown

@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 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Stale yarn lock blocks installs
    • Updated yarn.lock to lock react-on-rails-pro, react-on-rails-pro-node-renderer, and the nested react-on-rails dependency at 16.7.0-rc.0 so frozen installs now succeed.

Create PR

Or push these changes by commenting:

@cursor push 06cf0b472a
Preview (06cf0b472a)
diff --git a/yarn.lock b/yarn.lock
--- a/yarn.lock
+++ b/yarn.lock
@@ -8783,10 +8783,10 @@
   resolved "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz"
   integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==
 
-react-on-rails-pro-node-renderer@16.6.0:
-  version "16.6.0"
-  resolved "https://registry.npmjs.org/react-on-rails-pro-node-renderer/-/react-on-rails-pro-node-renderer-16.6.0.tgz#c13ca0f156566531d7c6e005759459b0f19472a8"
-  integrity sha512-fBZ0lKRaEe8LyVTdUsXx364zQfL6hGJuE+2qQsKo+bXm0aTVq2RtO49gzq0m7Y4xuhBTVmnPQUP0O1v1cGRzLg==
+react-on-rails-pro-node-renderer@16.7.0-rc.0:
+  version "16.7.0-rc.0"
+  resolved "https://registry.yarnpkg.com/react-on-rails-pro-node-renderer/-/react-on-rails-pro-node-renderer-16.7.0-rc.0.tgz#fcd6b23bddf81a3805f38d2f23561a6aeaa16ef2"
+  integrity sha512-kgm8iuoyT1I2DPID0AWTaSjIuVbiW39vTBBouEZ2adxzX2+YKVpuBGQgTaRedTUPcdCkuVTnSNPIgcmkY9FNmw==
   dependencies:
     "@fastify/formbody" "^7.4.0 || ^8.0.2"
     "@fastify/multipart" "^8.3.1 || ^9.0.3"
@@ -8796,12 +8796,12 @@
     lockfile "^1.0.4"
     pino "^9.0.0"
 
-react-on-rails-pro@16.6.0:
-  version "16.6.0"
-  resolved "https://registry.npmjs.org/react-on-rails-pro/-/react-on-rails-pro-16.6.0.tgz#19a5ea99d7b397dd56f14cff1f31955211b4d0a2"
-  integrity sha512-Uc8o3gdHyIETvY5J9wVUyONKOhnkw9kGJDREMHQb/IuXoB5/Vo51UK487Rcep2Z+Dzz/bEvNoF+GuZohORZ7Zw==
+react-on-rails-pro@16.7.0-rc.0:
+  version "16.7.0-rc.0"
+  resolved "https://registry.yarnpkg.com/react-on-rails-pro/-/react-on-rails-pro-16.7.0-rc.0.tgz#2b1b6f1a65dc1780e77f7c2cb3e78916bf7b9eba"
+  integrity sha512-gy7PgXJZokaDepBVnqPIzINAhxJiBYbkGN0BHu/chRjhW2L7CJO/PC5pLnWnaV6CBu8AJipM+oNBiUpEXUyA6A==
   dependencies:
-    react-on-rails "16.6.0"
+    react-on-rails "16.7.0-rc.0"
 
 react-on-rails-rsc@19.0.4:
   version "19.0.4"
@@ -8812,10 +8812,10 @@
     neo-async "^2.6.1"
     webpack-sources "^3.2.0"
 
-react-on-rails@16.6.0:
-  version "16.6.0"
-  resolved "https://registry.npmjs.org/react-on-rails/-/react-on-rails-16.6.0.tgz#da7f117fec14f420f7f6ffe6bdb34b7fc2e01b3a"
-  integrity sha512-LqLi7A0n0Tv5c3yMYlwS9s6rE82gvXNMj3sscmK2LOgIJ+mLQlOX65n0Jq5ZJ4Nsl9SRgxEOOQmcrfvDeB9F1g==
+react-on-rails@16.7.0-rc.0:
+  version "16.7.0-rc.0"
+  resolved "https://registry.yarnpkg.com/react-on-rails/-/react-on-rails-16.7.0-rc.0.tgz#4fb47203ab9f65528605233c3b6cddba69536107"
+  integrity sha512-81Lu9ToSlunduiPQm+R+C+DJ9irdPDFEN3Jvq8dyI6r5uKc9kgfsTiJkyeULjk613RBAT0apkr08GOyhaSe94A==
 
 react-proxy@^1.1.7:
   version "1.1.8"

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit e26bec4. Configure here.

Comment thread package.json Outdated
@claude
Copy link
Copy Markdown

claude Bot commented May 22, 2026

Code Review: chore: bump react_on_rails to 16.7.0.rc.0

Overview

This PR bumps react_on_rails_pro (Ruby gem) and react-on-rails-pro / react-on-rails-pro-node-renderer (npm packages) from 16.6.0 to 16.7.0.rc.0 / 16.7.0-rc.0. The changes touch Gemfile, Gemfile.lock, and package.json.


Blocking Issues

1. yarn.lock is stale (inline comment on package.json)
The PR author explicitly left yarn.lock unchanged. With Yarn v1, a --frozen-lockfile install (common in CI) will fail because the lockfile still resolves these packages to 16.6.0. Even without --frozen-lockfile, relying on Yarn to silently re-resolve at install time means the committed lockfile is not an accurate snapshot of what will actually be installed. Please run yarn install locally and commit the updated yarn.lock.

2. Gemfile.lock was manually edited, not regenerated (inline comment on Gemfile.lock)
Only the version strings in Gemfile.lock were hand-edited. Bundler (v2.2+) records per-gem checksums in the CHECKSUMS section of the lockfile; those are still pointing at the 16.6.0 tarballs. Running bundle install in strict mode will raise a checksum mismatch error. The fix is to run bundle update react_on_rails react_on_rails_pro and commit the result.


Non-blocking Observations

  • RC version suitability for a tutorial repo: End users clone this repo to learn from a working reference implementation. Pinning to an RC dependency means they may hit pre-release bugs. Consider whether a stable release is available or add a prominent note for users to be aware of this.

  • Version string format difference is correct: Ruby uses 16.7.0.rc.0 (dot-separated pre-release) and npm uses 16.7.0-rc.0 (semver pre-release identifier). This inconsistency between ecosystems is expected and not a bug.

  • react_on_rails transitive dependency: The lock file update correctly pins react_on_rails (= 16.7.0.rc.0) as a transitive dependency of react_on_rails_pro. This is the right approach since it is not declared directly in Gemfile.


Summary

The intent of the PR is correct, but the two lockfile issues need to be resolved before merge to avoid broken CI and broken bundle install for users of the repo. The fix is straightforward: run bundle update react_on_rails react_on_rails_pro && yarn install locally and commit both updated lockfiles.

justin808 added a commit to shakacode/react_on_rails that referenced this pull request May 24, 2026
…#3366) (#3368)

## Summary

Fixes [#3366](#3366) —
a 16.7.0-rc.0 regression where the RSC client manifest
(`react-client-manifest.json`) is silently dropped from the Pro Node
Renderer upload, breaking server-component rendering with `ENOENT` on
the renderer side.

- Add a side-effect import of `react-on-rails-rsc/client.browser` at the
top of `wrapServerComponentRenderer/client.tsx`. RSCWebpackPlugin scans
every parsed module's resource path for an exact match against
`require.resolve("../client.browser.js")` and only emits
`react-client-manifest.json` when it finds one. Previously the client
runtime was reachable only through a three-level transitive chain
(`wrapServerComponentRenderer/client` → `getReactServerComponent.client`
→ `react-on-rails-rsc/client.browser`); any tooling that severed a link
in that chain silently dropped the manifest and produced the misleading
`Client runtime at react-on-rails-rsc/client was not found` warning. The
direct import keeps the runtime resource in the client module graph
regardless of how downstream tooling handles transitive default imports.
- Add a structural regression test that fails if the side-effect import
is removed from either the source or the compiled lib output.
- Add a webpack-level regression test that compiles
`wrapServerComponentRenderer/client.tsx` with the old transitive
`getReactServerComponent.client` edge replaced by a stub, then asserts
`RSCWebpackPlugin` still emits `react-client-manifest.json`. This proves
the direct import itself preserves the manifest.

## Why

Issue #3366 reports that the warning + missing manifest first appears
when the consuming app upgrades to `react_on_rails_pro` /
`react-on-rails-pro` / `react-on-rails-pro-node-renderer` `16.7.0-rc.0`
from `16.6.0`, even with `react-on-rails-rsc@19.0.4` and
`shakapacker@10.0.0` unchanged. The actual chain in source/lib is intact
between both versions, and the issue does not reproduce on the in-tree
dummy app or a clean clone of the tutorial PR — but the user's
environment clearly does see `clientFileNameFound = false` from the
upstream RSC plugin. The defensive direct import removes that fragility:
even if a future transpiler/tree-shaker decides
`getReactServerComponent.client` is "unused" or rewrites the path, the
side-effect import keeps `client.browser` in the graph and the plugin
always emits the manifest.

## Evaluation

Confirmed the fix addresses the bug mechanism. The RSC plugin decides
whether to write `react-client-manifest.json` by seeing the
`react-on-rails-rsc/client.browser` module in the client compilation.
The direct side-effect import is an appropriate fix because it changes
build graph visibility without changing renderer runtime behavior.

I verified the new behavior test is meaningful by temporarily removing
the direct import: with the old transitive helper path stubbed out,
webpack emitted the expected `Client runtime at
react-on-rails-rsc/client was not found` warning and the test failed.
Restoring the import made the same test pass and emit
`react-client-manifest.json`. The Pro dummy build also emits both
`react-client-manifest.json` and `react-server-client-manifest.json`
successfully.

## Test plan

- [x] `pnpm --filter react-on-rails-pro exec jest
tests/wrapServerComponentRenderer.client.manifest.test.js --runInBand`
passes
- [x] Red check: temporarily removing `import
'react-on-rails-rsc/client.browser';` makes the new manifest test fail
with the upstream missing-client-runtime warning
- [x] `pnpm --filter react-on-rails-pro exec jest
tests/wrapServerComponentRenderer.client.manifest.test.js
tests/wrapServerComponentRenderer.client.imports.test.ts --runInBand`
passes
- [x] `pnpm --filter react-on-rails-pro exec jest
tests/createReactOnRailsPro.test.ts --runInBand` passes
- [x] `pnpm --filter react-on-rails-pro run test:non-rsc` passes: 7
suites, 58 tests
- [x] `pnpm --filter react-on-rails-pro run type-check` passes
- [x] `pnpm run lint` passes
- [x] `(cd react_on_rails && bundle exec rubocop)` passes
- [x] `(cd react_on_rails_pro && bundle exec rubocop
--ignore-parent-exclusion)` passes; RuboCop prints deprecation warnings
from dependencies but reports no offenses
- [x] `pnpm exec prettier --check
packages/react-on-rails-pro/tests/wrapServerComponentRenderer.client.manifest.test.js
packages/react-on-rails-pro/tests/fixtures/rsc-manifest/getReactServerComponent.client.stub.ts`
passes
- [x] `RAILS_ENV=test NODE_ENV=development bundle exec bin/shakapacker`
from `react_on_rails_pro/spec/dummy` passes and emits
`react-client-manifest.json` plus `react-server-client-manifest.json`
- [x] `git diff --check` passes
- [x] Pre-commit and pre-push hooks passed while committing and pushing
`3013f9d90`
- [ ] Full-repo `pnpm start format.listDifferent` is blocked by an
existing ignored `.context/plans/issue-3366-reproduction-plan.md`
formatting issue; the PR files pass direct Prettier checks
- [ ] Verify CI passes on this PR
- [ ] Verify
[shakacode/react-webpack-rails-tutorial#740](shakacode/react-webpack-rails-tutorial#740)
unblocks once a new RC build with this fix is published

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches Pro RSC client entrypoint bundling to force inclusion of the
RSC client runtime, which can affect build output and hydration behavior
if misconfigured. Added tests reduce regression risk, but changes are in
a critical RSC packaging path.
> 
> **Overview**
> Restores reliable React Server Components hydration for Pro by adding
a **top-level side-effect import** of
`react-on-rails-rsc/client.browser` in
`wrapServerComponentRenderer/client.tsx`, ensuring `RSCWebpackPlugin`
consistently emits `react-client-manifest.json` even when transitive
imports are disrupted.
> 
> Adds regression coverage: a structural test that enforces the presence
of the side-effect import (source and built `lib` when available) and a
webpack-level test that stubs `getReactServerComponent.client` to prove
manifest emission still occurs. Updates `knip.ts` to ignore dynamically
referenced test fixtures and records the fix in `CHANGELOG.md`.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
1bb24cc. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Restored inclusion of the client runtime so the client manifest is
emitted, preventing React Server Components hydration failures in
certain build setups.

* **Tests**
* Added regression tests to ensure the client runtime import is
preserved and the client manifest is emitted during bundling.

* **Documentation**
  * Updated CHANGELOG with the fix.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/shakacode/react_on_rails/pull/3368?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
justin808 added a commit to shakacode/react_on_rails that referenced this pull request May 24, 2026
…#3366) (#3368)

## Summary

Fixes [#3366](#3366) —
a 16.7.0-rc.0 regression where the RSC client manifest
(`react-client-manifest.json`) is silently dropped from the Pro Node
Renderer upload, breaking server-component rendering with `ENOENT` on
the renderer side.

- Add a side-effect import of `react-on-rails-rsc/client.browser` at the
top of `wrapServerComponentRenderer/client.tsx`. RSCWebpackPlugin scans
every parsed module's resource path for an exact match against
`require.resolve("../client.browser.js")` and only emits
`react-client-manifest.json` when it finds one. Previously the client
runtime was reachable only through a three-level transitive chain
(`wrapServerComponentRenderer/client` → `getReactServerComponent.client`
→ `react-on-rails-rsc/client.browser`); any tooling that severed a link
in that chain silently dropped the manifest and produced the misleading
`Client runtime at react-on-rails-rsc/client was not found` warning. The
direct import keeps the runtime resource in the client module graph
regardless of how downstream tooling handles transitive default imports.
- Add a structural regression test that fails if the side-effect import
is removed from either the source or the compiled lib output.
- Add a webpack-level regression test that compiles
`wrapServerComponentRenderer/client.tsx` with the old transitive
`getReactServerComponent.client` edge replaced by a stub, then asserts
`RSCWebpackPlugin` still emits `react-client-manifest.json`. This proves
the direct import itself preserves the manifest.

## Why

Issue #3366 reports that the warning + missing manifest first appears
when the consuming app upgrades to `react_on_rails_pro` /
`react-on-rails-pro` / `react-on-rails-pro-node-renderer` `16.7.0-rc.0`
from `16.6.0`, even with `react-on-rails-rsc@19.0.4` and
`shakapacker@10.0.0` unchanged. The actual chain in source/lib is intact
between both versions, and the issue does not reproduce on the in-tree
dummy app or a clean clone of the tutorial PR — but the user's
environment clearly does see `clientFileNameFound = false` from the
upstream RSC plugin. The defensive direct import removes that fragility:
even if a future transpiler/tree-shaker decides
`getReactServerComponent.client` is "unused" or rewrites the path, the
side-effect import keeps `client.browser` in the graph and the plugin
always emits the manifest.

## Evaluation

Confirmed the fix addresses the bug mechanism. The RSC plugin decides
whether to write `react-client-manifest.json` by seeing the
`react-on-rails-rsc/client.browser` module in the client compilation.
The direct side-effect import is an appropriate fix because it changes
build graph visibility without changing renderer runtime behavior.

I verified the new behavior test is meaningful by temporarily removing
the direct import: with the old transitive helper path stubbed out,
webpack emitted the expected `Client runtime at
react-on-rails-rsc/client was not found` warning and the test failed.
Restoring the import made the same test pass and emit
`react-client-manifest.json`. The Pro dummy build also emits both
`react-client-manifest.json` and `react-server-client-manifest.json`
successfully.

## Test plan

- [x] `pnpm --filter react-on-rails-pro exec jest
tests/wrapServerComponentRenderer.client.manifest.test.js --runInBand`
passes
- [x] Red check: temporarily removing `import
'react-on-rails-rsc/client.browser';` makes the new manifest test fail
with the upstream missing-client-runtime warning
- [x] `pnpm --filter react-on-rails-pro exec jest
tests/wrapServerComponentRenderer.client.manifest.test.js
tests/wrapServerComponentRenderer.client.imports.test.ts --runInBand`
passes
- [x] `pnpm --filter react-on-rails-pro exec jest
tests/createReactOnRailsPro.test.ts --runInBand` passes
- [x] `pnpm --filter react-on-rails-pro run test:non-rsc` passes: 7
suites, 58 tests
- [x] `pnpm --filter react-on-rails-pro run type-check` passes
- [x] `pnpm run lint` passes
- [x] `(cd react_on_rails && bundle exec rubocop)` passes
- [x] `(cd react_on_rails_pro && bundle exec rubocop
--ignore-parent-exclusion)` passes; RuboCop prints deprecation warnings
from dependencies but reports no offenses
- [x] `pnpm exec prettier --check
packages/react-on-rails-pro/tests/wrapServerComponentRenderer.client.manifest.test.js
packages/react-on-rails-pro/tests/fixtures/rsc-manifest/getReactServerComponent.client.stub.ts`
passes
- [x] `RAILS_ENV=test NODE_ENV=development bundle exec bin/shakapacker`
from `react_on_rails_pro/spec/dummy` passes and emits
`react-client-manifest.json` plus `react-server-client-manifest.json`
- [x] `git diff --check` passes
- [x] Pre-commit and pre-push hooks passed while committing and pushing
`3013f9d90`
- [ ] Full-repo `pnpm start format.listDifferent` is blocked by an
existing ignored `.context/plans/issue-3366-reproduction-plan.md`
formatting issue; the PR files pass direct Prettier checks
- [ ] Verify CI passes on this PR
- [ ] Verify
[shakacode/react-webpack-rails-tutorial#740](shakacode/react-webpack-rails-tutorial#740)
unblocks once a new RC build with this fix is published

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches Pro RSC client entrypoint bundling to force inclusion of the
RSC client runtime, which can affect build output and hydration behavior
if misconfigured. Added tests reduce regression risk, but changes are in
a critical RSC packaging path.
> 
> **Overview**
> Restores reliable React Server Components hydration for Pro by adding
a **top-level side-effect import** of
`react-on-rails-rsc/client.browser` in
`wrapServerComponentRenderer/client.tsx`, ensuring `RSCWebpackPlugin`
consistently emits `react-client-manifest.json` even when transitive
imports are disrupted.
> 
> Adds regression coverage: a structural test that enforces the presence
of the side-effect import (source and built `lib` when available) and a
webpack-level test that stubs `getReactServerComponent.client` to prove
manifest emission still occurs. Updates `knip.ts` to ignore dynamically
referenced test fixtures and records the fix in `CHANGELOG.md`.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
1bb24cc. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Restored inclusion of the client runtime so the client manifest is
emitted, preventing React Server Components hydration failures in
certain build setups.

* **Tests**
* Added regression tests to ensure the client runtime import is
preserved and the client manifest is emitted during bundling.

* **Documentation**
  * Updated CHANGELOG with the fix.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/shakacode/react_on_rails/pull/3368?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@justin808 justin808 changed the title chore: bump react_on_rails to 16.7.0.rc.0 chore: update React on Rails and Shakapacker RC test matrix May 24, 2026
Updates react_on_rails_pro gem and react-on-rails-pro,
react-on-rails-pro-node-renderer npm packages from RC.0 to RC.2.
@claude
Copy link
Copy Markdown

claude Bot commented May 25, 2026

Code Review — chore: update React on Rails and Shakapacker RC test matrix

Overview

This PR bumps react_on_rails / react_on_rails_pro from 16.6.0 → 16.7.0.rc.2 (Ruby + npm) and rewrites the RSC streaming spec to handle the new length-prefixed frame protocol. Dependency ripple-effects bring console, httpx, io-event, and most notably jwt along as updates.


Discrepancy: Shakapacker not updated

The PR description dedicates a full section to "Shakapacker 10.1.0 RC" and states:

Bumps the app to shakapacker@10.1.0-rc.2 and the shakapacker gem to 10.1.0.rc.2.

However the actual diff shows:

  • Gemfile still pins shakapacker, "10.0.0"
  • package.json has no shakapacker changes
  • yarn.lock has no shakapacker changes

This makes the PR description misleading for reviewers. If the Shakapacker update is intentionally deferred (which the description's blocking notes suggest), that section should be reworded to describe what was evaluated but not yet applied rather than framing it as a done item.


JWT major-version bump (2.10.2 → 3.2.0) — needs verification

react_on_rails_pro relaxed its constraint from jwt (~> 2.7) to jwt (>= 2.7), and Bundler resolved jwt 3.2.0. JWT 3.x is a breaking major release with changes to algorithm handling, the JWT.decode interface, and header claims. If any application-level code (or the gem itself) uses the JWT Ruby API directly, this needs explicit verification. Please confirm:

  1. The app has no direct JWT.decode / JWT.encode calls.
  2. The react_on_rails_pro 16.7.0.rc.2 gem is tested against jwt >= 3.

RSC Spec Parser — correctness notes

The new binary parser in server_components_spec.rb is generally sound — correct use of .b for binary-safe slicing, proper encoding coercion before JSON parsing, and explicit error raises with meaningful messages. A few issues worth addressing:

  1. Unhandled ArgumentError on bad hex (line 41): Integer(content_length_hex, 16) raises ArgumentError for invalid input, bypassing the explicit raise pattern used everywhere else. Wrapping it or pre-validating would give a consistent error message.

  2. byteslice length vs end-index (line 47): body.byteslice(offset, body.bytesize) works but the second argument is a length, not an end index. Using body.bytesize as the length implies "up to the full buffer size," which is correct but misleading at first read. body.byteslice(offset..) is clearer.

  3. while modifier with assignment (line 29): The one-liner body = ... while body.start_with?("\n") is valid but uncommon enough to cause a double-take. An explicit while block improves readability.

  4. Silent mutation in parse_payload_metadata (line 54): metadata.delete("payloadType") mutates the parsed hash before assigning html. The intent is clear once understood, but a brief comment would help the next reader.


Minor / Non-blocking

  • Pinning an RC gem version directly in Gemfile is appropriate for a tutorial/demo app but should be updated to a stable release before this branch could serve as a reference implementation for production setups.
  • fastify bump (^5.8.3^5.8.5) in yarn.lock is a patch update driven by the node-renderer package; no concerns.

Summary

The dependency upgrades are mechanically straightforward. The main asks before merge:

  • Clarify or fix the Shakapacker section in the PR description.
  • Verify JWT 3.x compatibility with the app and gem.
  • Minor spec-parser polish on lines 29, 41, and 47.

end

def discard_blank_frame_lines(body)
body = body.byteslice(1..) || "".b while body.start_with?("\n")
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The while modifier with an assignment on the same line is valid Ruby but uncommon enough to cause a double-take. An explicit loop block is easier to scan:

Suggested change
body = body.byteslice(1..) || "".b while body.start_with?("\n")
def discard_blank_frame_lines(body)
while body.start_with?("\n")
body = body.byteslice(1..) || "".b
end
body
end

raise "Malformed length-prefixed RSC payload: missing tab separator" unless content_length_hex

content_start = header_end + 1
content_length = Integer(content_length_hex, 16)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Integer(content_length_hex, 16) raises ArgumentError on invalid hex input, which bypasses the explicit raise "Malformed …" pattern used on every other error path. Consider wrapping it for a consistent failure message:

Suggested change
content_length = Integer(content_length_hex, 16)
content_length = Integer(content_length_hex.strip, 16)

Or wrap with rescue:

      content_length = Integer(content_length_hex, 16)
rescue ArgumentError
  raise "Malformed length-prefixed RSC payload: invalid hex length '#{content_length_hex}'"


[
parse_payload_metadata(metadata_json, content),
body.byteslice(content_start + content_length, body.bytesize) || "".b
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

byteslice(offset, length) takes a length as its second argument, not an end index. Passing body.bytesize works (it's large enough to capture everything remaining) but reads as if it were an end index. The endless-range form is both correct and self-documenting:

Suggested change
body.byteslice(content_start + content_length, body.bytesize) || "".b
body.byteslice(content_start + content_length..) || "".b

def parse_payload_metadata(metadata_json, content)
metadata = JSON.parse(metadata_json.force_encoding(Encoding::UTF_8))
content.force_encoding(Encoding::UTF_8)
metadata["html"] = metadata.delete("payloadType") == "object" ? JSON.parse(content) : content
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The metadata.delete("payloadType") call is doing double duty — removing the key and returning its value for the comparison. This is idiomatic Ruby but non-obvious at first read. A brief comment clarifies the intent:

Suggested change
metadata["html"] = metadata.delete("payloadType") == "object" ? JSON.parse(content) : content
# Remove payloadType from the hash and use its value to decide how to decode content.
metadata["html"] = metadata.delete("payloadType") == "object" ? JSON.parse(content) : content

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