Skip to content

feat(generator): scaffold native RSCRspackPlugin for rspack RSC (#3488)#3590

Merged
justin808 merged 10 commits into
mainfrom
jg-conductor/3488-rspack-rsc-native-plugin
Jun 5, 2026
Merged

feat(generator): scaffold native RSCRspackPlugin for rspack RSC (#3488)#3590
justin808 merged 10 commits into
mainfrom
jg-conductor/3488-rspack-rsc-native-plugin

Conversation

@justin808

@justin808 justin808 commented Jun 3, 2026

Copy link
Copy Markdown
Member

Summary

Part of #3488 (Rspack RSC to production-ready). Switches the RSC generator to scaffold the native RSCRspackPlugin (react-on-rails-rsc/RspackPlugin) for rspack projects instead of RSCWebpackPlugin. Webpack projects are unchanged.

This is the verified-correct manifest path for Rspack. A controlled A/B on the marketplace demo showed the webpack plugin running under Rspack's compatibility layer producing valid-looking manifests that still failed ~7/11 RSC routes at runtime, while the native RSCRspackPlugin (standard rspack public APIs, identical manifest schema) rendered and hydrated every route. The two plugins share the same { isServer, clientReferences } API, so this is a drop-in swap.

Changes

  • Helpers (GeneratorHelper): rsc_plugin_class_name / rsc_plugin_import_path, keyed on using_rspack?, as the single source of truth for both generator paths.
  • Fresh-install templates (base/base/config/webpack/{server,client}WebpackConfig.js.tt): branch the plugin import + class on those helpers.
  • Standalone-upgrade migration (rsc_setup.rb, rsc_setup/client_references.rb): parameterize the injected class name + import path by bundler; broaden the plugin-detection regex to match both RSCWebpackPlugin and RSCRspackPlugin so re-run idempotency stays correct. A fresh --rsc --rspack install runs the migration on top of the template, so without this the migration would inject a second, webpack plugin.
  • Warnings: migration warnings now name the active bundler's plugin.
  • Package pin (JsDependencyManager::RSC_PACKAGE_VERSION_PIN): 19.0.4 -> 19.0.5-rc.6, the release exporting ./RspackPlugin. Backward-compatible for webpack (still exports ./WebpackPlugin). Bumps to stable 19.0.5 when published (Rspack RSC: path to production-ready — pivot to native RSCRspackPlugin #3488).
  • Docs (rspack-compatibility.md): reflect the native-plugin pivot; "experimental" label kept because the stable package and demo route-hydration CI gate are still pending in Rspack RSC: path to production-ready — pivot to native RSCRspackPlugin #3488.

Architecture decisions and validation

  • Stayed on react-on-rails-rsc@19.0.5-rc.6; no RC7 gap was found. All RSC installs share the rc.6 pin so webpack and rspack generator paths are tested against the same package.
  • Rspack installs do not fall back to unversioned react-on-rails-rsc after a pinned install failure. Manual recovery instructions keep the rc.6 pin because older or unversioned packages may not export RspackPlugin.
  • Migration support stays intentionally conservative in this PR: the generator normalizes single-line CommonJS legacy plugin imports before the clientReferences rewrite safety gate, while unsupported ESM or multi-line plugin import shapes warn/comment rather than attempting a broader JavaScript rewrite.

Validation after review fixes:

  • bundle exec rubocop react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb react_on_rails/lib/generators/react_on_rails/generator_helper.rb react_on_rails/lib/generators/react_on_rails/rsc_setup/client_references.rb react_on_rails/spec/react_on_rails/generators/generator_helper_spec.rb react_on_rails/spec/react_on_rails/generators/js_dependency_manager_spec.rb react_on_rails/spec/react_on_rails/generators/rsc_generator_spec.rb
  • bundle exec rspec react_on_rails/spec/react_on_rails/generators/generator_helper_spec.rb react_on_rails/spec/react_on_rails/generators/js_dependency_manager_spec.rb
  • bundle exec rspec react_on_rails/spec/react_on_rails/generators/rsc_generator_spec.rb:2159 react_on_rails/spec/react_on_rails/generators/rsc_generator_spec.rb:2416
  • bundle exec rspec react_on_rails/spec/react_on_rails/generators/rsc_generator_spec.rb (198 examples, 0 failures)
  • git diff --check
  • Follow-up operator-message nits are tracked in Follow up RSC generator install messaging nits from #3590 #3640.

Tests

  • install_generator_spec (--rsc --rspack): asserts native RSCRspackPlugin and that no webpack plugin leaks in.
  • rsc_generator_spec (standalone upgrade on an rspack project): same.
  • Webpack --rsc contexts unchanged (regression guard).
  • generator_helper_spec: contract test for the new helpers.
  • js_dependency_manager_spec: updated pin assertions.

Follow-up (out of scope - flagged from #3488 work)

While grounding this against the proven demo, I found a pre-existing, bundler-agnostic divergence in the RSC bundle loader strategy (NOT the plugin this PR swaps):

  • The generator's rscWebpackConfig.js.tt injects react-on-rails-rsc/WebpackLoader by wrapping the existing swc/babel rule, so it runs before transpilation (on raw TSX).
  • The proven demo (for both webpack and rspack) instead uses a separate enforce: 'post' rule, so the loader runs after swc compiles TSX to JS, giving acorn clean JavaScript to parse.

WebpackLoader parses via React's react-server-dom-webpack-node-loader (acorn), which needs already-transpiled JS, so the demo's enforce: 'post' may be the correct strategy and the generator's wrap-before-transpile may be fragile for TSX under both bundlers. This can't be verified from this repo (it needs the demo route-hydration gate), is identical for webpack and rspack, and is orthogonal to the plugin swap. Recommend tracking it on #3488 rather than expanding this PR.


Note

Medium Risk
Changes affect RSC bundler config generation and dependency pinning for all --rsc installs; mistakes could break rspack RSC builds or leave wrong manifest plugins, though scope is generator/docs/tests rather than runtime rendering.

Overview
Rspack RSC installs now scaffold RSCRspackPlugin from react-on-rails-rsc/RspackPlugin instead of the webpack-compat RSCWebpackPlugin, while webpack RSC projects keep the webpack plugin. Generator helpers (rsc_plugin_class_name, rsc_plugin_import_path, and inactive counterparts) centralize that choice from using_rspack? for fresh templates, standalone RSC upgrades, cleanup re-renders, and verification messages.

Re-running RSC setup on rspack migrates legacy RSCWebpackPlugin imports and new calls to the native plugin when safe, dedupes duplicate imports, and flags the wrong plugin during verification. Plugin detection regexes and warnings cover both class names. react-on-rails-rsc is pinned to 19.0.5-rc.6 for all --rsc installs; if the pinned install fails on rspack, the generator does not fall back to unversioned latest (which lacks RspackPlugin). Docs and changelog describe the native rspack manifest path and updated compatibility matrix.

Summary by CodeRabbit

  • Documentation

    • Marked Rspack generator as “Experimental”; documented native Rspack manifest/plugin behavior, native manifest path, and that RSC configs are scaffolded under a rspack config area; clarified manifest emission is added only to server/client bundles; refreshed compatibility matrix and limitations.
  • Chores

    • Updated RSC package pin to 19.0.5-rc.6 and made generator messaging and scaffolding bundle-aware with clearer, bundler-specific guidance.
  • Tests

    • Expanded specs for Rspack-native plugin scaffolding, manifest expectations, migration/deduping, and updated version assertions.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

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

Generators, templates, migration logic, dependency management, specs, and docs now select and verify the RSC manifest plugin class/import based on bundler (rspack vs webpack), adding normalization, dedup, and rspack-specific dependency guidance.

Changes

RSC Plugin Abstraction for Rspack and Webpack

Layer / File(s) Summary
Bundler-agnostic RSC plugin selection helpers
react_on_rails/lib/generators/react_on_rails/generator_helper.rb, react_on_rails/spec/react_on_rails/generators/generator_helper_spec.rb
New methods rsc_plugin_class_name and rsc_plugin_import_path derive bundler-specific plugin class and import path from using_rspack?.
Template render context delegates
react_on_rails/lib/generators/react_on_rails/base_generator.rb
Expose rsc_plugin_class_name and rsc_plugin_import_path to ERB TemplateRenderContext for managed template cleanup rendering.
RSC setup generator refactor
react_on_rails/lib/generators/react_on_rails/rsc_setup.rb, react_on_rails/lib/generators/react_on_rails/rsc_setup/client_references.rb
Server/client transform, verification, and warning messages use rsc_plugin_class_name/import path; scanner regex widened to match both plugin constructors; normalization, dedup, injection, and manual-action helpers added.
Webpack config templates: dynamic plugin instantiation
react_on_rails/lib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt, .../clientWebpackConfig.js.tt, .../rscWebpackConfig.js.tt
Templates require the configured plugin import path and instantiate the configured plugin class; server template documents rscBundle skip behavior.
RSC package version pin update
react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb, react_on_rails/spec/react_on_rails/generators/js_dependency_manager_spec.rb
Pinned RSC package updated to 19.0.5-rc.6; manager docs and specs adjusted to expect the new pin and include rspack fallback guidance.
Generator and integration test updates
react_on_rails/spec/react_on_rails/generators/*
Install, rsc, and generator specs updated to assert native RSCRspackPlugin for rspack outputs, absence of webpack plugin in rspack configs, broader bundler-message wording, and dedup/injection behaviors.
Rspack compatibility documentation
docs/pro/react-server-components/rspack-compatibility.md
Docs marked experimental and updated to describe native RSCRspackPlugin manifest behavior, RSC bundle skip semantics, testing expectations, and related resources.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

  • AbanoubGhadban
  • alexeyr-ci2

Poem

🐰 I hopped through templates, plugins in tow,

Rspack or Webpack—helpers now know.
Manifests and tests lined up just right,
Migration tidy, no duplicate fright.
Hop on, review—this rabbit's delight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 19.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: scaffolding native RSCRspackPlugin for rspack RSC projects, which is the primary objective of this PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
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-conductor/3488-rspack-rsc-native-plugin

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.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

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 is ON. A cloud agent has been kicked off to fix the reported issue.

Reviewed by Cursor Bugbot for commit 5280682. Configure here.

Comment thread react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb Outdated

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

Copy link
Copy Markdown

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: 5280682242

ℹ️ 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".

@greptile-apps

greptile-apps Bot commented Jun 4, 2026

Copy link
Copy Markdown

Greptile Summary

Switches the RSC generator from RSCWebpackPlugin to the native RSCRspackPlugin for rspack projects, introduced two GeneratorHelper methods (rsc_plugin_class_name / rsc_plugin_import_path) as the single source of truth, and broadens RSC_PLUGIN_INVOCATION_REGEX so that re-running the migration on a freshly-templated rspack project does not inject a duplicate webpack plugin.

  • Bundler-specific plugin scaffolding: templates and standalone migration now both branch on using_rspack? through the new helpers; webpack path is unchanged.
  • Idempotency fix: the detection regex now matches RSCWebpackPlugin | RSCRspackPlugin, so a fresh --rsc --rspack install (template → migration) does not double-inject.
  • Version pin bump: react-on-rails-rsc is pinned from stable 19.0.4 to 19.0.5-rc.5 globally to expose the RspackPlugin export; webpack RSC projects receive this RC without needing it.

Confidence Score: 4/5

The rspack-specific changes are well-scoped and the idempotency fix is correct, but every webpack RSC install now pulls an RC package that stable 19.0.4 could serve instead.

The core generator logic (helper methods, template parameterisation, broadened regex) is clean and the test coverage is thorough. The single concrete concern is that RSC_PACKAGE_VERSION_PIN is a shared constant used by both webpack and rspack RSC installs. Webpack projects have no dependency on the RspackPlugin export that necessitated the bump, so they are unnecessarily locked to a pre-release on every fresh --rsc install until stable 19.0.5 ships.

react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb — the shared RSC_PACKAGE_VERSION_PIN constant and rsc_packages_with_version method.

Important Files Changed

Filename Overview
react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb Bumps RSC_PACKAGE_VERSION_PIN from stable 19.0.4 to 19.0.5-rc.5 globally; the same RC pin is applied to webpack RSC installs even though the native RspackPlugin they depend on is only needed for rspack projects.
react_on_rails/lib/generators/react_on_rails/generator_helper.rb Adds two new helper methods rsc_plugin_class_name and rsc_plugin_import_path that return the correct bundler-specific plugin identifiers, serving as the single source of truth for both templates and migration paths.
react_on_rails/lib/generators/react_on_rails/rsc_setup.rb Replaces all hardcoded RSCWebpackPlugin strings with rsc_plugin_class_name interpolation; one warning message still embeds webpack in the insertion-point description but this is cosmetic only.
react_on_rails/lib/generators/react_on_rails/rsc_setup/client_references.rb Broadens RSC_PLUGIN_INVOCATION_REGEX to match both RSCWebpackPlugin and RSCRspackPlugin, fixing idempotency for fresh --rsc --rspack installs; replaces remaining hardcoded class name references with the helper.
react_on_rails/lib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt Template plugin instantiation and comments are now bundler-aware via ERB helpers; clean change.
react_on_rails/lib/generators/react_on_rails/templates/base/base/config/webpack/clientWebpackConfig.js.tt Swaps hardcoded RSCWebpackPlugin import and instantiation for ERB template expressions using the new helper methods; clean change.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[rails generate react_on_rails:install --rsc] --> B{using_rspack?}
    B -- yes --> C[rsc_plugin_class_name = RSCRspackPlugin]
    B -- no --> D[rsc_plugin_class_name = RSCWebpackPlugin]
    C --> E[Templates scaffold RSCRspackPlugin in config/rspack/]
    D --> F[Templates scaffold RSCWebpackPlugin in config/webpack/]
    E --> G[rsc_setup migration runs]
    F --> G
    G --> H{RSC_PLUGIN_INVOCATION_REGEX matches existing plugin?}
    H -- yes --> I[Skip re-injection - Idempotent re-run]
    H -- no --> J[Inject rsc_plugin_class_name with import and instantiation]
    G --> K[install react-on-rails-rsc at RSC_PACKAGE_VERSION_PIN]
    K --> L[19.0.5-rc.5 installed for ALL bundlers]
Loading

Reviews (1): Last reviewed commit: "feat(generator): scaffold native RSCRspa..." | Re-trigger Greptile

Comment thread react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb Outdated
Comment thread react_on_rails/lib/generators/react_on_rails/rsc_setup.rb Outdated
Comment thread react_on_rails/lib/generators/react_on_rails/rsc_setup/client_references.rb Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
react_on_rails/lib/generators/react_on_rails/rsc_setup.rb (1)

386-389: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Normalize legacy plugin invocations before taking the existing-config path.

Now that rsc_plugin_invocation_in_js_code? matches both RSCWebpackPlugin and RSCRspackPlugin, older rspack projects that still have the webpack plugin will hit this early return and only get the clientReferences rewrite. The code never swaps RSCWebpackPlugin / react-on-rails-rsc/WebpackPlugin to the native rspack pair, so standalone upgrades keep the compatibility-layer plugin that this PR is meant to replace.

Please rewrite mismatched existing plugin imports/constructors to rsc_plugin_class_name / rsc_plugin_import_path before returning here, or only treat the file as “already configured” when it already uses the active bundler’s plugin.

Also applies to: 436-438

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@react_on_rails/lib/generators/react_on_rails/rsc_setup.rb` around lines 386 -
389, The early-return path in the block that checks
rsc_plugin_invocation_in_js_code? causes legacy webpack plugin names to persist;
before calling update_existing_rsc_webpack_config (and returning) detect and
normalize any legacy import/constructor identifiers to the active names: use
rsc_plugin_class_name and rsc_plugin_import_path to replace legacy import
specifiers and constructor calls in content (e.g., swap RSCWebpackPlugin /
react-on-rails-rsc/WebpackPlugin to the current rsc_plugin_class_name and
rsc_plugin_import_path), then call update_existing_rsc_webpack_config with the
normalized content; alternatively, only take the existing-config path when the
file already references the active bundler plugin (i.e., ensure
rsc_plugin_invocation_in_js_code? reflects the active plugin before returning).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/pro/react-server-components/rspack-compatibility.md`:
- Line 15: Update the minimum version note to reflect the pinned release
candidate: change the text that currently reads "Exported from
`react-on-rails-rsc` 19.0.5 and later" to "Exported from `react-on-rails-rsc`
19.0.5-rc.5+ (switch to 19.0.5 stable once published)" so it matches the pinned
RSC_PACKAGE_VERSION_PIN = "19.0.5-rc.5"; keep the existing mention of
RspackPlugin / RSCRspackPlugin and the note about switching to the stable
release when available.

---

Outside diff comments:
In `@react_on_rails/lib/generators/react_on_rails/rsc_setup.rb`:
- Around line 386-389: The early-return path in the block that checks
rsc_plugin_invocation_in_js_code? causes legacy webpack plugin names to persist;
before calling update_existing_rsc_webpack_config (and returning) detect and
normalize any legacy import/constructor identifiers to the active names: use
rsc_plugin_class_name and rsc_plugin_import_path to replace legacy import
specifiers and constructor calls in content (e.g., swap RSCWebpackPlugin /
react-on-rails-rsc/WebpackPlugin to the current rsc_plugin_class_name and
rsc_plugin_import_path), then call update_existing_rsc_webpack_config with the
normalized content; alternatively, only take the existing-config path when the
file already references the active bundler plugin (i.e., ensure
rsc_plugin_invocation_in_js_code? reflects the active plugin before returning).
🪄 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: c7b2214a-71e4-4348-a8f3-496eed4f48de

📥 Commits

Reviewing files that changed from the base of the PR and between d7ea70e and 5280682.

📒 Files selected for processing (11)
  • docs/pro/react-server-components/rspack-compatibility.md
  • react_on_rails/lib/generators/react_on_rails/generator_helper.rb
  • react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb
  • react_on_rails/lib/generators/react_on_rails/rsc_setup.rb
  • react_on_rails/lib/generators/react_on_rails/rsc_setup/client_references.rb
  • react_on_rails/lib/generators/react_on_rails/templates/base/base/config/webpack/clientWebpackConfig.js.tt
  • react_on_rails/lib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt
  • react_on_rails/spec/react_on_rails/generators/generator_helper_spec.rb
  • react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb
  • react_on_rails/spec/react_on_rails/generators/js_dependency_manager_spec.rb
  • react_on_rails/spec/react_on_rails/generators/rsc_generator_spec.rb

Comment thread docs/pro/react-server-components/rspack-compatibility.md Outdated
@claude

claude Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Code Review

Overview

This PR correctly pivots rspack RSC projects from the webpack-compat-layer RSCWebpackPlugin to the native RSCRspackPlugin. The rsc_plugin_class_name / rsc_plugin_import_path helper abstraction is a clean single source of truth, the idempotency regex broadening is correct, and the test coverage for fresh installs is solid.

Three issues worth addressing:


1. Upgrade path gap: existing rspack projects with RSCWebpackPlugin won't get migrated

For any rspack project scaffolded before this PR (already has RSCWebpackPlugin in its configs), running the generator after this PR produces confusing results:

  1. rsc_plugin_invocation_in_js_code? finds RSCWebpackPlugin via the now-broadened RSC_PLUGIN_INVOCATION_REGEX — good.
  2. This routes to update_existing_rsc_webpack_config, which only rewrites clientReferencesit does not replace the plugin class name.
  3. check_rsc_server_config / check_rsc_client_config then check content.include?(rsc_plugin_class_name) ("RSCRspackPlugin") — not found — reports the plugin as missing.
  4. The user gets a warning that RSCRspackPlugin is missing while RSCWebpackPlugin is silently left in place.

The fix is either:

  • Add a migration step (in update_existing_rsc_webpack_config or a new method) to gsub_file RSCWebpackPlugin to RSCRspackPlugin and its import when using_rspack?, or
  • Broaden the check to match either plugin name: content.match?(/RSC(?:Webpack|Rspack)Plugin/) instead of content.include?(rsc_plugin_class_name).

There are no existing tests covering this upgrade path (existing rspack project that already has RSCWebpackPlugin).


2. Stale "webpack import anchor" in two user-facing warning messages

These two messages in rsc_setup/client_references.rb (lines ~1329 and ~1340) were not updated by this PR but are now misleading for rspack users:

"Could not inject rscClientReferences into ...: expected webpack import anchor was not found ..."
"Could not inject rscClientReferences into ...: expected webpack import anchor was found, ..."

Both say "webpack import anchor" while all other messages updated in this PR now say "bundler". These should be aligned.


3. RC package pin (acknowledged, tracked in #3488)

RSC_PACKAGE_VERSION_PIN = "19.0.5-rc.5" means every fresh install gets a pre-release package. Worth verifying the generated package.json records an exact pin (not a range) so npm/yarn cannot drift to a later RC. The bump to stable 19.0.5 will be a one-line constant change — confirmed by the design.


Minor

RSC_REACT_VERSION_RANGE and RSC_PACKAGE_VERSION_PIN are for different packages (React/ReactDOM vs. react-on-rails-rsc). The naming is slightly opaque — a brief comment clarifying they are independent would prevent future readers from thinking one range constrains the other.

Comment thread react_on_rails/lib/generators/react_on_rails/rsc_setup.rb
Comment thread react_on_rails/lib/generators/react_on_rails/rsc_setup.rb
justin808 added a commit that referenced this pull request Jun 4, 2026
…arden specs

Address review findings on PR #3590 (native RSCRspackPlugin scaffolding).

The PR added `<%= rsc_plugin_class_name %>` / `<%= rsc_plugin_import_path %>`
to the managed serverWebpackConfig.js.tt and clientWebpackConfig.js.tt
templates. Those managed templates are re-rendered during stale-config
cleanup (`rendered_template_for_cleanup`) against the restricted
`TemplateRenderContext` binding, which did not expose those helpers. For any
RSC project the cleanup render raised NameError, printed a
"could not render template ... treating as non-removable" warning, and
wrongly preserved the managed config files. Delegate both helpers in
`TemplateRenderContext` (mirroring use_pro?/use_rsc?) to fix this, and add a
regression spec asserting the RSC managed templates render (not the
TEMPLATE_RENDER_FAILED sentinel) for both webpack and rspack projects.

Also:
- Make the rscWebpackConfig.js.tt skip comment bundler-neutral so
  rspack-generated configs no longer reference RSCWebpackPlugin.
- Update two now-stale rsc_setup.rb docstrings that still described the
  RSC plugin detection/injection as webpack-only after the regex was
  broadened to match both plugin names.
- Add an rspack dedup/idempotency spec mirroring the existing webpack
  contexts, exercising the rspack branch of RSC_PLUGIN_INVOCATION_REGEX
  (verified to fail if the regex regresses to webpack-only).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@claude

claude Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Code Review

Overview

Clean, well-scoped PR. The design is correct: introduce two helper methods as the single source of truth for the bundler-keyed plugin name/path, thread them through templates, migration code, and verification checks, and update the idempotency regex to match both names. The test additions are thorough and the dedup regression test for the existing-RSCRspackPlugin case is exactly the right edge case to cover.

Medium issues

RC pin is installed into every new RSC project (webpack and rspack alike)
RSC_PACKAGE_VERSION_PIN = "19.0.5-rc.5" is used unconditionally — any user who runs --rsc today gets a pre-release package wired into their package.json. The PR notes this is intentional and temporary, but the comment on the constant only says to bump it once 19.0.5 is published. It does not warn that the fallback path (unversioned react-on-rails-rsc) installs whatever latest tag resolves to, which at this moment is the stable 19.0.4 — a version that does not export ./RspackPlugin. So for rspack users, the fallback actually produces a broken install. Consider adding an explicit warning in the fallback branch when using_rspack? to make that failure visible immediately rather than at build time.

RSC_REACT_VERSION_RANGE is not updated alongside the pin
RSC_REACT_VERSION_RANGE = "~19.0.4" is used to install react and react-dom. If react-on-rails-rsc@19.0.5-rc.5 (or the eventual stable 19.0.5) declares react@>=19.0.5 as a peer dependency, projects will get a peer-dep warning on install. The two constants are intentionally independent (different packages) but a TODO/link to #3488 on RSC_REACT_VERSION_RANGE would prevent the stable-bump commit from forgetting to update both.

Minor issues

Method names still say "webpack" but now generate bundler-dependent code
rsc_webpack_plugin_import_statement, inject_rsc_webpack_plugin_import, and update_existing_rsc_webpack_config (all in client_references.rb) now produce either a webpack or an rspack import depending on context. The names are misleading for anyone reading the rspack branch. Renaming is out of scope here — flagging as tech debt to track under #3488.

Verification checks use String#include? rather than the shared idempotency regex
check_rsc_server_config / check_rsc_client_config in rsc_setup.rb call content.include?(rsc_plugin_class_name) to decide whether the plugin is present. RSC_PLUGIN_INVOCATION_REGEX already matches both names and tolerates whitespace variants; using it here would be more consistent and would correctly handle a file that contains the other bundler's plugin (e.g. an rspack project whose config was previously scaffolded for webpack).

Positives

  • Single source of truth via the two new helper methods is the right abstraction.
  • The broadened RSC_PLUGIN_INVOCATION_REGEX is correct and its rationale is well-documented.
  • The TemplateRenderContext delegation and the regression test that covers it catch a real silent failure mode.
  • The A/B evidence for why the native plugin is necessary is documented in both the PR body and the updated docs.
  • Test coverage of the no-duplicate-import path for the rspack case is exactly what's needed.

Comment thread react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb Outdated
Comment thread react_on_rails/lib/generators/react_on_rails/rsc_setup.rb
Comment thread react_on_rails/lib/generators/react_on_rails/rsc_setup/client_references.rb Outdated

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

Copy link
Copy Markdown

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: a8a9f9f6ee

ℹ️ 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".

Comment thread react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb Outdated
Comment thread react_on_rails/lib/generators/react_on_rails/rsc_setup/client_references.rb Outdated
Comment thread react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb Outdated
Comment thread react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb Outdated
@claude

claude Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Code Review: feat(generator): scaffold native RSCRspackPlugin for rspack RSC (#3488)

Overview

This PR is a clean, well-scoped change that switches rspack RSC projects from scaffolding RSCWebpackPlugin (which ran under Rspack's webpack-compat layer and failed ~7/11 routes at runtime) to the native RSCRspackPlugin. Webpack projects are entirely unchanged. The two shared helpers (rsc_plugin_class_name / rsc_plugin_import_path) serve as a single source of truth across templates, the standalone RSC migration, and the cleanup renderer.

Architecture / Correctness

Normalization flow is correctly ordered. update_server/client_webpack_config_for_rsc (which calls normalize_rsc_plugin_for_active_bundler) runs at lines 315–316, before verify_rsc_webpack_transforms at line 318. So by the time the check methods look for rsc_plugin_class_name, the file has already been normalized. The check_rsc_server/client_config checking only the active plugin name is therefore sound.

Idempotency guard is correct. Broadening RSC_PLUGIN_INVOCATION_REGEX to RSC(?:Webpack|Rspack)Plugin is the right call — without it, a re-run on a freshly-scaffolded rspack project that already has new RSCRspackPlugin( would miss the existing invocation and inject a duplicate import. The new context covering this scenario is a good regression guard.

Normalization bails consistently when ESM is used. When normalize_rsc_plugin_import_for_active_bundler can't find a CommonJS require for the inactive plugin, active_plugin_binding_available is false and invocation renaming is also skipped. This is intentional and the test "does not rename a legacy plugin invocation when the import form cannot be normalized" correctly verifies that both halves of the migration are skipped together.

Issues Found

Three inline comments have been posted:

  1. Regexp.escape inconsistency (line 172, client_references.rb): The gsub inside normalize_rsc_plugin_import_for_active_bundler interpolates inactive_plugin_class_name into a regex without Regexp.escape, while the sibling method at line 208 wraps the same variable with it. No current bug (names are alphanumeric), but the inconsistency is a maintenance hazard. One-line fix suggested.

  2. Warning concatenation fragility (lines 424–425, js_dependency_manager.rb): rspack_rsc_dependency_fallback_warning returns either "" or a space-prefixed string — the leading space is implicitly required by the surrounding string concatenation. A future change to the method that drops that space would silently fuse two sentences. Suggested making ownership explicit at the call site.

  3. RSC_REACT_VERSION_RANGE vs RSC_PACKAGE_VERSION_PIN gap (line 152, js_dependency_manager.rb): The range (~19.0.4) and the pin (19.0.5-rc.5) now track different versions. This is intentional per the TODO (the range governs the React peer dep, not react-on-rails-rsc), but a reader scanning the block cold will be confused. Suggested a one-line clarifying comment.

RC Version Pin

Shipping 19.0.5-rc.5 in the generator is a known trade-off: the /RspackPlugin export doesn't exist in 19.0.4. The PR documents this clearly, the TODO points to the right follow-up, and the fallback warning for rspack users covers the install-failure case. Acceptable given the explicit tracking in #3488.

Test Coverage

Coverage is thorough: helper contract, fresh-install scaffolding (webpack and rspack), normalization of legacy webpack configs on rspack projects, dedup/idempotency, the cleanup re-render regression, and the dependency pin. No gaps noticed.

Summary

All three findings are minor (two style, one doc). The core logic is correct and well-tested. The PR is ready to merge once the inline nits are addressed (or explicitly waived).

🤖 Generated with Claude Code

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@react_on_rails/lib/generators/react_on_rails/rsc_setup/client_references.rb`:
- Around line 160-176: normalize_rsc_plugin_import_for_active_bundler can remove
an earlier inactive import while an active import appears later, causing a TDZ;
fix by computing the actual position of any existing active import (search
content with rsc_plugin_commonjs_import_regex(rsc_plugin_import_path) to get its
match.begin(0)) and only remove the inactive binding when that active import
occurs before the current inactive statement (i.e., active_pos <
Regexp.last_match.begin(0)). If the active import exists but is after the
current inactive match, instead rewrite the current (earlier) inactive statement
in-place to the active class/path and mark that position as the active import so
the later duplicate can be removed; keep updates localized to
normalize_rsc_plugin_import_for_active_bundler and coordinate with
normalize_rsc_plugin_invocations_for_active_bundler via the unchanged return
value ([normalized_content, active_import_seen]).
🪄 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: 8f196c73-13e8-443e-9e91-8fa5a5628d26

📥 Commits

Reviewing files that changed from the base of the PR and between 2e26063 and a8a9f9f.

📒 Files selected for processing (6)
  • docs/pro/react-server-components/rspack-compatibility.md
  • react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb
  • react_on_rails/lib/generators/react_on_rails/rsc_setup.rb
  • react_on_rails/lib/generators/react_on_rails/rsc_setup/client_references.rb
  • react_on_rails/spec/react_on_rails/generators/js_dependency_manager_spec.rb
  • react_on_rails/spec/react_on_rails/generators/rsc_generator_spec.rb
✅ Files skipped from review due to trivial changes (1)
  • docs/pro/react-server-components/rspack-compatibility.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • react_on_rails/spec/react_on_rails/generators/js_dependency_manager_spec.rb
  • react_on_rails/lib/generators/react_on_rails/rsc_setup.rb

Comment thread react_on_rails/lib/generators/react_on_rails/rsc_setup/client_references.rb Outdated
@claude

claude Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Code Review: feat(generator) scaffold native RSCRspackPlugin for rspack RSC (#3488)

Overview

This PR correctly addresses the core problem: Rspack's webpack-compatibility layer produced valid-looking manifests that still broke ~7/11 RSC routes at runtime, while the native RSCRspackPlugin works. The approach—single-source-of-truth helpers (rsc_plugin_class_name / rsc_plugin_import_path) driving both templates and the standalone migration—is the right architecture. Test coverage is thorough: dedup detection, idempotency, normalization of legacy imports, and cleanup re-render delegation all have spec coverage.


Issues

1. RSC_REACT_VERSION_RANGE doesn't cover the RC pin (minor, but observable)

# js_dependency_manager.rb
RSC_REACT_VERSION_RANGE = "~19.0.4"          # unchanged
RSC_PACKAGE_VERSION_PIN  = "19.0.5-rc.5"     # new

In npm semver, ~19.0.4 resolves to >=19.0.4 <19.1.0 for stable releases, but pre-release versions are excluded unless the range itself carries a pre-release tag. Any caller comparing the installed 19.0.5-rc.5 against ~19.0.4 via a pre-release-aware semver library would consider it out-of-range.

The TODO comment acknowledges this needs bumping when 19.0.5 stable ships, but during the RC window any tooling that validates the range could produce spurious compatibility warnings. Either bump the range to ~19.0.5-rc.5 now (stable 19.0.5 satisfies that too) or document why the range intentionally lags the pin.

2. check_rsc_server_config / check_rsc_client_config diverge from the routing check (minor UX gap)

The routing check (rsc_plugin_invocation_in_js_code?) uses RSC_PLUGIN_INVOCATION_REGEX which matches both RSCWebpackPlugin and RSCRspackPlugin. The verification helpers use:

if content.include?(rsc_plugin_class_name)   # active bundler's plugin only

If normalization silently skips a file (e.g., the user wrote an ESM import instead of a CommonJS require), the verification helper would report "missing RSCRspackPlugin" when in fact RSCWebpackPlugin is present. A more actionable message would say "found RSCWebpackPlugin but this is an rspack project — please replace with RSCRspackPlugin." This is a pre-existing pattern, but more likely to surface now that the normalizer can non-destructively skip files it cannot safely rewrite.

3. Warning string coupling between concatenation and method return value (micro-nit)

# js_dependency_manager.rb add_rsc_dependencies
"The installed react-on-rails-rsc version may not match the expected compatibility pin." \
"#{rspack_rsc_dependency_fallback_warning}"

rspack_rsc_dependency_fallback_warning begins with " Rspack RSC projects..." (leading space). For non-rspack this collapses cleanly. For rspack the result is "...pin. Rspack RSC projects..." — correct, but only because the leading space in the return value happens to be there. This implicit contract is fragile. Better to own the space at the call site and strip it from the method:

"The installed react-on-rails-rsc version may not match the expected compatibility pin. " \
"#{rspack_rsc_dependency_fallback_warning.lstrip}"

4. remove_commonjs_named_import_binding silently erases the line on parse failure

def remove_commonjs_named_import_binding(statement, binding_name)
  match = statement.match(...)
  return "" unless match   # <-- whole line deleted if regex doesn't match
  ...
end

For generator-produced files this regex will always match. But for hand-edited files (e.g., const { RSCWebpackPlugin, SomethingElse } = require(...) with unusual spacing), returning "" silently destroys the import. Returning statement unchanged on parse failure would be safer and still leave the file in a valid state.


Minor Observations

  • normalize_rsc_plugin_invocations_for_active_bundler offset handling is correct. The character delta between RSCWebpackPlugin (16) and RSCRspackPlugin (15) shifts subsequent match positions by -1, but since search_from is reset to match.begin(0) + replacement.length after each in-place replacement and the next normalized_content.match(pattern, search_from) operates on the already-mutated string, positions are always fresh. This is correct.
  • ESM-import guard is correctly scoped. Not renaming invocations when only an ESM import (not a CommonJS require) is found avoids producing a file with a mismatched binding name and import. The test that covers this case is exactly right.
  • The rscWebpackConfig.js.tt comment change from "skip RSCWebpackPlugin" to "skip the RSC manifest plugin" is cleaner and correct.

What's Right

  • Single source of truth via rsc_plugin_class_name / rsc_plugin_import_path delegated through TemplateRenderContext is the correct abstraction.
  • Broadening RSC_PLUGIN_INVOCATION_REGEX to /new\s+RSC(?:Webpack|Rspack)Plugin\s*\(/ ensures idempotency during re-run regardless of which plugin was written first.
  • The normalization path handles five distinct cases (no existing import, active import only, inactive import only, both imports with active first, both imports with active second) and each has a dedicated spec.
  • The test asserting RSCWebpackPlugin does not leak into an rspack config is exactly the right regression guard.
  • Keeping "experimental" on the docs while Rspack RSC: path to production-ready — pivot to native RSCRspackPlugin #3488 is still open is the correct call.

Recommendation

Approve with the version range note addressed. The RC-vs-range inconsistency (RSC_REACT_VERSION_RANGE = "~19.0.4" vs pin "19.0.5-rc.5") is the only issue likely to surface in practice. Everything else is minor polish. The follow-up on the RSC bundle loader strategy (enforce: 'post') is correctly deferred to #3488.

Comment thread react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb Outdated
Comment thread react_on_rails/lib/generators/react_on_rails/rsc_setup/client_references.rb Outdated
@justin808 justin808 force-pushed the jg-conductor/3488-rspack-rsc-native-plugin branch from c6ae519 to 72d8b47 Compare June 5, 2026 02:48

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

Copy link
Copy Markdown

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: 72d8b47afc

ℹ️ 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".

@claude

claude Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Code Review

Overview

This PR introduces a clean, well-structured swap from RSCWebpackPlugin to the native RSCRspackPlugin for Rspack RSC projects. The core design is sound: a single source of truth in GeneratorHelper (rsc_plugin_class_name / rsc_plugin_import_path) drives both fresh installs and standalone migrations, and the bidirectional normalization in client_references.rb correctly handles legacy configs.

Strengths

  • Single source of truthrsc_plugin_class_name / rsc_plugin_import_path in GeneratorHelper eliminate scattered string literals. All downstream callers (templates, migration, verification, warnings) stay consistent.
  • Safe invocation-rename guardnormalize_rsc_plugin_for_active_bundler only renames new RSCWebpackPlugin( to new RSCRspackPlugin( when the import can be confirmed/converted via CommonJS regex; ES module imports cause a silent skip (verified by test). This prevents a runtime ReferenceError from a renamed invocation with no binding.
  • Rspack pin-fallback protection — correctly skips the unversioned latest retry for rspack (which lacks RspackPlugin) while keeping the webpack path unchanged.
  • Comprehensive edge case tests — the new rsc_generator_spec tests cover the mixed state (native already present), ES module import (no-op), commented legacy import (no-op), and dedup scenarios.

Issues

See inline comments for specific line references. Summary:

  1. Stale docstring in rsc_plugin_option_sections_partition — still says RSCWebpackPlugin( but the regex now matches both plugin names.
  2. Safety check gates normalizationrsc_plugin_sections_safe_to_rewrite? blocks the plugin rename when options are unparseable, even though a bare class/import rename is safe. Consider separating normalization from the clientReferences safety check.
  3. Rollback semantics — Normalization is committed independently of clientReferences; if clientReferences migration fails, the plugin rename is kept. Intentional and safe, but non-obvious; a comment would help.
  4. inactive_rsc_plugin_* helpers untested in isolation — exercised transitively, but no contract test in generator_helper_spec.
  5. Multi-line CommonJS destructuring silently skipped[^}]* in rsc_plugin_commonjs_import_regex does not span newlines; worth a brief note.
  6. npm install hardcoded in warning — pre-existing pattern; worth a follow-up for yarn/pnpm/bun users.

Overall this is well-reasoned, well-tested, and safe. Items 1 and 2 are the most actionable before merge.

Comment thread react_on_rails/lib/generators/react_on_rails/rsc_setup/client_references.rb Outdated
@claude

claude Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Code Review

Overall: Solid, well-structured PR. The core design (centralize bundler choice through rsc_plugin_class_name/rsc_plugin_import_path helpers, handle migration conservatively, skip unversioned fallback for rspack) is sound. The test coverage is thorough and the rollback story is well-reasoned. A few issues worth addressing before merge.


Issues

1. Misleading comment — [^}]* in rsc_plugin_commonjs_import_regex spans newlines in Ruby

rsc_setup/client_references.rb ~line 198–199:

# Intentionally matches single-line destructuring only. Multi-line CommonJS destructuring is
# skipped safely because the lightweight regexp does not span newlines.

This is incorrect. In Ruby, [^}] (any char except }) matches newlines — Ruby's single-line restriction only applies to .. So \{[^}]*\} will happily match a multi-line destructuring like:

const {
  RSCWebpackPlugin
} = require('...');

The comment should either be corrected (it does match across lines if no } appears inside the destructuring) or the regex should use [^}\n] to enforce the stated intent. In practice the mis-match is harmless for normal configs, but the documentation is wrong and could mislead future maintainers.

2. Redundant parameters on normalize_rsc_plugin_import_for_active_bundler

The method signature is:

def normalize_rsc_plugin_import_for_active_bundler(content, inactive_plugin_class_name, inactive_plugin_import_path)

The sole call-site always passes the module-level helper values directly:

normalize_rsc_plugin_import_for_active_bundler(
  content,
  inactive_rsc_plugin_class_name,
  inactive_rsc_plugin_import_path
)

These parameters add indirection without enabling any new testability — the method already calls rsc_plugin_class_name/rsc_plugin_import_path directly. The signature could be simplified to (content) with direct use of the module helpers, removing 3 places where the values are threaded through.

3. Verify path has a gap when BOTH active + inactive plugin symbols coexist

rsc_setup.rb check_rsc_server_config / check_rsc_client_config:

if content.include?(rsc_plugin_class_name)
  # happy path – checks clientReferences
elsif content.include?(inactive_rsc_plugin_class_name)
  missing << "#{rsc_plugin_class_name} in ... (found #{inactive_rsc_plugin_class_name} — wrong bundler plugin)"
else
  missing << "#{rsc_plugin_class_name} in ..."
end

If a file contains both RSCRspackPlugin and RSCWebpackPlugin (e.g. user ran an older generator then manually half-migrated), the if branch is taken and the stale RSCWebpackPlugin entry is silently ignored. The migration path (normalize_rsc_plugin_for_active_bundler) cleans this up before the verify check runs in a fresh generator run, so the gap is only observable when check_rsc_*_config is called independently. Low risk today, but worth noting.

4. Minor: rspack_rsc_dependency_pin_failed_warning says "or newer" but the guard is pin-specific

"Rspack RSC projects require that version (or newer) for react-on-rails-rsc/RspackPlugin, " \

The claim "or newer" is accurate — RspackPlugin is also in stable 19.0.5 — but then the manual instruction recommends the exact RC pin. Once 19.0.5 stable ships, the instruction will correctly point at stable; before then, a user reading "or newer" may try @latest (which currently lacks RspackPlugin). Consider tightening to "require at least 19.0.5-rc.6" or just dropping "or newer" until the stable ships.


Minor nits

  • rsc_setup.rb:306–307 comment header updated correctly from serverWebpackConfig.jsserverWebpackConfig.js: RSC plugin import (RSCRspackPlugin/RSCWebpackPlugin) — good.
  • The TemplateRenderContext delegation in base_generator.rb and the regression spec in install_generator_spec.rb are a nice safety net for the cleanup-render path — appreciated.
  • The RSC_PLUGIN_INVOCATION_REGEX broadening to /new\s+RSC(?:Webpack|Rspack)Plugin\s*\(/ is clean and the spec covering the "duplicate import" dedup path for rspack explicitly is exactly right.

Tests

Coverage is excellent: fresh install (rspack + webpack), standalone migration, dedup paths, ESM-import warning, wrong-bundler-plugin detection, pin-failure paths, and the TemplateRenderContext regression guard. No gaps identified beyond the dual-plugin verify case above.

Comment thread react_on_rails/lib/generators/react_on_rails/rsc_setup.rb
@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Pro Node Renderer Benchmark Summary

Benchmark RPS p50(ms) p90(ms) Fail% Status
Pro Node Renderer: simple_eval (non-RSC) 2166.94 4.15 5.34 0.0 200=65015
Pro Node Renderer: react_ssr (non-RSC) 1929.87 4.66 6.15 0.0 200=57899

🔴 significant regression · 🟢 significant improvement (vs baseline; tracked measures only)

@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Core Benchmark Summary

Benchmark RPS p50(ms) p90(ms) Fail% Status
/: Core 2.8 2887.82 3549.5 0.0 200=95
/client_side_hello_world: Core 705.42 11.41 20.03 0.0 200=21309
/client_side_rescript_hello_world: Core 481.52 11.99 21.03 0.0 200=14548
/client_side_hello_world_shared_store: Core 682.68 9.27 24.13 0.0 200=20623
/client_side_hello_world_shared_store_controller: Core 675.16 9.7 19.64 0.0 200=20398
/client_side_hello_world_shared_store_defer: Core 662.41 12.32 21.69 0.0 200=20011
/server_side_hello_world_shared_store: Core 12.34 678.95 849.16 0.0 200=380
/server_side_hello_world_shared_store_controller: Core 12.38 675.06 855.42 0.0 200=381
/server_side_hello_world_shared_store_defer: Core 12.26 492.23 811.0 0.0 200=382
/server_side_hello_world: Core 25.54 354.18 392.52 0.0 200=776
/server_side_hello_world_hooks: Core 17.88 338.51 370.16 0.0 200=548
/server_side_hello_world_props: Core 25.34 337.3 393.05 0.0 200=769
/client_side_log_throw: Core 714.79 9.22 18.68 0.0 200=21593
/server_side_log_throw: Core 24.76 337.8 406.15 0.0 200=755
/server_side_log_throw_plain_js: Core 24.98 364.17 402.28 0.0 200=760
/server_side_log_throw_raise: Core 24.96 341.04 397.68 0.0 3xx=762
/server_side_log_throw_raise_invoker: Core 822.51 8.31 15.71 0.0 200=24849
/server_side_hello_world_es5: Core 25.61 329.7 392.89 0.0 200=777
/server_side_redux_app: Core 24.28 339.69 404.7 0.0 200=742
/server_side_hello_world_with_options: Core 25.47 331.78 390.1 0.0 200=777
/server_side_redux_app_cached: Core 715.42 10.32 18.21 0.0 200=21613
/client_side_manual_render: Core 685.51 4.33 16.78 0.0 200=20851
/render_js: Core 26.96 314.28 372.93 0.0 200=819
/react_router: Core 24.17 342.98 415.45 0.0 200=735
/pure_component: Core 25.53 324.16 386.57 0.0 200=778
/css_modules_images_fonts_example: Core 25.53 331.16 391.78 0.0 200=775
/turbolinks_cache_disabled: Core 709.6 9.38 18.13 0.0 200=21438
/rendered_html: Core 19.08 308.0 370.81 0.0 200=584
/xhr_refresh: Core 12.97 650.88 888.28 0.0 200=396
/react_helmet: Core 25.04 365.78 400.49 0.0 200=760
/broken_app: Core 24.79 342.08 400.59 0.0 200=754
/image_example: Core 25.09 362.65 401.09 0.0 200=762
/turbo_frame_tag_hello_world: Core 781.12 8.54 16.72 0.0 200=23597
/manual_render_test: Core 721.28 9.08 18.29 0.0 200=21792

🔴 significant regression · 🟢 significant improvement (vs baseline; tracked measures only)

@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Pro (shard 2/2) Benchmark Summary

Benchmark RPS p50(ms) p90(ms) Fail% Status
/empty: Pro 1063.64 5.42 13.25 0.0 200=32129
/ssr_shell_error: Pro 308.5 18.75 57.17 0.0 200=9322
/ssr_sync_error: Pro 357.75 21.4 35.11 0.0 200=10812
/rsc_component_error: Pro 296.57 25.95 41.61 0.0 200=8963
/non_existing_stream_react_component: Pro 313.91 24.19 36.72 0.0 200=9490
/server_side_redux_app_cached: Pro 389.89 19.95 29.67 0.0 200=11783
/loadable: Pro 339.75 23.16 34.13 0.0 200=10268
/apollo_graphql: Pro 123.97 47.5 120.25 0.0 200=3748
/console_logs_in_async_server: Pro 3.16 2121.85 2149.51 0.0 200=108
/stream_error_demo: Pro 360.45 21.39 32.44 0.0 200=10892
/stream_async_components: Pro 368.71 23.64 32.41 0.0 200=11139
/rsc_posts_page_over_http: Pro 369.82 20.86 33.9 0.0 200=11176
/rsc_echo_props: Pro 192.1 29.97 36.57 0.0 200=5807
/async_on_server_sync_on_client_client_render: Pro 397.58 19.26 32.2 0.0 200=12016
/server_router_client_render: Pro 381.17 19.96 32.16 0.0 200=11523
/unwrapped_rsc_route_stream_render: Pro 385.41 19.98 34.15 0.0 200=11644
/async_render_function_returns_component: Pro 372.51 20.93 30.59 0.0 200=11259
/native_metadata: Pro 287.19 20.23 31.7 0.0 200=8679
/hybrid_metadata_streaming: Pro 351.08 21.95 32.93 0.0 200=10606
/cache_demo: Pro 341.4 18.42 31.65 0.0 200=10318
/client_side_hello_world_shared_store: Pro 319.86 17.35 32.84 0.0 200=9670
/client_side_hello_world_shared_store_defer: Pro 330.55 22.52 37.15 0.0 200=9988
/server_side_hello_world_shared_store_controller: Pro 276.64 28.55 40.48 0.0 200=8364
/server_side_hello_world: Pro 333.51 23.29 34.05 0.0 200=10077
/client_side_log_throw: Pro 307.99 18.76 28.59 0.0 200=9308
/server_side_log_throw_plain_js: Pro 352.31 22.31 31.89 0.0 200=10645
/server_side_log_throw_raise_invoker: Pro 414.67 18.53 27.96 0.0 200=12449
/server_side_redux_app: Pro 351.61 22.0 32.99 0.0 200=10626
/server_side_redux_app_cached: Pro 382.66 20.44 29.52 0.0 200=11562
/render_js: Pro 385.21 19.58 31.13 0.0 200=11642
/pure_component: Pro 363.45 21.53 33.54 0.0 200=10983
/turbolinks_cache_disabled: Pro 331.99 17.29 27.75 0.0 200=10031
/xhr_refresh: Pro 288.48 26.97 40.39 0.0 200=8719
/broken_app: Pro 343.05 22.53 36.7 0.0 200=10367
/server_render_with_timeout: Pro 324.7 24.1 34.94 0.0 200=9809

🔴 significant regression · 🟢 significant improvement (vs baseline; tracked measures only)

@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Pro (shard 1/2) Benchmark Summary

Benchmark RPS p50(ms) p90(ms) Fail% Status
/: Pro 179.39 49.51 62.54 0.0 200=5391
/error_scenarios_hub: Pro 318.82 17.99 25.27 0.0 200=9636
/ssr_async_error: Pro 302.58 19.0 60.89 0.0 200=9143
/ssr_async_prop_error: Pro 339.1 18.55 31.09 0.0 200=10249
/non_existing_react_component: Pro 359.52 21.85 34.74 0.0 200=10864
/non_existing_rsc_payload: Pro 370.33 20.55 31.77 0.0 200=11203
/cached_react_helmet: Pro 320.42 17.84 50.71 0.0 200=9683
/cached_redux_component: Pro 399.34 21.86 29.12 0.0 200=11991
/lazy_apollo_graphql: Pro 134.14 44.05 108.84 0.0 200=4060
/redis_receiver: Pro 88.31 58.9 193.11 0.0 200=2671
/stream_shell_error_demo: Pro 367.1 20.97 31.5 0.0 200=11090
/test_incremental_rendering: Pro 361.26 21.67 34.06 0.0 200=10914
/rsc_posts_page_over_redis: Pro 98.98 74.11 112.81 0.0 200=2994
/async_on_server_sync_on_client: Pro 302.31 15.04 22.42 0.0 200=9139
/server_router: Pro 304.27 18.85 73.51 0.0 200=9194
/unwrapped_rsc_route_client_render: Pro 413.28 18.5 27.18 0.0 200=12489
/async_render_function_returns_string: Pro 313.01 18.16 43.84 0.0 200=9459
/async_components_demo: Pro 223.64 36.37 50.61 0.0 200=6757
/stream_native_metadata: Pro 383.13 20.17 32.61 0.0 200=11578
/rsc_native_metadata: Pro 364.6 20.59 32.15 0.0 200=11041
/client_side_hello_world: Pro 311.16 14.91 24.86 0.0 200=9468
/client_side_hello_world_shared_store_controller: Pro 222.08 26.03 35.46 0.0 200=6718
/server_side_hello_world_shared_store: Pro 230.69 34.56 50.97 0.0 200=6972
/server_side_hello_world_shared_store_defer: Pro 199.09 28.93 64.62 0.0 200=6017
/server_side_hello_world_hooks: Pro 292.98 25.22 39.95 0.0 200=8857
/server_side_log_throw: Pro 315.79 24.51 39.83 0.0 200=9544
/server_side_log_throw_raise: Pro 610.45 12.24 21.18 0.0 3xx=18445
/server_side_hello_world_es5: Pro 286.78 20.01 42.24 0.0 200=8666
/server_side_hello_world_with_options: Pro 339.08 18.6 31.1 0.0 200=10247
/client_side_manual_render: Pro 381.95 20.02 29.96 0.0 200=11542
/react_router: Pro 401.01 18.92 33.51 0.0 200=12116
/css_modules_images_fonts_example: Pro 346.76 16.09 30.78 0.0 200=10482
/rendered_html: Pro 329.66 22.9 36.31 0.0 200=9966
/react_helmet: Pro 207.02 27.88 88.55 0.0 200=6256
/image_example: Pro 202.83 28.22 34.23 0.0 200=6171
/posts_page: Pro 52.49 🔴 106.46 🔴 212.98 0.0 200=1592

🔴 significant regression · 🟢 significant improvement (vs baseline; tracked measures only)

@justin808

Copy link
Copy Markdown
Member Author

CI is green and the remaining review comments are polish/follow-up coverage items. I tracked them in #3646 so this can merge without restarting CI for nits.

@justin808 justin808 merged commit 37a5b69 into main Jun 5, 2026
47 checks passed
@justin808 justin808 deleted the jg-conductor/3488-rspack-rsc-native-plugin branch June 5, 2026 08:09
justin808 added a commit that referenced this pull request Jun 5, 2026
Resolve conflicts after #3590/#3616 and keep the RSC manifest discovery branch on the published rc.6 dependency path.

Also make the discovery-plugin missing-module error version-agnostic and keep the existing-helper migration path on the current bundler-neutral import helper.
justin808 added a commit that referenced this pull request Jun 5, 2026
## Summary

- generate an RSC-only server component registration entry from the pack
generator
- run an RSC reference discovery build from the precompile hook
- have Pro client/server manifest configs consume the discovered refs
JSON instead of broad filesystem scanning
- update the Pro dummy build scripts to run the precompile hook before
bundling
- consume released `react-on-rails-rsc@19.0.5-rc.6` from npm
- remove the old fork/patch path; rc.6 already includes the CSS manifest
metadata fix needed here
- upgrade existing broad `rscClientReferences` helpers to the
manifest-backed helper during migration
- keep discovery builds isolated from stale manifests and bundle-only
env leakage

Supports #3553.

RSC package dependency: stay on `react-on-rails-rsc@19.0.5-rc.6`. I
found no current gap that requires an rc.7. This PR should land after
#3590 and preferably after #3616 so the generator and optional
peer-range story are already in `main`.

## Architecture decisions

- The generated helper intentionally uses the discovery manifest as the
source of truth, then falls back to broad scanning only when the app has
not generated a manifest yet or the installed RSC setup does not expose
discovery support.
- Existing broad object-literal `rscClientReferences` helpers are not
treated as fully migrated. The generator upgrades them to the
manifest-backed helper so upgraded apps get exact RSC graph references
instead of silently keeping broad scanning.
- The generated `fileContainsAll` checks read each inspected file once
per check. This avoids repeated disk reads without memoizing across
long-running webpack config evaluation, where setup files may change
while a developer is iterating.
- `build:dev:watch` deletes stale development webpack output and
`ssr-generated` before running the precompile hook. It deliberately does
not delete production output.
- The shared and standalone precompile-hook paths now preserve failure
backtrace context consistently.

## Verification

Review-fix commit `9d414eabe530902c8b5073a92b69f61dc4f40501`:

- `pnpm install --frozen-lockfile`
- `bundle exec rubocop
react_on_rails/lib/generators/react_on_rails/rsc_setup/client_references.rb
react_on_rails/spec/react_on_rails/generators/rsc_generator_spec.rb
react_on_rails/spec/react_on_rails/shakapacker_precompile_hook_shared_spec.rb
react_on_rails/spec/support/shakapacker_precompile_hook_shared.rb`
- `pnpm exec prettier --check
react_on_rails_pro/spec/dummy/tests/rsc-manifest-client-references.test.js
react_on_rails_pro/spec/dummy/tests/package-scripts.test.js
react_on_rails_pro/spec/dummy/config/webpack/rscManifestClientReferences.js
react_on_rails_pro/spec/dummy/package.json`
- `bundle exec rspec
react_on_rails/spec/react_on_rails/generators/rsc_generator_spec.rb`
- `bundle exec rspec
react_on_rails/spec/react_on_rails/shakapacker_precompile_hook_shared_spec.rb`
- `pnpm --dir react_on_rails_pro/spec/dummy run test:js -- --runInBand
tests/rsc-manifest-client-references.test.js
tests/package-scripts.test.js`
- `git diff --check`

Earlier implementation verification retained from the original PR:

- `cd react_on_rails/spec/dummy && bundle exec rspec
spec/packs_generator_spec.rb`
- `bundle exec rspec spec/requests/rsc_use_client_css_manifest_spec.rb`
from `react_on_rails_pro/spec/dummy`
- `NODE_CONDITIONS=react-server pnpm --dir packages/react-on-rails-pro
exec jest tests/manifestLoaderServerRSC.rsc.test.ts --runInBand`
- `pnpm --dir packages/react-on-rails-pro exec jest
tests/manifestLoader.test.ts tests/resolveCssHrefs.test.ts --runInBand`
- `pnpm run lint`
- `pnpm start format.listDifferent`
- `bundle exec rubocop` from `react_on_rails/`
- `bundle exec rubocop --ignore-parent-exclusion` from
`react_on_rails_pro/`
- `bundle exec rubocop benchmarks`
- `pnpm list react-on-rails-rsc --depth 0 -r`
- `pnpm run build:dev` from `react_on_rails_pro/spec/dummy`
- `pnpm run build:test` from `react_on_rails_pro/spec/dummy`
- `pnpm run build:client` from `react_on_rails_pro/spec/dummy`
- `pnpm run build:server` from `react_on_rails_pro/spec/dummy`

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

* **New Features**
* Integrates RSC client-reference discovery into the precompile/build
flow to emit a client-reference manifest for bundling.

* **Bug Fixes**
* Resolves RSC CSS behavior behind "use client" boundaries by consuming
the upstream prerelease.

* **Chores**
* Bumped RSC package to 19.0.5-rc.6 and removed the local patch
override.
* Expanded tests and CI to cover RSC discovery, manifest handling, and
precompile hook behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

## Follow-up

- #3642 tracks non-blocking review nits around PacksGenerator scan
reuse, shared-hook traversal hardening, CSS prefix comments, discovery
support memoization/documentation, and post-stable prerelease cleanup.

<!-- codex-review-followups-start -->

### Review follow-ups

Non-blocking review nits are tracked in #3642, including unresolved
threads `PRRT_kwDOAnNnU86HSorq`, `PRRT_kwDOAnNnU86HSpqC`, and
`PRRT_kwDOAnNnU86HSp95`. The two version-specific discovery-plugin
error-message threads were addressed in the latest push and may remain
visible only as outdated unresolved threads until reviewer cleanup.

<!-- codex-review-followups-end -->

---------

Co-authored-by: Justin Gordon <justin@shakacode.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant