-
Notifications
You must be signed in to change notification settings - Fork 374
Fix SSR runtime failures for React Server Components #723
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
4d09e13
4c0df6e
2f3c42c
ae420af
05cb2bc
0d8d75a
aaeba86
92306ff
3f7e452
1022234
e16612c
c0a66b1
248ba65
6429ce0
f09b789
6ecfa97
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -33,6 +33,7 @@ jobs: | |||||
| DRIVER: selenium_chrome | ||||||
| CHROME_BIN: /usr/bin/google-chrome | ||||||
| USE_COVERALLS: true | ||||||
| RENDERER_PASSWORD: devPassword | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Security: this hardcodes a credential in source. Even as a dev-only password in a tutorial, it teaches poor practice. Use a GitHub Actions secret instead:
Suggested change
Add |
||||||
|
|
||||||
| steps: | ||||||
| - name: Install Chrome | ||||||
|
|
@@ -82,6 +83,20 @@ jobs: | |||||
| - name: Build shakapacker chunks | ||||||
| run: NODE_ENV=development bundle exec bin/shakapacker | ||||||
|
|
||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Observability: The renderer's stdout/stderr goes to the runner's default output mixed with the CI step log, making it hard to diagnose start-up failures. Redirect to a log file so it can be shown only on failure:
Suggested change
Then add a step after the readiness check (or in an |
||||||
| - name: Start Node renderer for SSR | ||||||
| run: | | ||||||
| node react-on-rails-pro-node-renderer.js & | ||||||
justin808 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| echo "Waiting for Node renderer on port 3800..." | ||||||
justin808 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| for i in $(seq 1 30); do | ||||||
| if nc -z localhost 3800 2>/dev/null; then | ||||||
| echo "Node renderer is ready" | ||||||
| exit 0 | ||||||
| fi | ||||||
| sleep 1 | ||||||
| done | ||||||
| echo "Node renderer failed to start within 30 seconds" | ||||||
| exit 1 | ||||||
|
|
||||||
|
Comment on lines
85
to
+99
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Two concerns with this readiness check:
|
||||||
| - name: Run rspec with xvfb | ||||||
| uses: coactions/setup-xvfb@v1 | ||||||
| with: | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -16,6 +16,76 @@ bundle exec rubocop | |||||
| bundle exec rubocop -a | ||||||
| ``` | ||||||
|
|
||||||
| ## Architecture: Three Bundle System | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use hyphenated compound modifier in heading. Line 19 should read “Three-Bundle System” for correct compound adjective style. Suggested edit-## Architecture: Three Bundle System
+## Architecture: Three-Bundle System📝 Committable suggestion
Suggested change
🧰 Tools🪛 LanguageTool[grammar] ~19-~19: Use a hyphen to join words. (QB_NEW_EN_HYPHEN) 🤖 Prompt for AI Agents |
||||||
|
|
||||||
| This project builds **three separate Rspack bundles** via Shakapacker: | ||||||
|
|
||||||
| | Bundle | Config | Output | Runtime | | ||||||
| |--------|--------|--------|---------| | ||||||
| | **Client** | `clientWebpackConfig.js` | `public/packs/` | Browser | | ||||||
| | **Server (SSR)** | `serverWebpackConfig.js` | `ssr-generated/` | Node renderer VM sandbox | | ||||||
| | **RSC** | `rscWebpackConfig.js` | RSC output dir | Node renderer (full Node.js) | | ||||||
|
|
||||||
| ### Key constraints | ||||||
|
|
||||||
| - The **server bundle runs in a VM sandbox** (`vm.createContext()`), NOT full Node.js. It lacks `require`, `MessageChannel`, `TextEncoder`, and other Node/browser globals. | ||||||
| - Use `resolve.fallback: false` (NOT `externals`) for Node builtins in the server bundle — the VM has no `require()`, so externalized `require('path')` calls will crash. | ||||||
| - The **RSC bundle** targets Node.js directly with the `react-server` condition, so Node builtins work normally there. | ||||||
| - A `BannerPlugin` injects a `MessageChannel` polyfill into the server bundle because `react-dom/server.browser` needs it at module load time. | ||||||
|
|
||||||
| ### Build commands | ||||||
|
|
||||||
| ```bash | ||||||
| # Build only the server bundle | ||||||
| SERVER_BUNDLE_ONLY=yes bin/shakapacker | ||||||
|
|
||||||
| # Build only the RSC bundle | ||||||
| RSC_BUNDLE_ONLY=true bin/shakapacker | ||||||
|
|
||||||
| # Watch modes (used by Procfile.dev) | ||||||
| SERVER_BUNDLE_ONLY=yes bin/shakapacker --watch | ||||||
| RSC_BUNDLE_ONLY=true bin/shakapacker --watch | ||||||
| ``` | ||||||
|
|
||||||
| ## Node Renderer | ||||||
|
|
||||||
| React on Rails Pro's NodeRenderer must be running for SSR and RSC payload generation. | ||||||
|
|
||||||
| - **Port**: 3800 (configured in `config/initializers/react_on_rails_pro.rb`) | ||||||
| - **Launcher**: `node react-on-rails-pro-node-renderer.js` | ||||||
| - **Auth**: Requires `RENDERER_PASSWORD` env var (defaults to `local-dev-renderer-password` in dev/test, required with no fallback in production) | ||||||
| - **Started automatically** by `bin/dev` / `Procfile.dev` | ||||||
|
|
||||||
| ### Testing requirement | ||||||
|
|
||||||
| **The Node renderer must be running before `bundle exec rspec`.** Tests will fail with `Net::ReadTimeout` if it is not running. CI starts it as a background process and waits up to 30 seconds for TCP readiness on port 3800. | ||||||
|
|
||||||
| ### Enabled environments | ||||||
|
|
||||||
| The renderer is enabled when `Rails.env.local?` (development + test) or `REACT_USE_NODE_RENDERER=true`. Production requires explicit env var configuration. | ||||||
|
|
||||||
| ## RSC Component Classification | ||||||
|
|
||||||
| React on Rails Pro auto-classifies components based on the `'use client'` directive: | ||||||
|
|
||||||
| - **With `'use client'`** → registered via `ReactOnRails.register()` (traditional SSR/client component) | ||||||
| - **Without `'use client'`** → registered via `registerServerComponent()` (React Server Component) | ||||||
|
|
||||||
| ### Common gotcha: `.server.jsx` files | ||||||
|
|
||||||
| In this codebase, `.server.jsx` does NOT mean "React Server Component" — it means traditional **server-side rendering** (e.g., `StaticRouter`). If a `.server.jsx` file uses client APIs like `ReactOnRails.getStore()` or React hooks, it **needs** the `'use client'` directive to prevent RSC misclassification. The naming predates RSC and refers to the Rails SSR render path. | ||||||
|
|
||||||
| ### Key files | ||||||
|
|
||||||
| | File | Purpose | | ||||||
| |------|---------| | ||||||
| | `config/webpack/rscWebpackConfig.js` | RSC bundle configuration | | ||||||
| | `config/webpack/rspackRscPlugin.js` | Detects `'use client'` directives, emits React Flight manifests | | ||||||
| | `client/app/packs/rsc-bundle.js` | RSC entry point | | ||||||
| | `client/app/packs/rsc-client-components.js` | Client component registration for RSC | | ||||||
| | `config/initializers/react_on_rails_pro.rb` | Node renderer + RSC configuration | | ||||||
| | `react-on-rails-pro-node-renderer.js` | Node renderer launcher | | ||||||
|
|
||||||
| ## Conductor Compatibility (Version Managers) | ||||||
|
|
||||||
| ### Problem | ||||||
|
|
||||||
Uh oh!
There was an error while loading. Please reload this page.