Skip to content

Test source-mapped TSX prerender errors#4135

Merged
justin808 merged 5 commits into
mainfrom
codex/issue-4112-tsx-prerender-source-map
Jun 20, 2026
Merged

Test source-mapped TSX prerender errors#4135
justin808 merged 5 commits into
mainfrom
codex/issue-4112-tsx-prerender-source-map

Conversation

@justin808

@justin808 justin808 commented Jun 20, 2026

Copy link
Copy Markdown
Member

Summary

  • Stage the Pro dummy app's external server-bundle.js.map with test node-renderer uploads.
  • Add a TSX prerender probe plus Rails route/view coverage for a PrerenderError raised during SSR.
  • Assert the Rails-side error handler logs the original .tsx source-mapped frame.

Closes #4112

Verification

  • pnpm run build:test from react_on_rails_pro/spec/dummy
  • RENDERER_PORT=3812 pnpm run node-renderer:fresh
  • REACT_RENDERER_URL=http://localhost:3812 bundle exec rspec spec/requests/source_mapped_prerender_error_spec.rb --format documentation
  • REACT_RENDERER_URL=http://localhost:3812 bundle exec rspec spec/requests/console_logging_spec.rb --format documentation
  • pnpm run type-check
  • pnpm --filter react-on-rails-pro-node-renderer run build
  • pnpm run lint
  • pnpm exec prettier --check -- react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/SourceMappedPrerenderErrorProbe.tsx
  • BUNDLE_GEMFILE=../Gemfile bundle exec rubocop --ignore-parent-exclusion spec/dummy/config/initializers/react_on_rails_pro.rb spec/dummy/config/routes.rb spec/dummy/app/controllers/pages_controller.rb spec/dummy/spec/requests/source_mapped_prerender_error_spec.rb
  • git diff --cached --check
  • script/ci-changes-detector origin/main
  • pre-commit hook: trailing newlines, Prettier, RuboCop, autofix, ESLint, Pro license headers
  • pre-push hook: branch RuboCop, markdown-links

Notes

  • Renderer shutdown after successful request specs exits nonzero on SIGINT with an EPIPE; specs completed before shutdown.
  • AI review was attempted from a clean diff but stopped as inconclusive after repeated broad fixture/generated-bundle exploration without a concrete finding. Treat AI-review result as UNKNOWN, not passed.

Note

Low Risk
Changes are limited to the Pro dummy app’s tests, routes, and build scripts; production initializer behavior is unchanged outside test env source-map copying.

Overview
Adds dummy-app coverage so request specs can prove Rails logs source-mapped .tsx frames when a ReactOnRails::PrerenderError is raised during classic react_component SSR (not the RSC path).

A new route, view, and SourceMappedPrerenderErrorProbe client component ('use client', intentional throw) exercise prerender: true with raise_on_prerender_error and trace. The probe is wired into the existing error-hub with_config_overrides list. A request spec stubs the logger and expects the handler message, probe error text, and a SourceMappedPrerenderErrorProbe.tsx:line:col frame, plus the usual redirect to the invoker path.

Test node-renderer staging now includes ssr-generated/server-bundle.js.map in assets_to_copy when Rails.env.test?, so the remote renderer can resolve maps during those specs.

Build hygiene: dummy package.json gains prepare:i18n (mkdir -p client/app/i18n/generated) ahead of shakapacker-precompile-hook on all main build scripts; Jest asserts that ordering.

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

Summary by CodeRabbit

Release Notes

  • New Features

    • Added a dedicated error-probe page and route to surface source-mapped prerender errors for SSR debugging.
  • Tests

    • Added a request spec that validates the probe redirect flow and asserts server log output includes a source-mapped location.
    • Enhanced Jest coverage to verify i18n asset generation happens before the precompile hook across multiple build scripts.
  • Chores

    • Updated test asset copying to include server bundle source maps.
    • Updated dummy build scripts to generate i18n assets prior to precompilation.

@coderabbitai

coderabbitai Bot commented Jun 20, 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

Adds a new end-to-end test fixture for source-mapped prerender errors: a TSX probe component that throws on render, an ERB view, a controller action with route, a config change to copy server-bundle.js.map in the test environment, an RSpec request spec that asserts the Rails logger output contains a source-mapped .tsx file:line:column frame, and updates build scripts to prepare i18n assets before test builds.

Changes

Source-mapped prerender error probe

Layer / File(s) Summary
Probe component and ERB view
react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/SourceMappedPrerenderErrorProbe.tsx, react_on_rails_pro/spec/dummy/app/views/pages/source_mapped_prerender_error_probe.html.erb
Adds a "use client" TSX component that immediately throws Error('source-mapped TSX prerender probe'), and an ERB template that prerenders it with raise_on_prerender_error: true and trace: true.
Controller action, route, and source-map asset config
react_on_rails_pro/spec/dummy/app/controllers/pages_controller.rb, react_on_rails_pro/spec/dummy/config/routes.rb, react_on_rails_pro/spec/dummy/config/initializers/react_on_rails_pro.rb
Adds source_mapped_prerender_error_probe to the around_action list and as a new controller action; adds the matching GET route; conditionally appends ssr-generated/server-bundle.js.map to config.assets_to_copy when Rails.env.test?.
RSpec request spec
react_on_rails_pro/spec/dummy/spec/requests/source_mapped_prerender_error_spec.rb
Adds a :server_rendering request spec that captures Rails.logger into a StringIO, issues a GET to the probe route, asserts the redirect response, and asserts the log contains both the marker string and a SourceMappedPrerenderErrorProbe.tsx:<line>:<column> pattern.

Build script i18n preparation

Layer / File(s) Summary
i18n preparation in build scripts and tests
react_on_rails_pro/spec/dummy/package.json, react_on_rails_pro/spec/dummy/tests/package-scripts.test.js
Introduces prepare:i18n script that creates client/app/i18n/generated, and updates build:test, build:test:rspack, build:dev, build:dev:watch, build:client, and build:server to run the script before invoking the Shakapacker precompile hook. Adds parameterized Jest tests validating the script definition and its inclusion in the build commands.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • shakacode/react_on_rails#3940: Introduced the Pro node renderer's source-map stack remapping (vmSourceMapSupport/remapStackTrace) that this PR's spec exercises end-to-end at the Rails layer.
  • shakacode/react_on_rails#4128: Adds a precompile hook that generates locales when configured, complementing the prepare:i18n script added here that ensures client/app/i18n/generated exists before precompile.

Suggested labels

review-needed, full-ci, P2

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 'Test source-mapped TSX prerender errors' directly summarizes the main change: adding test coverage for source-mapped TypeScript/TSX prerender errors, which is the core objective of the PR.
Linked Issues check ✅ Passed The PR fully satisfies issue #4112's objective to add Rails RSpec coverage asserting source-mapped .tsx frames in PrerenderError, including test setup, component, request spec with proper assertions, and build configuration.
Out of Scope Changes check ✅ Passed All changes are scoped to the Pro dummy app test infrastructure (controller, view, component, routing, config, specs, build scripts) with no modifications to production gem code or error-handling logic.
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 codex/issue-4112-tsx-prerender-source-map

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.

@justin808

Copy link
Copy Markdown
Member Author

+ci-status

@justin808

Copy link
Copy Markdown
Member Author

+ci-run-hosted

@github-actions

Copy link
Copy Markdown
Contributor

CI Status

Head SHA: b398033920b3
Changed files: 6
Docs-only heuristic (matches ci-changes-detector metadata paths): no
ready-for-hosted-ci label: absent
force-full-hosted-ci label: absent
Current hosted-CI waiver: not present for this SHA

Only the required gate is active unless hosted CI is requested.

@github-actions github-actions Bot added the ready-for-hosted-ci Run optimized hosted GitHub CI for this PR label Jun 20, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Hosted CI Requested

Triggered 9 workflow(s) for b398033920b3.
Mode: optimized hosted CI (path-selected by script/ci-changes-detector).
Added ready-for-hosted-ci, so future commits will keep running optimized hosted CI until +ci-stop-hosted is used.

View progress in the Actions tab.

@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.

🧹 Nitpick comments (1)
react_on_rails_pro/spec/dummy/spec/requests/source_mapped_prerender_error_spec.rb (1)

21-31: ⚡ Quick win

Avoid mutating global Rails.logger in the example.

Reassigning Rails.logger can leak across concurrently running specs. Prefer stubbing Rails.logger for this example to keep isolation deterministic.

Proposed refactor
-    log_output = StringIO.new
-    original_logger = Rails.logger
-    Rails.logger = ActiveSupport::Logger.new(log_output)
+    log_output = StringIO.new
+    test_logger = ActiveSupport::Logger.new(log_output)
+    allow(Rails).to receive(:logger).and_return(test_logger)
@@
-  ensure
-    Rails.logger = original_logger
   end
 end
🤖 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_pro/spec/dummy/spec/requests/source_mapped_prerender_error_spec.rb`
around lines 21 - 31, The test is directly mutating the global Rails.logger by
reassigning it to a new ActiveSupport::Logger instance, which can cause test
isolation issues when specs run concurrently. Instead of reassigning
Rails.logger and using an ensure block to restore it, stub Rails.logger using
RSpec's stubbing mechanism (with allow or expect) to capture log output while
maintaining test isolation. This approach prevents the global state from being
modified across concurrent test runs.
🤖 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.

Nitpick comments:
In
`@react_on_rails_pro/spec/dummy/spec/requests/source_mapped_prerender_error_spec.rb`:
- Around line 21-31: The test is directly mutating the global Rails.logger by
reassigning it to a new ActiveSupport::Logger instance, which can cause test
isolation issues when specs run concurrently. Instead of reassigning
Rails.logger and using an ensure block to restore it, stub Rails.logger using
RSpec's stubbing mechanism (with allow or expect) to capture log output while
maintaining test isolation. This approach prevents the global state from being
modified across concurrent test runs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2ad08fd9-8e1b-49a3-a3e7-1001d89d414a

📥 Commits

Reviewing files that changed from the base of the PR and between 0cfadcc and b398033.

📒 Files selected for processing (6)
  • react_on_rails_pro/spec/dummy/app/controllers/pages_controller.rb
  • react_on_rails_pro/spec/dummy/app/views/pages/source_mapped_prerender_error_probe.html.erb
  • react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/SourceMappedPrerenderErrorProbe.tsx
  • react_on_rails_pro/spec/dummy/config/initializers/react_on_rails_pro.rb
  • react_on_rails_pro/spec/dummy/config/routes.rb
  • react_on_rails_pro/spec/dummy/spec/requests/source_mapped_prerender_error_spec.rb

@greptile-apps

greptile-apps Bot commented Jun 20, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds an end-to-end test for source-mapped SSR prerender errors. A minimal TSX probe component (always throws), a Rails route/view/controller action, and a request spec are wired together to verify that when raise_on_prerender_error: true causes a ReactOnRails::PrerenderError, the Rails error handler logs a source-mapped SourceMappedPrerenderErrorProbe.tsx:LINE:COL frame rather than a minified bundle reference.

  • New probe component (SourceMappedPrerenderErrorProbe.tsx) always throws during render; the view renders it with prerender: true and raise_on_prerender_error: true so the PrerenderError is raised unconditionally.
  • assets_to_copy extended in the test initializer to include ssr-generated/server-bundle.js.map in the test environment, ensuring the map file is uploaded to the node renderer so source mapping can apply.
  • Request spec swaps Rails.logger to a StringIO buffer for the duration of the example, triggers the probe route, and asserts both the error message text and a .tsx:LINE:COL frame pattern appear in the log output.

Confidence Score: 4/5

The change adds test infrastructure only — a new probe component, route, view, controller action, and request spec. No production rendering paths are modified. The only runtime-affecting change is the assets_to_copy initializer, which adds the source map file in the test environment, leaving other environments unchanged.

The test globally replaces Rails.logger mid-process rather than using a scoped capture, which works under serial test execution but would produce intermittent failures if the suite ever runs concurrently. The 'use client' directive on a component expected to execute during SSR prerender also warrants a clarifying comment, given that RSC support is enabled in the dummy app. Neither concern affects the current verified behavior, but both could silently break under future changes to concurrency settings or renderer RSC handling.

The request spec (source_mapped_prerender_error_spec.rb) and the probe component (SourceMappedPrerenderErrorProbe.tsx) are the two files worth a second look before merging.

Important Files Changed

Filename Overview
react_on_rails_pro/spec/dummy/spec/requests/source_mapped_prerender_error_spec.rb New request spec that swaps Rails.logger to a StringIO buffer, triggers a prerender error via the probe route, and asserts both the error message and a source-mapped TSX frame appear in the log; the logger is restored in an ensure block.
react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/SourceMappedPrerenderErrorProbe.tsx Minimal TSX probe component that always throws during render; marked 'use client' and registered for auto-load so the traditional react_component SSR path executes it.
react_on_rails_pro/spec/dummy/app/views/pages/source_mapped_prerender_error_probe.html.erb New ERB view that renders the probe component with prerender:true and raise_on_prerender_error:true to force the PrerenderError path.
react_on_rails_pro/spec/dummy/config/initializers/react_on_rails_pro.rb Extends assets_to_copy with server-bundle.js.map in test env only so the map file is uploaded to the node renderer during test runs.
react_on_rails_pro/spec/dummy/app/controllers/pages_controller.rb Adds source_mapped_prerender_error_probe action to the controller and includes it in the with_config_overrides around-action list alongside similar error probe actions.
react_on_rails_pro/spec/dummy/config/routes.rb Adds a GET route for the new probe endpoint; placement between related log-throw routes is consistent with the surrounding route group.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Spec as RSpec
    participant Rails as Rails (test)
    participant Controller as PagesController
    participant View as ERB view
    participant Helper as react_component helper
    participant Renderer as Node Renderer
    participant AppCtrl as ApplicationController

    Spec->>Rails: swap Rails.logger to StringIO
    Spec->>Rails: GET /source_mapped_prerender_error_probe
    Rails->>Controller: source_mapped_prerender_error_probe action
    Controller->>View: render template
    View->>Helper: react_component with prerender true and raise_on_prerender_error true
    Helper->>Renderer: SSR request with server-bundle.js and .js.map
    Renderer-->>Helper: Error at SourceMappedPrerenderErrorProbe.tsx LINE COL
    Helper->>Helper: raise ReactOnRails::PrerenderError
    Helper-->>AppCtrl: PrerenderError bubbles up
    AppCtrl->>AppCtrl: rescue_from calls log_prerender_error
    AppCtrl->>Rails: Rails.logger.error captures TSX frame
    AppCtrl->>Rails: redirect_to server_side_log_throw_raise_invoker_path
    Rails-->>Spec: 302 redirect
    Spec->>Spec: assert redirect and TSX frame in log
    Spec->>Rails: ensure restore original Rails.logger
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Spec as RSpec
    participant Rails as Rails (test)
    participant Controller as PagesController
    participant View as ERB view
    participant Helper as react_component helper
    participant Renderer as Node Renderer
    participant AppCtrl as ApplicationController

    Spec->>Rails: swap Rails.logger to StringIO
    Spec->>Rails: GET /source_mapped_prerender_error_probe
    Rails->>Controller: source_mapped_prerender_error_probe action
    Controller->>View: render template
    View->>Helper: react_component with prerender true and raise_on_prerender_error true
    Helper->>Renderer: SSR request with server-bundle.js and .js.map
    Renderer-->>Helper: Error at SourceMappedPrerenderErrorProbe.tsx LINE COL
    Helper->>Helper: raise ReactOnRails::PrerenderError
    Helper-->>AppCtrl: PrerenderError bubbles up
    AppCtrl->>AppCtrl: rescue_from calls log_prerender_error
    AppCtrl->>Rails: Rails.logger.error captures TSX frame
    AppCtrl->>Rails: redirect_to server_side_log_throw_raise_invoker_path
    Rails-->>Spec: 302 redirect
    Spec->>Spec: assert redirect and TSX frame in log
    Spec->>Rails: ensure restore original Rails.logger
Loading

Reviews (1): Last reviewed commit: "Test source-mapped TSX prerender errors" | Re-trigger Greptile

Comment thread react_on_rails_pro/spec/dummy/spec/requests/source_mapped_prerender_error_spec.rb Outdated
Comment thread react_on_rails_pro/spec/dummy/spec/requests/source_mapped_prerender_error_spec.rb Outdated
Comment thread react_on_rails_pro/spec/dummy/config/initializers/react_on_rails_pro.rb Outdated
@claude

claude Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

The approach is sound and well-scoped: all changes are confined to the Pro dummy app test harness with no production SSR or error-handling paths touched. Four issues are flagged via inline comments; two are worth resolving before merge.

Issues requiring attention before merge

'use client' directive on the probe component (inline comment on SourceMappedPrerenderErrorProbe.tsx:18) — In an RSC-aware renderer, 'use client' marks the component as a client boundary and the node renderer may skip it during SSR entirely. If so, no error is thrown, no log is written, and the spec fails in a confusing place (redirect assertion rather than the regex). No other deliberate-error fixture in this repo carries this directive. Please verify it can be removed, or document why it is necessary despite the component's SSR-only purpose.

Incorrect functional component signature (inline comment on SourceMappedPrerenderErrorProbe.tsx:19) — The (props, context) two-argument form is the class component API. A functional component's second argument is the forwarded ref (React.forwardRef only). Harmless since the component always throws, but misleading.

Lower-priority improvements

Logger replacement pattern (inline comment on source_mapped_prerender_error_spec.rb:21-22) — The bare ActiveSupport::Logger replacement drops the original's broadcast chain (relevant on Rails 7.1+). An around hook is more idiomatic and composes better with other hooks.

Local variable inside conditional expression (inline comment on react_on_rails_pro.rb:66-68) — Assigning assets = [...] and reading it as the implicit return of the if branch is unusual Ruby style. Renaming to paths and keeping the same logic is slightly clearer.

Positives

  • server-bundle.js.map staging is guarded to test env only — no source maps in staging/production.
  • require 'stringio' is explicit — correct for Ruby < 3 compat.
  • The :server_rendering tag is consistent with other node-renderer specs.
  • License headers present on all new files.
  • Redirect assertion verifies the rescue_from handler ran; the regex assertion pins actual source-map resolution behavior.

@justin808

Copy link
Copy Markdown
Member Author

Review follow-up pushed in 49a93117e.

Addressed:

  • Kept 'use client', but added a comment explaining why. I verified removal locally: with RSC support enabled in the Pro dummy app, the probe is auto-registered as a Server Component and react_component fails before the intended TSX throw. Keeping the directive preserves the traditional SSR path this spec is exercising.
  • Changed the probe from the misleading (props, context) render-function shape to a normal zero-arg function component.
  • Replaced global Rails.logger = ... assignment with an RSpec-scoped allow(Rails).to receive(:logger) capture.
  • Renamed the initializer local from assets to paths.

Validation after the push:

  • pnpm run build:test from react_on_rails_pro/spec/dummy passed.
  • REACT_RENDERER_URL=http://localhost:3812 bundle exec rspec spec/requests/source_mapped_prerender_error_spec.rb spec/requests/console_logging_spec.rb --format documentation passed: 2 examples, 0 failures.
  • pnpm run type-check passed.
  • pnpm run lint passed after removing ignored node-renderer runtime bundle artifacts created by the focused spec run.
  • BUNDLE_GEMFILE=../Gemfile bundle exec rubocop --ignore-parent-exclusion spec/dummy/config/initializers/react_on_rails_pro.rb spec/dummy/spec/requests/source_mapped_prerender_error_spec.rb passed.
  • git diff --check passed.
  • Pre-commit and pre-push hooks passed.

Hosted checks are rerunning on the new head.

@claude

claude Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Code Review — PR #4135: Test source-mapped TSX prerender errors

Overview: Adds regression coverage verifying that when the Node renderer raises PrerenderError during SSR, Rails logs the original .tsx source-mapped frame (not only the bundled JS frame). Changes are confined to the Pro dummy app and test infrastructure — no production SSR or error-handling logic is touched.


What works well

  • Minimal, purposeful probe componentSourceMappedPrerenderErrorProbe does exactly one thing (throw), making the test signal unambiguous.
  • 'use client' directive with explanatory comment — prevents the RSC-enabled app from auto-registering it as a Server Component; the comment makes the intent clear for future readers.
  • Regex for line/col in log assertion (/SourceMappedPrerenderErrorProbe\.tsx:\d+:\d+/) — build output will vary, so the pattern is appropriately flexible.
  • Redirect assertion is groundedredirect_to(server_side_log_throw_raise_invoker_path) correctly matches the existing ApplicationController#redirect_prerender_error_response handler.
  • RSpec mock cleanup is automaticallow(Rails).to receive(:logger) is cleaned up by RSpec after each example; no teardown needed.

Issues

1. Missing file-existence guard for server-bundle.js.map (initializer)

paths << Rails.root.join("ssr-generated", "server-bundle.js.map") if Rails.env.test?

If a developer runs the spec suite without first running pnpm run build:test, the map file won't exist and the node-renderer upload will fail with an opaque error. Consider guarding with:

map_path = Rails.root.join("ssr-generated", "server-bundle.js.map")
paths << map_path if Rails.env.test? && File.exist?(map_path)

This makes local dev more forgiving while still exercising the upload path when the file is present.

2. Spec uses RSpec.describe while existing sibling specs use bare describe

All other specs in spec/requests/ (e.g., console_logging_spec.rb, upload_asset_spec.rb) use describe at the top level. Switching to describe keeps the file consistent with its neighbours. Purely stylistic, but worth noting.

3. require "stringio" is unnecessary

StringIO is part of Ruby's default gems and is loaded automatically in any Rails process. The explicit require "stringio" is harmless, but it's noise — no other spec in the tree does this. Can be removed.

4. Single-scenario spec — consider a negative case

The spec only verifies the happy path where source mapping works. A companion example asserting behaviour when the map is absent (or mapping fails to resolve) would close the gap on the failure path. That said, this is likely better added in a follow-up once the positive case lands in CI.


Summary

The change is low-risk and well-scoped. The only actionable item before merge is the missing file-existence guard in the initializer, which prevents silent/confusing failures for contributors who haven't built the test bundle. The require 'stringio' removal and descriptor rename are trivial. Everything else looks solid.

@claude

claude Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Code Review

Overview

This PR adds regression coverage for source-mapped TSX prerender errors in the Pro dummy app. The feature verifies that when SSR raises PrerenderError, Rails logging includes a frame pointing back to the original .tsx file (not just bundled JS). The scope is correctly limited to the Pro dummy app — no production SSR or error-handling logic is modified.

Strengths

  • Clean, focused scope: one component, one route, one view, one spec
  • The 'use client' directive comment explaining why it's needed is valuable
  • Explicit require "stringio" in the spec is good practice for Ruby 3.x compatibility
  • The prepare:i18n extraction into a named script and its corresponding test in package-scripts.test.js is a nice improvement

Issues and Observations

Medium

build:dev and build:client don't include prepare:i18n (package.json:114-116)

The new prepare:i18n step is only wired into build:test and build:test:rspack. If the client/app/i18n/generated/ directory is required for any non-test code path (e.g. the webpack precompile hook fails without it), build:dev, build:dev:watch, and build:client would silently break. Either add pnpm run prepare:i18n && to those scripts too, or confirm the directory is only needed in the test env and document the reasoning.

assets_to_copy now returns an Array in non-test mode (react_on_rails_pro.rb:65-68)

Previously the value was a single Pathname; now it's always Array<Pathname>. The initializer comment says the option accepts "a file_path or an Array", so both are valid, but this is an implicit interface change for all non-HMR environments. Worth a brief comment or a note confirming the consumer handles both forms consistently.

Minor

Spec doesn't assert the handler's own log line

The test verifies the error message and the source-mapped frame in the log, but skips asserting "Caught ReactOnRails::PrerenderError in ApplicationController error handler.". That line is the entry point of the handler and would make a regression if the handler was bypassed. Adding it would strengthen the coverage with one line.

Hardcoded id: in the view (source_mapped_prerender_error_probe.html.erb:16)

id: "SourceMappedPrerenderErrorProbe-react-component-0" is hardcoded while other probe views omit this option and rely on the default generated ID. This isn't wrong (the spec doesn't test the DOM), but omitting the override would be more consistent with sibling views.

Summary

The PR is well-scoped and correct. The two medium items are worth discussing before merge (particularly the build:dev/prepare:i18n gap), but the minor items are low risk. The spec design — stub the logger, assert the redirect and the source-mapped frame — directly tests the feature's contract.

Comment thread react_on_rails_pro/spec/dummy/package.json 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

🤖 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_pro/spec/dummy/tests/package-scripts.test.js`:
- Around line 38-39: The test assertion at line 38-39 only verifies that the
string 'pnpm run prepare:i18n' is present in the script using toContain(), but
does not verify its ordering relative to 'bin/shakapacker-precompile-hook'.
Replace the toContain assertion with one that explicitly checks the index
positions of both strings to ensure 'pnpm run prepare:i18n' appears before
'bin/shakapacker-precompile-hook' in the script, confirming the required
execution order for the prepare-before-precompile contract.
🪄 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: 22900719-9a00-4052-9763-31b4e6c6b1db

📥 Commits

Reviewing files that changed from the base of the PR and between 49a9311 and 278c499.

📒 Files selected for processing (2)
  • react_on_rails_pro/spec/dummy/package.json
  • react_on_rails_pro/spec/dummy/tests/package-scripts.test.js

Comment thread react_on_rails_pro/spec/dummy/tests/package-scripts.test.js Outdated
@justin808

Copy link
Copy Markdown
Member Author

CI failure follow-up pushed in 278c499b4.

Root cause:

  • Fresh hosted CI did not have the ignored react_on_rails_pro/spec/dummy/client/app/i18n/generated directory before bin/shakapacker-precompile-hook ran, so config.i18n_dir validation failed in both build-dummy-app-webpack-test-bundles and dummy-app-rspack-rsc-runtime-gate.

Fix:

  • Added prepare:i18n to create client/app/i18n/generated.
  • Called it from both build:test and build:test:rspack after the clean step.
  • Added a package-script Jest guard so both test build scripts keep preparing that directory.

Validation on the pushed head:

  • pnpm exec jest ./tests/package-scripts.test.js passed: 9 tests.
  • rm -rf .../client/app/i18n/generated .../public/webpack/test .../ssr-generated then pnpm run build:test passed.
  • Same clean state then pnpm run build:test:rspack passed.
  • pnpm run build:test passed again before the renderer-backed specs.
  • RENDERER_PORT=3812 pnpm run node-renderer:fresh started successfully.
  • REACT_RENDERER_URL=http://localhost:3812 bundle exec rspec spec/requests/source_mapped_prerender_error_spec.rb spec/requests/console_logging_spec.rb --format documentation passed: 2 examples, 0 failures.
  • pnpm run lint passed.
  • pnpm run type-check passed.
  • pnpm start format.listDifferent passed.
  • script/ci-changes-detector origin/main still recommends the Pro dummy/integration lane.
  • codex review --base origin/main found no actionable correctness issues.
  • Pre-commit and pre-push hooks passed.

Hosted CI and review bots are rerunning on 278c499b4.

@justin808

Copy link
Copy Markdown
Member Author

Review follow-up for the current CodeRabbit package-script ordering request:

  • Strengthened package-scripts.test.js so build:test and build:test:rspack assert pnpm run prepare:i18n appears before bin/shakapacker-precompile-hook.
  • Local validation after the change:
    • pnpm exec jest ./tests/package-scripts.test.js PASS, 9 examples.
    • pnpm run lint PASS.
    • pnpm start format.listDifferent PASS.
    • git diff --check PASS.
    • pre-commit hook PASS.
    • pre-push hook PASS.

Head pushed: 1c7397aa4.

Comment thread react_on_rails_pro/spec/dummy/package.json
@claude

claude Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Code Review

PR summary: Adds end-to-end coverage for source-mapped TSX prerender errors in the Pro dummy app — a new SourceMappedPrerenderErrorProbe component that throws during SSR, plumbed through a route/controller/view, with a request spec that asserts the Rails logger receives a .tsx:line:col frame after source-map resolution.

Overall the approach is clean and well-scoped (all changes stay in the dummy app / test fixtures). A few things to look at:


Potential issues

1. Missing map file → confusing renderer failure (initializer, line 67)
server-bundle.js.map is added to assets_to_copy unconditionally whenever Rails.env.test?, but the file only exists after build:test has run. If someone runs the spec suite cold (no prior build), the renderer receives a file list pointing at a missing path. The failure won't be obviously "run build:test first". A File.exist? guard or a CI-prerequisite note would help. (inline comment posted)

2. prepare:i18n gap in dev/production builds (package.json, line 111)
build:dev, build:dev:watch, and build:client don't call prepare:i18n. If client/app/i18n/generated doesn't already exist locally, those builds will hit the same missing-directory error. Either propagate the step to the other build scripts or commit a .gitkeep so the directory is always present. (inline comment posted)


Style / minor notes

3. Intermediate variable inside if expression (initializer, lines 65–69)
The paths = [...]; paths << ... pattern inside the if expression is unusual. A splat or tap reads more naturally and avoids the block-local variable. Inline suggestion posted — functionally equivalent, just cleaner.

4. New logger has no formatter (spec, line 22)
The stub swaps in a bare ActiveSupport::Logger, which has no custom formatter. The assertions are substring/regex matches so this is harmless today, but worth keeping in mind if the spec is later extended to assert on formatted output. (inline comment posted)

5. require "stringio" is correct
Included for explicitness — in Ruby 3.2+ StringIO requires an explicit require. No change needed.


What's good

  • The 'use client' directive on the probe component is well-motivated by a real constraint (the RSC-enabled dummy auto-registers components as Server Components) and the comment explains it clearly.
  • configuration.rb already normalises assets_to_copy via Array(), so the type change from a single Pathname to an Array is safe.
  • The Jest test for script ordering is a nice self-documenting guard.
  • The request spec assertions are tight: redirect check + message inclusion + source-map frame pattern.

@justin808

Copy link
Copy Markdown
Member Author

Second review follow-up pushed: 4abdf24ae.

Review fixes included:

  • Extended prepare:i18n before bin/shakapacker-precompile-hook to every Pro dummy build script that invokes the shared precompile hook.
  • Strengthened package-scripts.test.js to enforce that ordering for all six build scripts.
  • Removed the explicit probe view id, aligned request-spec style with sibling specs, and asserted the ApplicationController PrerenderError handler log line.

Local validation:

  • pnpm exec jest ./tests/package-scripts.test.js PASS, 13 tests.
  • BUNDLE_GEMFILE=../Gemfile bundle exec rubocop spec/dummy/spec/requests/source_mapped_prerender_error_spec.rb PASS.
  • pnpm start format.listDifferent PASS.
  • git diff --check PASS.
  • clean pnpm run build:test PASS.
  • clean pnpm run build:test:rspack PASS.
  • REACT_RENDERER_URL=http://localhost:3812 bundle exec rspec spec/requests/source_mapped_prerender_error_spec.rb spec/requests/console_logging_spec.rb --format documentation PASS, 2 examples.
  • pnpm run type-check PASS.
  • pnpm run lint PASS after removing ignored .node-renderer-bundles generated by the node-renderer smoke run.
  • script/ci-changes-detector origin/main PASS; recommends Pro dummy integration/pro lint/benchmark coverage.
  • pre-commit and pre-push hooks PASS.

Note: stopping the local node renderer after the passing specs emitted a SIGINT shutdown write EPIPE from the renderer master; no test failed, and the generated .node-renderer-bundles artifacts were removed before rerunning lint.

@claude

claude Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Code Review

Good, focused PR. The changes are tightly scoped to the Pro dummy app's test infrastructure and close a real coverage gap: source-mapped TSX frames in the Rails prerender error path.

What works well

  • Script ordering guard: The Jest test that asserts prepare:i18n appears before bin/shakapacker-precompile-hook in every build target is a nice defensive measure.
  • 'use client' placement: The comment on the probe component clearly explains a non-obvious constraint — without it, the RSC-enabled dummy would auto-register it as a Server Component, bypassing the classic SSR path being tested.
  • Logger stub pattern: Consistent with console_logging_spec.rb and appropriate for asserting Rails-side error handler output.
  • Route placement: Logically grouped near server_side_log_throw routes.

Observations

1. No file-existence guard on server-bundle.js.map

assets_to_copy appends the source map path unconditionally in test mode. Per upload_asset_spec.rb, a missing file prints a warning to stderr rather than raising — so this won't break CI as long as build:test runs first. But a developer running just the spec without a prior build will get a silent "Asset not found" warning that is hard to diagnose. Consider a File.exist? guard or at minimum a comment in the initializer that the source map must be generated before running this spec.

2. Logger stub and cached logger instances

allow(Rails).to receive(:logger).and_return(ActiveSupport::Logger.new(log_output)) creates a fresh logger object on every Rails.logger call. If any middleware or controller code captures Rails.logger into a local variable before the request enters the action, those log lines won't be captured. This is unlikely given Rails' typical access pattern, but the alternative is to spy on the existing logger instance: allow(Rails.logger).to receive(:error) { |msg| log_output.write(msg) } — this intercepts calls on the already-live object rather than replacing it.

3. prepare:i18n added to production build targets

Adding mkdir -p client/app/i18n/generated to build:client and build:server is presumably needed because the webpack config references that path. This is correct, but a brief comment explaining why the directory must pre-exist in production builds would help the next person avoid removing it as dead code.

4. Regex pattern in spec (non-blocking)

match(/SourceMappedPrerenderErrorProbe\.tsx:\d+:\d+/) verifies the filename is source-mapped but not specific line numbers. This is intentional and correct for avoiding brittle tests — confirming it is not an oversight.

Summary

The core implementation is correct and the test strategy is sound. The File.exist? guard on the source map path (item 1) is the only change worth considering before merge. Everything else is non-blocking feedback.

@justin808

Copy link
Copy Markdown
Member Author

Confidence note:

Current blocker: external Cursor Bugbot pending; no failing checks or unresolved review threads observed.

@cursor

cursor Bot commented Jun 20, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_539b240c-ffde-469b-9413-7bc9a2fb4008)

@justin808

Copy link
Copy Markdown
Member Author

Readiness update after Cursor Bugbot completion:

  • Cursor Bugbot completed as neutral/skipped due usage limit and is not treated as a required gate.
  • .agents/skills/pr-batch/bin/pr-ci-readiness 4135 now reports READY with no failing or pending contexts.
  • Current-head checks are green, including Pro node-renderer RSpec and E2E jobs.
  • Current unresolved review thread count remains 0.
  • Release tracker Release gate: react_on_rails 17.0.0 #3823 Agent Release Mode block says Mode: development; target branch main is the beta/development phase.
  • script/pr-merge-ledger 4135 --strict --changelog-classification not_user_visible still reports complete_allowed: false only because GitHub reviewDecision is UNKNOWN; there are no unresolved current-head review threads, no active changes-requested reviews, no priority findings, and no lockfile diff.

Current blocker: needs explicit maintainer approval/waiver for the ledger reviewDecision: UNKNOWN state, and separate authorization before any merge action.

@justin808 justin808 merged commit a8a52fc into main Jun 20, 2026
64 checks passed
@justin808 justin808 deleted the codex/issue-4112-tsx-prerender-source-map branch June 20, 2026 03:07
@justin808

Copy link
Copy Markdown
Member Author

Manual verification: confirmed end-to-end against a live renderer ✅

This PR (Closes #4112) is test-only coverage (no shipped product code; the merge touches only spec/dummy/** + the staged server-bundle.js.map). It adds the end-to-end proof that #3940's source-mapped SSR stacks reach the Rails error handler: a TSX prerender probe that raises a PrerenderError during SSR, with a request spec asserting the Rails log carries the original .tsx source-mapped frame. I ran it against a real build + a live Pro node renderer.

Reproduction

cd react_on_rails_pro/spec/dummy
LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 RUBYOPT="-EUTF-8" pnpm run build:test   # bundle + server-bundle.js.map
RENDERER_PORT=3812 node renderer/node-renderer.js &                          # live Pro node renderer
REACT_RENDERER_URL=http://localhost:3812 bundle exec rspec \
  spec/requests/source_mapped_prerender_error_spec.rb --format documentation

Results

Scenario Behavior Result
No renderer running Connection error … Node Renderer; log lacks the probe frame fails (env, expected)
Live renderer + built bundle/sourcemap Rails PrerenderError handler logs the original SourceMappedPrerenderErrorProbe.tsx:<line>:<col> frame 1 passed / 1
source-mapped prerender errors
  logs the original TSX frame when Rails handles a PrerenderError
1 example, 0 failures

# the spec asserts:
expect(log_output.string).to include("source-mapped TSX prerender probe")
expect(log_output.string).to match(/SourceMappedPrerenderErrorProbe\.tsx:\d+:\d+/)

This is the real end-to-end confirmation of #3940 that I'd previously only checked at the mechanism level: a built bundle → live node renderer → SSR PrerenderError → the Rails-side log shows the original .tsx:line:col, not a bundled position.

Caveat

Full E2E (real build:test, real cluster node renderer on :3812, real request). Cleaned up: renderer process killed, port :3812 free, scratch removed. Since this PR has no product code, there's no before/after to revert — the source-mapping behavior itself belongs to #3940 (separately verified). Note: build:test here had to run under a UTF-8 locale — see the separate finding below about the #4128 precompile-hook locale step crashing under a non-UTF-8 locale.

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

Labels

ready-for-hosted-ci Run optimized hosted GitHub CI for this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Residual #3893: Rails RSpec asserting source-mapped .tsx frames in PrerenderError

1 participant