Skip to content

Commit e88362f

Browse files
committed
Generate Rspack RSC manifests without webpack plugin
1 parent 1e134c8 commit e88362f

11 files changed

Lines changed: 420 additions & 93 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ After a release, run `/update-changelog` in Claude Code to analyze commits, writ
3030

3131
#### Fixed
3232

33+
- **[Pro]** **Rspack RSC manifests generated without webpack-only plugin APIs**: RSC-enabled Rspack generator output now includes `rscManifestPlugin.js`, which scans Shakapacker source paths for `'use client'` modules, adds them to the client/server bundle graphs, and emits `react-client-manifest.json` plus `react-server-client-manifest.json` without applying `react-on-rails-rsc/WebpackPlugin` to the Rspack compiler. This avoids the Rspack crash on webpack-only compilation APIs and prevents the Pro Node Renderer from uploading missing RSC manifests. Fixes [Issue 1828](https://github.com/shakacode/react_on_rails/issues/1828).
3334
- **[Pro]** **Preserve ruby-jwt 2.x compatibility in 16.7**: Relaxes the `16.7.0.rc.0` `jwt >= 3.2.0` floor to `jwt >= 2.7`, keeping compatibility with apps that still resolve jwt 2.x while continuing to allow patched jwt 3.2.0+ releases. [PR 3344](https://github.com/shakacode/react_on_rails/pull/3344) by [ihabadham](https://github.com/ihabadham).
3435
- **[Pro]** **RSC client manifest restored when only `registerServerComponent/client` is in the pack graph**: `wrapServerComponentRenderer/client` now directly imports `react-on-rails-rsc/client.browser` as a side-effect import. Previously the client runtime was only reachable through a three-level transitive chain (`wrapServerComponentRenderer/client``getReactServerComponent.client``react-on-rails-rsc/client.browser`). Tooling that severed any link in that chain (tree-shaking, transpiler quirks, custom `NormalModuleReplacement`, externals) caused `RSCWebpackPlugin` to emit `Client runtime at react-on-rails-rsc/client was not found. React Server Components module map file react-client-manifest.json was not created.` and silently skip the manifest, breaking RSC hydration on the Pro Node Renderer. The direct import keeps the runtime resource in the module graph so the plugin always emits `react-client-manifest.json`. Fixes [#3366](https://github.com/shakacode/react_on_rails/issues/3366).
3536

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Rspack Compatibility with React Server Components
22

3-
> **Status**: Experimental — generator support is complete; runtime verification is in progress.
3+
> **Status**: Experimental — generated Rspack configs now emit the RSC manifests; full Pro Node Renderer hydration verification is still in progress.
44
55
This page documents the compatibility status of [Rspack](https://rspack.dev/) with React on Rails Pro's React Server Components (RSC) implementation.
66

@@ -11,25 +11,34 @@ The generator already supports Rspack — when `assets_bundler: rspack` is detec
1111

1212
The RSC implementation depends on the `react-on-rails-rsc` npm package, which provides:
1313

14-
- **WebpackPlugin** — generates client/server component manifest files
14+
- **WebpackPlugin** — generates client/server component manifest files for webpack builds
1515
- **WebpackLoader** — transforms `'use client'` files into client reference proxies in the RSC bundle
1616

17+
For Rspack builds, generated configs use `config/rspack/rscManifestPlugin.js` instead of applying
18+
`react-on-rails-rsc/WebpackPlugin` directly. The helper scans Shakapacker source paths for `'use client'`
19+
modules, adds those modules to the client and server bundle graphs, and emits the two manifest files
20+
expected by the Pro Node Renderer:
21+
22+
- `react-client-manifest.json`
23+
- `react-server-client-manifest.json`
24+
1725
## Compatibility Matrix
1826

19-
| Component | Rspack Compatible | Notes |
20-
| ---------------------------------------------------------------- | ------------------ | ------------------------------------------------------------------------------- |
21-
| **RSC bundle config** (`rscWebpackConfig.js`) | Yes | Does not use WebpackPlugin; only uses loader + resolve settings |
22-
| **WebpackLoader** (`react-on-rails-rsc/WebpackLoader`) | Yes | Standard loader interface (`this.resourcePath`, source transform) |
23-
| **`conditionNames: ['react-server', '...']`** | Yes | Rspack supports conditional exports resolution |
24-
| **`resolve.alias`** (`react-dom/server: false`) | Yes | Rspack supports alias to `false` |
25-
| **`LimitChunkCountPlugin`** | Yes | Generated configs use bundler-agnostic `bundler.optimize.LimitChunkCountPlugin` |
26-
| **Loader chain (SWC + Babel)** | Yes | Generated config handles both `function` and `Array` `rule.use` styles |
27-
| **WebpackPlugin** (`react-on-rails-rsc/WebpackPlugin`) | Needs verification | Uses webpack internal APIs (see below) |
28-
| **Three-bundle build** (`RSC_BUNDLE_ONLY`, `SERVER_BUNDLE_ONLY`) | Yes | Environment variable routing is bundler-agnostic |
27+
| Component | Rspack Compatible | Notes |
28+
| ---------------------------------------------------------------- | ----------------- | ------------------------------------------------------------------------------- |
29+
| **RSC bundle config** (`rscWebpackConfig.js`) | Yes | Does not use WebpackPlugin; only uses loader + resolve settings |
30+
| **WebpackLoader** (`react-on-rails-rsc/WebpackLoader`) | Yes | Standard loader interface (`this.resourcePath`, source transform) |
31+
| **`conditionNames: ['react-server', '...']`** | Yes | Rspack supports conditional exports resolution |
32+
| **`resolve.alias`** (`react-dom/server: false`) | Yes | Rspack supports alias to `false` |
33+
| **`LimitChunkCountPlugin`** | Yes | Generated configs use bundler-agnostic `bundler.optimize.LimitChunkCountPlugin` |
34+
| **Loader chain (SWC + Babel)** | Yes | Generated config handles both `function` and `Array` `rule.use` styles |
35+
| **WebpackPlugin** (`react-on-rails-rsc/WebpackPlugin`) | Webpack only | Uses webpack internal APIs (see below) |
36+
| **Rspack manifest helper** (`rscManifestPlugin.js`) | Yes | Emits the Pro RSC client/server manifests without webpack-only plugin APIs |
37+
| **Three-bundle build** (`RSC_BUNDLE_ONLY`, `SERVER_BUNDLE_ONLY`) | Yes | Environment variable routing is bundler-agnostic |
2938

3039
## WebpackPlugin Compatibility Details
3140

32-
The `RSCWebpackPlugin` is the critical compatibility question. It wraps React's
41+
The `RSCWebpackPlugin` remains the webpack path. It wraps React's
3342
`react-server-dom-webpack/plugin`, which uses these webpack internal APIs:
3443

3544
- `webpack/lib/dependencies/ModuleDependency` — base class for dependency tracking
@@ -44,14 +53,14 @@ The `RSCWebpackPlugin` is the critical compatibility question. It wraps React's
4453
- `compilation.chunkGraph` — chunk/module graph traversal
4554

4655
**Why this matters**: The plugin generates `react-client-manifest.json` and
47-
`react-ssr-manifest.json`, which map client component file paths to their chunk
56+
`react-server-client-manifest.json`, which map client component file paths to their chunk
4857
IDs and bundle filenames. Without these manifests, the RSC runtime cannot resolve
4958
`'use client'` component references during streaming.
5059

51-
**Rspack v2 compatibility**: Rspack v2 has significantly improved webpack plugin
52-
compatibility. The [Rspack team has confirmed](https://github.com/shakacode/react_on_rails/issues/1828#issuecomment-3350629010)
53-
that Rspack supports RSC with the JavaScript API. However, runtime verification
54-
with the specific `react-on-rails-rsc` plugin is still needed.
60+
**Rspack v2 compatibility**: Applying this webpack plugin directly to Rspack is not supported.
61+
Rspack does not expose every webpack compiler and compilation object used by the plugin. Generated
62+
Rspack configs therefore route manifest generation through `rscManifestPlugin.js`, which uses Rspack's
63+
public compilation hooks and keeps the existing `react-on-rails-rsc` wire protocol.
5564

5665
## How the RSC Bundle Avoids the Plugin
5766

@@ -63,7 +72,8 @@ which skips adding `RSCWebpackPlugin`. The RSC bundle only uses:
6372
3. **Aliases** to exclude `react-dom/server` from the RSC bundle
6473

6574
This means the RSC bundle itself should work with Rspack today. The plugin is only
66-
added to the **server** and **client** bundles for manifest generation.
75+
added to the **server** and **client** bundles for manifest generation when using webpack. With Rspack,
76+
the generated manifest helper is added to those bundles instead.
6777

6878
## Testing with Rspack
6979

@@ -74,27 +84,29 @@ To test RSC with Rspack in your project:
7484
3. Verify configs are in `config/rspack/`
7585
4. Build all three bundles and check for:
7686
- `rsc-bundle.js` in the output
77-
- `react-client-manifest.json` and `react-ssr-manifest.json` (from the plugin)
87+
- `react-client-manifest.json`
88+
- `react-server-client-manifest.json`
7889
- No webpack/Rspack compilation errors
7990

8091
## Known Limitations
8192

82-
1. **No `react-server-dom-rspack` package**: React does not ship a dedicated Rspack
83-
variant of the RSC wire protocol. The `react-server-dom-webpack` package is used,
84-
relying on Rspack's webpack compatibility layer.
93+
1. **Runtime support remains experimental**: The generated Rspack helper emits the manifests
94+
expected by React on Rails Pro, but each app should still verify an interactive `'use client'`
95+
boundary through the Pro Node Renderer before treating Rspack + RSC as production-ready.
8596

8697
2. **Plugin `require('webpack')` call**: The `react-server-dom-webpack/plugin`
8798
internally calls `require('webpack')`, which loads webpack even in Rspack projects.
88-
Rspack's compatibility layer must intercept the compiler/compilation interactions
89-
for the plugin to function correctly.
99+
Generated Rspack configs avoid that direct plugin path and use `rscManifestPlugin.js`.
90100

91-
3. **No official React Rspack support**: The React team has not officially tested or
92-
endorsed `react-server-dom-webpack` with Rspack. Compatibility is provided by
93-
Rspack's webpack API compatibility layer.
101+
3. **Native Rspack RSC is a future migration path**: Rspack documents native RSC support through
102+
`@rspack/core`'s `experiments.rsc` APIs and `react-server-dom-rspack`, but that path currently
103+
targets React 19.1+ and a different manifest/runtime shape. React on Rails Pro's current RSC
104+
integration stays on the `react-on-rails-rsc` protocol for React 19.0.x compatibility.
94105

95106
## Related Resources
96107

97108
- [Issue #1828: Rspack support for RSC](https://github.com/shakacode/react_on_rails/issues/1828)
98109
- [Rspack RSC support PR](https://github.com/web-infra-dev/rspack/pull/5824)
110+
- [Rspack React Server Components guide](https://v2.rspack.rs/guide/tech/rsc)
99111
- [Three-bundle architecture](./how-react-server-components-work.md)
100112
- [Upgrading an existing Pro app to RSC](./upgrading-existing-pro-app.md)

react_on_rails/lib/generators/react_on_rails/base_generator.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ class BaseGenerator < Rails::Generators::Base
6464
"development.js" => "base/base/config/webpack/development.js.tt",
6565
"production.js" => "base/base/config/webpack/production.js.tt",
6666
"serverWebpackConfig.js" => "base/base/config/webpack/serverWebpackConfig.js.tt",
67+
"rscManifestPlugin.js" => "base/base/config/webpack/rscManifestPlugin.js.tt",
6768
"rscWebpackConfig.js" => "rsc/base/config/webpack/rscWebpackConfig.js.tt",
6869
"ServerClientOrBoth.js" => "base/base/config/webpack/ServerClientOrBoth.js.tt",
6970
# Legacy filename generated by older React on Rails versions.
@@ -229,6 +230,7 @@ def copy_webpack_config
229230
config/webpack/production.js
230231
config/webpack/serverWebpackConfig.js
231232
config/webpack/ServerClientOrBoth.js]
233+
base_files << "config/webpack/rscManifestPlugin.js" if use_rsc?
232234
config = { message: DOCS_REFERENCE_MESSAGE }
233235
base_files.each do |file|
234236
template("#{base_path}/#{file}.tt", destination_config_path(file), config)

react_on_rails/lib/generators/react_on_rails/rsc/USAGE

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ Example:
1313
- RSC bundle watcher to Procfile.dev
1414

1515
Modifies:
16-
- config/webpack/serverWebpackConfig.js (adds RSCWebpackPlugin, rscBundle param)
17-
- config/webpack/clientWebpackConfig.js (adds RSCWebpackPlugin)
16+
- config/webpack/rscManifestPlugin.js (adds RSC manifest generation helper)
17+
- config/webpack/serverWebpackConfig.js (adds RSC manifest helper, rscBundle param)
18+
- config/webpack/clientWebpackConfig.js (adds RSC manifest helper)
1819
- config/webpack/ServerClientOrBoth.js (adds rscWebpackConfig, RSC_BUNDLE_ONLY)
1920
- config/initializers/react_on_rails_pro.rb (adds RSC config block)
2021
- package.json (adds react-on-rails-rsc)

0 commit comments

Comments
 (0)