Doctor: accept TS/TSX server bundle suffixes#3111
Conversation
WalkthroughThe Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
ReviewThe fix is correct and well-motivated. The false positive occurred because IssuesRedundant
Existing test now has implicit filesystem dependency (see inline on The "uses Shakapacker API configuration with relative paths" test (unchanged) now runs Minor
|
Review: Doctor accepts TS/TSX server bundle suffixesThe overall approach is sound and addresses a real user pain point. The A few things worth addressing: Redundant extension re-checkIn ([extension] + SERVER_BUNDLE_SOURCE_EXTENSIONS).uniqBut by the time this method is called, Misleading placement of the new info messageThe new line: is emitted in the branch where Test coverage gapThe new test exercises only |
Greptile SummaryFixes a false-positive doctor warning where Confidence Score: 5/5Safe to merge; both findings are minor style suggestions that do not affect runtime correctness. All findings are P2 — an incomplete info message string and a test isolation nit. The core fix logic is correct, the regex properly strips extensions, and the fallback behavior is sound. No files require special attention. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[determine_server_bundle_path] --> B[Build bundle_path from source_path + entry + filename]
B --> C[resolve_server_bundle_source_path]
C --> D{File.exist? bundle_path?}
D -- yes --> E[Return bundle_path]
D -- no --> F[Strip extension to base_path]
F --> G[server_bundle_source_extensions_for .js .jsx .ts .tsx .mjs .cjs]
G --> H{Try each extension}
H -- exists --> I[Return candidate_path]
H -- none found --> J[Return original bundle_path]
E --> K[check_javascript_bundles File.exist? check]
I --> K
J --> K
K -- exists --> L[Success]
K -- missing --> M[Warning]
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: dc646adb5d
ℹ️ 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".
Review: Doctor accepts TS/TSX server bundle suffixesOverall: Looks good — the fix is correct and the core logic is sound. A few things worth addressing before merge. What works well
Issues to address1. Filesystem-dependent tests in unchanged contexts (medium) Three existing contexts now implicitly hit the real filesystem through
All three pass today because those files don't exist in CI, but they'll give a false green if extension resolution is broken for those paths. The relative-paths context already shows the right pattern — apply the same 2. No TypeScript test for the rescue path (low) The rescue branch ("when Shakapacker gem is not available") also routes through 3. If 4. Double
|
The absolute-paths, nested-absolute-paths, and LoadError rescue test contexts were implicitly filesystem-dependent after the addition of resolve_server_bundle_source_path. Add the same File.exist? stub pattern used in the relative-paths context, and add a TS resolution test for the rescue path. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Review Comment SummaryTriaged 14 review comments across 9 threads. All threads are now resolved. Fixed (2 items, commit 7570857)
Already addressed in dc646ad (4 items)
Declined with rationale (3 items)
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7570857364
ℹ️ 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".
| return SERVER_BUNDLE_SOURCE_EXTENSIONS if extension.empty? | ||
|
|
||
| SERVER_BUNDLE_SOURCE_EXTENSIONS.reject { |candidate_extension| candidate_extension == extension } |
There was a problem hiding this comment.
Don't treat unknown server bundle extensions as valid
server_bundle_source_extensions_for falls back to all known source suffixes whenever the configured filename has an unrecognized extension, so a typo like server-bundle.jss will pass the doctor check as soon as server-bundle.ts (or .js/.jsx/.tsx/.mjs/.cjs) exists. In that scenario SSR can still fail at runtime because React on Rails resolves the configured server_bundle_js_file, not the discovered fallback path. Fresh evidence in this commit is that determine_server_bundle_path now always calls this fallback and the new specs only verify alternate-extension success cases.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
react_on_rails/spec/lib/react_on_rails/doctor_spec.rb (1)
297-300: Consider extracting shared extension-stubbing setup into a helper.The same extension loop/stub is repeated in four contexts, which increases drift risk if supported extensions change again.
♻️ Optional refactor
+ def stub_server_bundle_candidates(base_path) + allow(File).to receive(:exist?).and_call_original + %w[.js .jsx .ts .tsx .mjs .cjs].each do |ext| + allow(File).to receive(:exist?).with("#{base_path}#{ext}").and_return(false) + end + end + before do ... - allow(File).to receive(:exist?).and_call_original - %w[.js .jsx .ts .tsx .mjs .cjs].each do |extension| - allow(File).to receive(:exist?).with("client/app/packs/server-bundle#{extension}").and_return(false) - end + stub_server_bundle_candidates("client/app/packs/server-bundle") endAlso applies to: 330-333, 355-358, 371-374
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@react_on_rails/spec/lib/react_on_rails/doctor_spec.rb` around lines 297 - 300, Extract the repeated extension-stubbing loop into a single test helper to avoid duplication: create a helper method (e.g., stub_server_bundle_extensions) or a shared_context used by the examples in react_on_rails::doctor_spec that performs the allow(File).to receive(:exist?).and_call_original setup and then iterates the extensions array %w[.js .jsx .ts .tsx .mjs .cjs] to stub File.exist? for "client/app/packs/server-bundle#{extension}" to false; replace the four duplicated blocks (the occurrences using the extensions loop around allow(File).to receive(:exist?).with(...).and_return(false)) with calls to that helper or include the shared_context so all specs reference the single implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@react_on_rails/lib/react_on_rails/doctor.rb`:
- Around line 1581-1586: The method server_bundle_source_extensions_for
currently treats any unknown extension like a valid specific suffix and still
returns alternate candidates; change it to first check the configured extension
(File.extname(bundle_path)) and if it's non-empty but not included in
SERVER_BUNDLE_SOURCE_EXTENSIONS return an empty array (no probes), otherwise
keep the existing behavior (return SERVER_BUNDLE_SOURCE_EXTENSIONS when
extension.empty? or reject the exact extension when it is a known member).
Ensure you update server_bundle_source_extensions_for to use
SERVER_BUNDLE_SOURCE_EXTENSIONS.include?(extension) for the guard.
---
Nitpick comments:
In `@react_on_rails/spec/lib/react_on_rails/doctor_spec.rb`:
- Around line 297-300: Extract the repeated extension-stubbing loop into a
single test helper to avoid duplication: create a helper method (e.g.,
stub_server_bundle_extensions) or a shared_context used by the examples in
react_on_rails::doctor_spec that performs the allow(File).to
receive(:exist?).and_call_original setup and then iterates the extensions array
%w[.js .jsx .ts .tsx .mjs .cjs] to stub File.exist? for
"client/app/packs/server-bundle#{extension}" to false; replace the four
duplicated blocks (the occurrences using the extensions loop around
allow(File).to receive(:exist?).with(...).and_return(false)) with calls to that
helper or include the shared_context so all specs reference the single
implementation.
🪄 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: 109af017-6cbb-4b96-a8f0-4531470476ec
📒 Files selected for processing (3)
CHANGELOG.mdreact_on_rails/lib/react_on_rails/doctor.rbreact_on_rails/spec/lib/react_on_rails/doctor_spec.rb
| def server_bundle_source_extensions_for(bundle_path) | ||
| extension = File.extname(bundle_path) | ||
| return SERVER_BUNDLE_SOURCE_EXTENSIONS if extension.empty? | ||
|
|
||
| SERVER_BUNDLE_SOURCE_EXTENSIONS.reject { |candidate_extension| candidate_extension == extension } | ||
| end |
There was a problem hiding this comment.
Avoid probing alternate extensions for unknown configured suffixes.
If server_bundle_js_file has an unsupported extension (e.g., .jss), current logic still probes known suffixes and may incorrectly pass diagnostics.
🐛 Suggested guard
def server_bundle_source_extensions_for(bundle_path)
extension = File.extname(bundle_path)
return SERVER_BUNDLE_SOURCE_EXTENSIONS if extension.empty?
+ return [] unless SERVER_BUNDLE_SOURCE_EXTENSIONS.include?(extension)
SERVER_BUNDLE_SOURCE_EXTENSIONS.reject { |candidate_extension| candidate_extension == extension }
end🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@react_on_rails/lib/react_on_rails/doctor.rb` around lines 1581 - 1586, The
method server_bundle_source_extensions_for currently treats any unknown
extension like a valid specific suffix and still returns alternate candidates;
change it to first check the configured extension (File.extname(bundle_path))
and if it's non-empty but not included in SERVER_BUNDLE_SOURCE_EXTENSIONS return
an empty array (no probes), otherwise keep the existing behavior (return
SERVER_BUNDLE_SOURCE_EXTENSIONS when extension.empty? or reject the exact
extension when it is a known member). Ensure you update
server_bundle_source_extensions_for to use
SERVER_BUNDLE_SOURCE_EXTENSIONS.include?(extension) for the guard.
| bundle_path | ||
| end | ||
|
|
||
| def server_bundle_source_extensions_for(bundle_path) |
There was a problem hiding this comment.
The method name server_bundle_source_extensions_for is a bit ambiguous — it actually returns alternate candidate extensions (excluding the original), not all extensions for that bundle. A name like alternate_source_extensions_for would clarify intent.
Also, a subtle edge case: if the configured extension is not in SERVER_BUNDLE_SOURCE_EXTENSIONS (e.g. .mts, .cts), this returns the full list — meaning .js is included in the candidates even though the user explicitly chose a non-JS extension. The behavior is still correct (it finds the closest match), but it could unexpectedly resolve to a .js file that's unrelated. Worth a code comment explaining why full-list inclusion is intentional in that case.
| def resolve_server_bundle_source_path(bundle_path) | ||
| return bundle_path if File.exist?(bundle_path) | ||
|
|
||
| base_path = bundle_path.sub(%r{\.[^./]+\z}, "") |
There was a problem hiding this comment.
The regex %r{\.[^./]+\z} is correct (the [^./] exclusion prevents matching dots inside directory components), but this would silently strip nothing if bundle_path has no extension — leaving base_path == bundle_path. The subsequent extension candidates would then be appended to the extensionless stem, which is actually desirable behavior. Worth a brief comment since it's non-obvious:
| base_path = bundle_path.sub(%r{\.[^./]+\z}, "") | |
| base_path = bundle_path.sub(%r{\.[^./]+\z}, "") # strips the last extension, if any; handles dots in directory names |
| expect(path).to eq("client/app/packs/server-bundle.js") | ||
| end | ||
|
|
||
| it "accepts a TypeScript server bundle source file when the configured filename is .js" do |
There was a problem hiding this comment.
The new test only covers .ts as an alternative extension. The parallel contexts for absolute paths ("when Shakapacker gem is available with absolute paths" and "when Shakapacker gem returns nested absolute paths") received the before-block stubs but no corresponding TS-resolution test. Suggest also adding coverage for at least one other non-JS extension (e.g. .tsx) either here or in a shared example, to guard against accidental ordering bugs in the extension list iteration.
Review: Doctor TS/TSX server bundle suffix fixOverall this is a clean, focused, low-risk fix for a genuine false-positive in How the fix works
The caller at line 213 then does Issues noted (inline)
None of these block the merge — the core logic is sound and the primary use case is well-covered. |
…ages * origin/main: Fix initial page startup race for late-loading client bundles (#3151) chore: apply prettier formatting to tracked docs files (#3153) docs: comprehensive RSC API documentation and registration consolidation (#3140) Split rspec-package-tests into parallel generator/unit shards (#3134) fix: add concurrency groups to long-running CI workflows (#3133) refactor: add RenderRequest, JsCodeBuilder, and RenderingStrategy abstractions (#3094) fix: address deferred review items from PR #2849 (#3093) Add complimentary OSS license policy for React on Rails Pro (#3123) fix: centralize CI docs-only detection and add CLI flag validation (#3091) refactor: replace stub-throw + Object.assign with capability-based composition (#3096) Enhance address-review with parallel fixes, self-review, and Greptile verification (#3121) fix: Doctor no longer fails custom projects for missing bin/dev (#3117) fix: cap webpack <5.106.0 to prevent ExecJS SSR breakage (#3095) Add Rspack + RSC compatibility tests and documentation (#1828) (#3120) Add error scenarios hub and test pages (#2497) docs: document polyfill requirements for web-targeted server bundles (#3092) docs: RSC integration pitfalls from tutorial app (#3087) docs: fix render function/helper API documentation (#3088) Doctor: accept TS/TSX server bundle suffixes (#3111) feat: add CI guard requiring sidebar updates when adding docs (#3089)
Summary
react_on_rails:doctorcould report a false warning whenserver_bundle_js_filewasserver-bundle.jsbut the Shakapacker source entrypoint file wasserver-bundle.ts.This PR makes doctor resolve common source suffixes (
.js,.jsx,.ts,.tsx,.mjs,.cjs) before warning, and updates doctor messaging to clarify TS/TSX source entrypoints are valid.Pull Request checklist
Update documentationOther Information
Issue searches in
shakacode/react_on_railsdid not find an existing issue for this exact suffix false-positive.Note
Low Risk
Low risk: changes only adjust
react_on_rails:doctorpath detection to reduce false warnings; no runtime rendering or security-sensitive logic is modified.Overview
Doctor now accepts non-
.jsserver bundle source entrypoints. Whenreact_on_rails:doctordetermines the server bundle path, it now tries common source extensions (.js,.jsx,.ts,.tsx,.mjs,.cjs) before warning that the configured bundle is missing, avoiding false positives for apps usingserver-bundle.ts.Adds focused specs for the new extension-resolution behavior and documents the fix in the
CHANGELOG.Reviewed by Cursor Bugbot for commit 7570857. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by CodeRabbit
Bug Fixes
react_on_rails:doctornow correctly resolves server bundle files with alternate extensions (.js, .jsx, .ts, .tsx, .mjs, .cjs), preventing false warnings for missing bundles when using TypeScript configurations.Tests