Doctor: query runtime config instead of regex-parsing initializers#2823
Doctor: query runtime config instead of regex-parsing initializers#2823
Conversation
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughThe doctor now prefers runtime configuration from Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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 |
Greptile SummaryThis PR upgrades the Key changes:
Issues found:
Confidence Score: 3/5
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Doctor check invoked] --> B{ensure_rails_environment_loaded?}
B -- yes --> C[react_on_rails_runtime_configuration\n= ReactOnRails.configuration]
B -- no --> D[react_on_rails_runtime_configuration = nil]
C --> E{runtime_config present?}
D --> E
E -- yes --> F[Use live config values\nserver_bundle_js_file, prerender,\npool_size, timeout, etc.]
E -- no --> G[Regex-parse initializer file\nconfig/initializers/react_on_rails.rb]
F --> H[check_react_on_rails_initializer\ncheck_server_bundle_prerender_consistency\nserver_bundle_filename]
G --> H
B -- yes --> I{defined? ReactOnRailsPro?}
I -- yes --> J[resolved_pro_server_renderer\n= ReactOnRailsPro.configuration.server_renderer]
I -- no --> K{pro_initializer_has_node_renderer?}
K -- yes --> L[resolved_pro_server_renderer = NodeRenderer]
K -- no --> M[resolved_pro_server_renderer = nil]
J --> N[check_server_rendering_engine]
L --> N
M --> N
|
|
test |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Memoization fails when Rails environment cannot load
- I changed the conditional assignment to an if/end expression so the instance variable is always assigned (including nil), allowing defined?-based memoization to work on subsequent calls.
Or push these changes by commenting:
@cursor push 8bce0f4194
Preview (8bce0f4194)
diff --git a/react_on_rails/lib/react_on_rails/doctor.rb b/react_on_rails/lib/react_on_rails/doctor.rb
--- a/react_on_rails/lib/react_on_rails/doctor.rb
+++ b/react_on_rails/lib/react_on_rails/doctor.rb
@@ -2333,7 +2333,10 @@
def react_on_rails_runtime_configuration
return @react_on_rails_runtime_configuration if defined?(@react_on_rails_runtime_configuration)
- @react_on_rails_runtime_configuration = ReactOnRails.configuration if ensure_rails_environment_loaded
+ @react_on_rails_runtime_configuration =
+ if ensure_rails_environment_loaded
+ ReactOnRails.configuration
+ end
rescue StandardError => e
checker.add_warning("⚠️ Could not query React on Rails runtime configuration: #{e.message}")
@react_on_rails_runtime_configuration = nil|
Superseded by the follow-up comment with corrected formatting: #2823 (comment) |
|
Addressed the Greptile findings in commit 2bd1c3a.\n\nChanges made:\n- Fixed runtime-config memoization so nil is memoized when Rails environment cannot be loaded, preventing repeated re-entry.\n- Updated the server bundle/prerender consistency check to run when runtime config is available even if config/initializers/react_on_rails.rb is missing.\n- Tightened random_dom_id runtime reporting so it is emitted only when explicitly false (not nil).\n- Added regression coverage for all three cases in doctor specs.\n\nRe-validated:\n- bundle exec rspec react_on_rails/spec/lib/react_on_rails/doctor_spec.rb\n- bundle exec rubocop react_on_rails/lib/react_on_rails/doctor.rb react_on_rails/spec/lib/react_on_rails/doctor_spec.rb |
There was a problem hiding this comment.
Actionable comments posted: 2
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/react_on_rails/doctor.rb (1)
750-769:⚠️ Potential issue | 🟠 MajorDon't let the initializer guard bypass the new runtime path.
Line 754 and Line 1330 still short-circuit on
config/initializers/react_on_rails.rb, so apps that setReactOnRails.configurationsomewhere else will either be reported as missing config or skip the prerender/server-bundle consistency check entirely even whenreact_on_rails_runtime_configurationis already available. If you want to keep the missing-file warning, that's fine, but the runtime-derived diagnostics should still run whenruntime_configexists.💡 Suggested change
def check_react_on_rails_initializer config_path = "config/initializers/react_on_rails.rb" runtime_config = react_on_rails_runtime_configuration + initializer_exists = File.exist?(config_path) - unless File.exist?(config_path) + unless runtime_config || initializer_exists checker.add_warning("⚠️ React on Rails configuration file not found: #{config_path}") checker.add_info("💡 Run 'rails generate react_on_rails:install' to create configuration file") return end begin - content = File.read(config_path) + content = initializer_exists ? File.read(config_path) : "" ... end end def check_server_bundle_prerender_consistency config_path = "config/initializers/react_on_rails.rb" - return unless File.exist?(config_path) + runtime_config = react_on_rails_runtime_configuration + return unless runtime_config || File.exist?(config_path) checker.add_info("\n🔍 Server Rendering Consistency:") begin - runtime_config = react_on_rails_runtime_configuration if runtime_config ...Also applies to: 1328-1349
🤖 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 750 - 769, The initializer check currently returns early when config/initializers/react_on_rails.rb is missing, which prevents runtime-derived diagnostics from running even if react_on_rails_runtime_configuration is present; update check_react_on_rails_initializer to only return early when the file is missing AND react_on_rails_runtime_configuration is nil — if runtime_config exists, emit the missing-file warning via checker.add_warning/checker.add_info but continue to read/use runtime_config and run the subsequent diagnostics (references: check_react_on_rails_initializer, react_on_rails_runtime_configuration, checker.add_warning); make the same change in the other similar block (around the check that currently short-circuits at lines ~1328-1349) so runtime-derived checks always run when runtime_config is available.
🤖 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 1296-1300: The current logic uses configured_value.present? which
discards an intentionally empty runtime server_bundle_js_file; instead, when
react_on_rails_runtime_configuration is present return its server_bundle_js_file
value unchanged (including empty string) so callers can treat blank as
SSR-disabled. Update the block that reads react_on_rails_runtime_configuration
and server_bundle_js_file to return the runtime value directly when
runtime_config is non-nil (reference react_on_rails_runtime_configuration and
server_bundle_js_file) rather than only returning when present?.
- Around line 677-678: The code currently calls
ReactOnRails::Utils.react_on_rails_pro? again when setting uses_node_renderer,
which can re-run Pro license validation; instead use the result of
resolved_pro_server_renderer to determine availability and type. Change the
uses_node_renderer logic to rely only on pro_renderer (e.g., check that
pro_renderer is non-nil and equals "NodeRenderer") and remove the duplicate
ReactOnRails::Utils.react_on_rails_pro? invocation so
resolved_pro_server_renderer is the single source of truth for Pro availability.
---
Outside diff comments:
In `@react_on_rails/lib/react_on_rails/doctor.rb`:
- Around line 750-769: The initializer check currently returns early when
config/initializers/react_on_rails.rb is missing, which prevents runtime-derived
diagnostics from running even if react_on_rails_runtime_configuration is
present; update check_react_on_rails_initializer to only return early when the
file is missing AND react_on_rails_runtime_configuration is nil — if
runtime_config exists, emit the missing-file warning via
checker.add_warning/checker.add_info but continue to read/use runtime_config and
run the subsequent diagnostics (references: check_react_on_rails_initializer,
react_on_rails_runtime_configuration, checker.add_warning); make the same change
in the other similar block (around the check that currently short-circuits at
lines ~1328-1349) so runtime-derived checks always run when runtime_config is
available.
🪄 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: f3552bc6-b883-47f6-b5b2-18f8235eacdd
📒 Files selected for processing (2)
react_on_rails/lib/react_on_rails/doctor.rbreact_on_rails/spec/lib/react_on_rails/doctor_spec.rb
Review SummaryThe direction is good — preferring runtime config over regex-parsing is more reliable and handles dynamic configuration (env-based values, computed expressions, multi-file initializers) that static parsing can never get right. Bug`server_bundle_filename` empty-string fallback (see inline at line 1299): when `server_bundle_js_file` is explicitly set to "" (SSR disabled), `configured_value.present?` is false so the method falls through to regex/default and returns "server-bundle.js". Downstream, `determine_server_bundle_path` will then construct a path for a file that does not exist and the doctor may report false-positive warnings about a missing bundle. Design inconsistencies
MinorThe `react_on_rails_runtime_configuration` memoization is correct. One edge case worth a comment: if `ReactOnRails.configuration` raises (rescue fires), `ensure_rails_environment_loaded` remains true, so `resolved_pro_server_renderer` will still attempt `ReactOnRailsPro.configuration`. Not a safety issue, just worth documenting. (See inline at line 2334.) TestsThe new specs are well-structured and cover the key scenarios (runtime-over-file precedence, nil memoization, NodeRenderer detection). The `around { Dir.mktmpdir }` pattern is clean. One suggestion: add a test for the `server_bundle_filename` empty-string case to lock in the intended behaviour once the bug above is resolved. |
There was a problem hiding this comment.
♻️ Duplicate comments (2)
react_on_rails/lib/react_on_rails/doctor.rb (2)
677-678:⚠️ Potential issue | 🟡 MinorAvoid re-running Pro license checks in server rendering engine detection.
Line 678 calls
ReactOnRails::Utils.react_on_rails_pro?again even thoughresolved_pro_server_rendereralready resolved Pro availability. This can re-trigger license validation and mask the original diagnostic path.💡 Suggested fix
- uses_node_renderer = ReactOnRails::Utils.react_on_rails_pro? && pro_renderer == "NodeRenderer" + uses_node_renderer = pro_renderer == "NodeRenderer"Based on learnings:
ReactOnRails::Utils.react_on_rails_pro?validates licenses and should fail early, so redundant calls can introduce extra failure paths.🤖 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 677 - 678, The code currently calls ReactOnRails::Utils.react_on_rails_pro? again when computing uses_node_renderer which can re-run Pro license validation; instead, derive uses_node_renderer solely from resolved_pro_server_renderer (e.g. set uses_node_renderer = resolved_pro_server_renderer == "NodeRenderer") and remove the duplicate ReactOnRails::Utils.react_on_rails_pro? invocation so license checks remain controlled by resolved_pro_server_renderer resolution logic (update the assignment that defines uses_node_renderer and any related conditional logic around it).
1296-1300:⚠️ Potential issue | 🟠 MajorPreserve empty runtime
server_bundle_js_filevalues.Line 1299 uses
.present?, so an intentional runtime empty string falls back to initializer/default parsing and can incorrectly reportserver-bundle.js.💡 Suggested fix
def server_bundle_filename runtime_config = react_on_rails_runtime_configuration - if runtime_config - configured_value = runtime_config.server_bundle_js_file - return configured_value if configured_value.present? - end + return runtime_config.server_bundle_js_file if runtime_config🤖 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 1296 - 1300, The current check in react_on_rails_runtime_configuration lookup in doctor.rb uses .present? and therefore ignores intentional empty-string values for server_bundle_js_file; change the conditional to return runtime_config.server_bundle_js_file when it is set (i.e., not nil) rather than when it is non-blank—update the logic around react_on_rails_runtime_configuration / server_bundle_js_file to use a nil check (or `unless configured_value.nil?`) so an explicit empty string is preserved instead of falling back to the initializer/default.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@react_on_rails/lib/react_on_rails/doctor.rb`:
- Around line 677-678: The code currently calls
ReactOnRails::Utils.react_on_rails_pro? again when computing uses_node_renderer
which can re-run Pro license validation; instead, derive uses_node_renderer
solely from resolved_pro_server_renderer (e.g. set uses_node_renderer =
resolved_pro_server_renderer == "NodeRenderer") and remove the duplicate
ReactOnRails::Utils.react_on_rails_pro? invocation so license checks remain
controlled by resolved_pro_server_renderer resolution logic (update the
assignment that defines uses_node_renderer and any related conditional logic
around it).
- Around line 1296-1300: The current check in
react_on_rails_runtime_configuration lookup in doctor.rb uses .present? and
therefore ignores intentional empty-string values for server_bundle_js_file;
change the conditional to return runtime_config.server_bundle_js_file when it is
set (i.e., not nil) rather than when it is non-blank—update the logic around
react_on_rails_runtime_configuration / server_bundle_js_file to use a nil check
(or `unless configured_value.nil?`) so an explicit empty string is preserved
instead of falling back to the initializer/default.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 84d4f404-f6a2-4ccf-8416-8ee8badf039a
📒 Files selected for processing (2)
react_on_rails/lib/react_on_rails/doctor.rbreact_on_rails/spec/lib/react_on_rails/doctor_spec.rb
Review SummaryThe core idea — prefer A few things worth addressing before merge: Bugs / correctness
Diagnostic output consistency
ScopeUnrelated TypeScript refactors (inline on Overall the PR is a solid improvement. The three items above are all small fixes. |
size-limit report 📦
|
Review SummaryThe core idea is solid — querying Issues foundBug — nil vs.
Minor — memoization bypass in The UX — spurious warning when runtime config is available but no initializer file (see inline comment on line 766) When the Rails environment loads successfully and Positive notes
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Memoization bypassed on every call to renderer method
- I swapped the guard order in resolved_pro_server_renderer so memoized values return before any Pro check, preventing unnecessary re-evaluation and preserving cached results.
Or push these changes by commenting:
@cursor push 163c8620b8
Preview (163c8620b8)
diff --git a/react_on_rails/lib/react_on_rails/doctor.rb b/react_on_rails/lib/react_on_rails/doctor.rb
--- a/react_on_rails/lib/react_on_rails/doctor.rb
+++ b/react_on_rails/lib/react_on_rails/doctor.rb
@@ -2351,8 +2351,8 @@
end
def resolved_pro_server_renderer
+ return @resolved_pro_server_renderer if defined?(@resolved_pro_server_renderer)
return nil unless ReactOnRails::Utils.react_on_rails_pro?
- return @resolved_pro_server_renderer if defined?(@resolved_pro_server_renderer)
@resolved_pro_server_renderer =
if ensure_rails_environment_loaded && defined?(ReactOnRailsPro)There was a problem hiding this comment.
Actionable comments posted: 2
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/react_on_rails/doctor.rb (1)
685-692:⚠️ Potential issue | 🟡 MinorGate the ExecJS fallback warning on the actual fallback setting.
This branch now resolves
NodeRendererfrom runtime config, but it still assumesrenderer_use_fallback_exec_jsis enabled wheneverNodeRendereris active. Apps that deliberately disable the fallback will still get the “ExecJS fallback is enabled” warning here whenExecJSis absent.🤖 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 685 - 692, The code assumes the ExecJS fallback is enabled whenever NodeRenderer is used and warns even if the app has disabled the fallback; update the branch that handles uses_node_renderer in react_on_rails/doctor.rb to check the actual renderer_use_fallback_exec_js configuration before emitting the ExecJS warning: only call checker.add_warning(" ⚠️ ExecJS fallback is enabled but ExecJS is not available") and the subsequent checker.add_info hint when renderer_use_fallback_exec_js is true (and ExecJS is missing); keep the existing ExecJS.runtime availability check and messages otherwise, and reference the uses_node_renderer boolean, NodeRenderer runtime resolution, and renderer_use_fallback_exec_js setting when locating the code to change.
🤖 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 201-203: Change the SSR fast-path check to use blank semantics
instead of empty? by replacing server_bundle_filename.to_s.empty? with
server_bundle_filename.to_s.blank? (or equivalent strip-empty check), and update
the add_info message in the same block (the string passed to checker.add_info)
to avoid the exact phrase "server bundle" (e.g., use "server-side bundle") so it
won't trigger the later literal-match rebuild hint; refer to
server_bundle_filename and checker.add_info in react_on_rails/doctor.rb.
- Around line 795-801: The doctor currently treats nil and empty-string runtime
values the same; update the runtime-config reporting in ReactOnRails::Doctor to
distinguish nil (meaning "use initializer/default") from "" (meaning
"disabled"). Specifically, in the blocks that read
runtime_config.server_bundle_js_file (and the similar block around the
server_bundle handling at the other occurrence), change the branching to: if
server_bundle_value.nil? then report it as falling back to initializer/default,
elsif server_bundle_value == "" then report as disabled, else report the actual
value via checker.add_info. Ensure you update both places that read
server_bundle_js_file so nil and "" have consistent meanings.
---
Outside diff comments:
In `@react_on_rails/lib/react_on_rails/doctor.rb`:
- Around line 685-692: The code assumes the ExecJS fallback is enabled whenever
NodeRenderer is used and warns even if the app has disabled the fallback; update
the branch that handles uses_node_renderer in react_on_rails/doctor.rb to check
the actual renderer_use_fallback_exec_js configuration before emitting the
ExecJS warning: only call checker.add_warning(" ⚠️ ExecJS fallback is enabled
but ExecJS is not available") and the subsequent checker.add_info hint when
renderer_use_fallback_exec_js is true (and ExecJS is missing); keep the existing
ExecJS.runtime availability check and messages otherwise, and reference the
uses_node_renderer boolean, NodeRenderer runtime resolution, and
renderer_use_fallback_exec_js setting when locating the code to change.
🪄 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: 77835fcc-af6e-4472-8d05-5c152e6d5b7e
📒 Files selected for processing (4)
packages/create-react-on-rails-app/src/create-app.tspackages/create-react-on-rails-app/src/index.tsreact_on_rails/lib/react_on_rails/doctor.rbreact_on_rails/spec/lib/react_on_rails/doctor_spec.rb
✅ Files skipped from review due to trivial changes (2)
- packages/create-react-on-rails-app/src/index.ts
- packages/create-react-on-rails-app/src/create-app.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- react_on_rails/spec/lib/react_on_rails/doctor_spec.rb
…hs (#2824) ## Summary - prioritize exact `Shakapacker.config.assets_bundler_config_path` as the first webpack config candidate in `ConfigPathResolver` - keep directory-derived and default webpack/rspack fallback candidates after the exact path - make `SystemChecker#bundler_config_file_exists?` use resolver-based path detection so custom config paths are recognized - update doctor missing-config messaging to reference generic bundler config discovery instead of only `config/webpack/webpack.config.js` - add coverage for exact-path precedence and custom-path detection in doctor/system checker specs ## Testing - `bundle exec rspec react_on_rails/spec/lib/react_on_rails/doctor_spec.rb react_on_rails/spec/lib/react_on_rails/system_checker_spec.rb` - `bundle exec rubocop react_on_rails/lib/react_on_rails/config_path_resolver.rb react_on_rails/lib/react_on_rails/doctor.rb react_on_rails/lib/react_on_rails/system_checker.rb react_on_rails/spec/lib/react_on_rails/doctor_spec.rb react_on_rails/spec/lib/react_on_rails/system_checker_spec.rb` Fixes #2633 <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes bundler config discovery/selection logic (including candidate ordering and shakapacker-derived paths), which can alter which webpack/rspack config file is detected and what guidance/warnings are emitted during doctor/install checks. > > **Overview** > **Improves bundler config path detection for Doctor/SystemChecker**, especially when apps use a custom `Shakapacker.config.assets_bundler_config_path`. > > Config resolution now **prioritizes the exact Shakapacker-configured path**, probes standard `webpack.config.*`/`rspack.config.*` siblings in the same directory (including `.cjs`/`.mjs` there), and falls back to deterministic default locations with **`.js` preferred over `.ts`**. Doctor and SystemChecker messaging is updated to refer to generic *bundler configuration* discovery (not just `config/webpack/webpack.config.js`), and specs add coverage for these precedence/ambiguity cases. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit c653830. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> ### Note - Behavior change: config candidate ordering now prefers before for both webpack and rspack candidates (including default fallbacks) to match current generator output and keep selection deterministic when both files exist. ### Note - Behavior change: config candidate ordering now prefers `.js` before `.ts` for both webpack and rspack candidates (including default fallbacks) to match current generator output and keep selection deterministic when both files exist.
ReviewWell-structured refactor that improves diagnostic accuracy by preferring runtime config over brittle regex parsing. The defined?(@ivar) memoization to cache nil is correct and well-commented. Test coverage of runtime-vs-initializer precedence is thorough. Behavioral change worth documenting: the runtime path now omits settings at their default values (pool size, timeout, trace, logging_on_server, etc.), whereas the initializer-fallback path always showed defaults explicitly. Users auditing their full config may be surprised when entries silently disappear. Adding a note in the doctor output like 'Default-valued settings are omitted (see docs)' would make this self-documenting. ensure_rails_environment_loaded warning deduplication confirmed: both react_on_rails_runtime_configuration and resolved_pro_server_renderer call ensure_rails_environment_loaded independently. @rails_environment_attempted is set before the require call, so if require throws, the rescue returns false but the flag stays set. The warning fires exactly once across all callers. Good design. analyze_server_rendering_config falsy non-nil edge: the present? / nil? / else triple-branch silently maps false to the empty-string-disabled message. server_bundle_js_file should always be String or nil per the schema so this is harmless in practice, but a comment near is_a?(String) would clarify intent for future readers. create-app.ts simplification looks good. The refactor preserves behavior correctly and the type narrowing to the literal union type is an improvement. |
| @shakapacker_assets_bundler_config_path = nil | ||
| rescue StandardError => e | ||
| message = "ReactOnRails could not read Shakapacker assets_bundler_config_path: #{e.class}: #{e.message}" | ||
| warn(message) unless Rails.logger |
There was a problem hiding this comment.
The warn call here outputs to stderr when Rails.logger is nil. Since this is a debug-level diagnostic (Shakapacker config read failure), consider whether stderr noise is acceptable in environments where Rails logger hasn't been set up yet (e.g. during early boot). The logic is correct — warn fires only when logger is absent, logger.debug fires when present — but the severity mismatch (warn-to-stderr for a debug situation) may surprise operators.
There was a problem hiding this comment.
Good callout. I’m keeping this behavior as-is for now: when Rails.logger is unavailable, this fallback path uses warn intentionally so configuration-read failures are still visible during early boot/diagnostics. Once logger is available, it stays at debug level via Rails.logger.debug.
| if runtime_config | ||
| raw_server_bundle_value = runtime_config.server_bundle_js_file | ||
| server_bundle_value = | ||
| if raw_server_bundle_value.is_a?(String) |
There was a problem hiding this comment.
The else branch here fires for any falsy, non-nil value of server_bundle_js_file (e.g. false). Since the config schema only allows String or nil, this is unreachable in practice. A brief comment like # configured value is always String | nil near the is_a?(String) guard would save the next reader from having to trace through config defaults to reach the same conclusion.
There was a problem hiding this comment.
Thanks. I’m leaving this unchanged for now. The branch is defensive to preserve clear handling of blank/disabled semantics, and in practice config values are constrained by the configuration layer. I agree a clarifying comment could help, but I’m keeping this PR focused on behavior fixes.
| @resolved_pro_server_renderer = | ||
| if rails_environment_loaded && defined?(ReactOnRailsPro) | ||
| # server_renderer is stored as a plain string in Pro config (for example, "NodeRenderer"). | ||
| ReactOnRailsPro.configuration.server_renderer.to_s |
There was a problem hiding this comment.
Minor: if ReactOnRailsPro.configuration returns nil for some reason (e.g. Pro not fully initialized), this will raise NoMethodError which is correctly caught by the rescue below. The rescue coverage is sufficient but a &. safe-navigator here (ReactOnRailsPro.configuration&.server_renderer.to_s) would make the nil-tolerance explicit.
There was a problem hiding this comment.
Agreed the rescue already covers this. I’m keeping the explicit call (without safe navigation) so unexpected nil/incomplete Pro initialization still takes the warning path rather than silently coercing to an empty renderer value.
|
Addressed the outside-diff fallback warning concern from review Change made:
|
Code ReviewOverall well-structured. Moving Doctor diagnostics from brittle regex parsing to runtime config queries is the right call, and the fallback chain is clearly documented. Test coverage is thorough. Correctness1. Silent fallback when Rails is loaded but ReactOnRailsPro is not defined In 2. Every other field in the runtime-config path suppresses output when the value matches the default (pool size, timeout, raise_on_prerender_error, etc.). But 3. Misleading label for the When runtime config returns Minor4. Non-obvious nil-guard pattern in The two-statement guard ( 5. A Shakapacker |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Dedup logic over-skips sibling bundler config candidate
- Updated candidate generation to reject only the exact shakapacker path so sibling standard bundler configs remain discoverable and added a regression spec covering that case.
Or push these changes by commenting:
@cursor push 45bdd4451b
Preview (45bdd4451b)
diff --git a/react_on_rails/lib/react_on_rails/config_path_resolver.rb b/react_on_rails/lib/react_on_rails/config_path_resolver.rb
--- a/react_on_rails/lib/react_on_rails/config_path_resolver.rb
+++ b/react_on_rails/lib/react_on_rails/config_path_resolver.rb
@@ -34,20 +34,14 @@
shakapacker_config_dir = bundler_config_directory(shakapacker_config_path)
if shakapacker_config_dir
- shakapacker_basename = File.basename(shakapacker_config_path.to_s)
- shakapacker_config_ext = File.extname(shakapacker_config_path.to_s).delete_prefix(".")
candidates.concat(%w[js ts cjs mjs].flat_map do |ext|
- # Skip only exact standard-name duplicates. Non-standard configured
- # paths (for example `custom.config.cjs`) still probe standard-name
- # fallbacks in the same directory; any accidental duplicates are
- # de-duplicated by `candidates.uniq` below.
- config_basenames = ["webpack.config.#{ext}", "rspack.config.#{ext}"]
- next [] if ext == shakapacker_config_ext && config_basenames.include?(shakapacker_basename)
-
+ # Skip only the exact shakapacker path; still probe sibling
+ # standard-name configs (for example rspack when shakapacker points to
+ # webpack in the same directory).
[
File.join(shakapacker_config_dir, "webpack.config.#{ext}"),
File.join(shakapacker_config_dir, "rspack.config.#{ext}")
- ]
+ ].reject { |path| path == shakapacker_config_path }
end)
end
diff --git a/react_on_rails/spec/lib/react_on_rails/doctor_spec.rb b/react_on_rails/spec/lib/react_on_rails/doctor_spec.rb
--- a/react_on_rails/spec/lib/react_on_rails/doctor_spec.rb
+++ b/react_on_rails/spec/lib/react_on_rails/doctor_spec.rb
@@ -1879,6 +1879,18 @@
expect(doctor.send(:resolved_webpack_config_path)).to eq("config/custom/custom-bundler.config.js")
end
+ it "keeps sibling bundler candidates when shakapacker path uses a standard filename" do
+ allow(File).to receive(:file?).and_return(false)
+ allow(File).to receive(:file?).with("config/webpack/rspack.config.js").and_return(true)
+ allow(doctor).to receive(:bundler_config_directory)
+ .with("config/webpack/webpack.config.js")
+ .and_return("config/webpack")
+ allow(doctor).to receive(:shakapacker_assets_bundler_config_path)
+ .and_return("config/webpack/webpack.config.js")
+
+ expect(doctor.send(:resolved_webpack_config_path)).to eq("config/webpack/rspack.config.js")
+ end
+
it "falls back to shakapacker-derived webpack config candidates when exact shakapacker path is not a file" do
allow(File).to receive(:file?).and_return(false)
allow(File).to receive(:file?).with("config/custom/webpack.config.ts").and_return(true)You can send follow-ups to this agent here.
| if rails_environment_loaded && defined?(ReactOnRailsPro) | ||
| # server_renderer is stored as a plain string in Pro config (for example, "NodeRenderer"). | ||
| ReactOnRailsPro.configuration.server_renderer.to_s | ||
| elsif pro_initializer_has_node_renderer? |
There was a problem hiding this comment.
When rails_environment_loaded is true but defined?(ReactOnRailsPro) is false, the code falls through here to regex parsing without any warning. This means Rails loaded successfully but the Pro constant is absent — likely a gem load failure. The regex parse could return "NodeRenderer" even if the Pro gem never actually loaded, producing a false positive diagnostic. Suggest adding an explicit warning branch:
| elsif pro_initializer_has_node_renderer? | |
| elsif rails_environment_loaded && !defined?(ReactOnRailsPro) | |
| checker.add_warning( | |
| "⚠️ Rails environment loaded but ReactOnRailsPro constant is not defined. " \ | |
| "The Pro gem may have failed to load; falling back to initializer parsing." | |
| ) | |
| pro_initializer_has_node_renderer? ? "NodeRenderer" : nil | |
| elsif pro_initializer_has_node_renderer? |
| rails_bundle_path = server_bundle_path_match ? server_bundle_path_match[1] : default_path | ||
| rails_bundle_path = | ||
| if runtime_config | ||
| runtime_config.server_bundle_output_path || default_path |
There was a problem hiding this comment.
Unlike server_renderer_pool_size, server_renderer_timeout, and component_registry_timeout (which are suppressed when equal to their defaults), server_bundle_output_path is always emitted. For the vast majority of apps this will always print ssr-generated. Suggest applying the same default-suppression pattern:
| runtime_config.server_bundle_output_path || default_path | |
| rails_bundle_path = | |
| if runtime_config | |
| runtime_config.server_bundle_output_path || default_path | |
| else | |
| server_bundle_path_match = content.match(/config\.server_bundle_output_path\s*=\s*["']([^"']+)["']/) | |
| server_bundle_path_match ? server_bundle_path_match[1] : default_path | |
| end | |
| checker.add_info(" server_bundle_output_path: #{rails_bundle_path}") if rails_bundle_path != default_path |
| if server_bundle_value.present? | ||
| checker.add_info(" server_bundle_js_file: #{server_bundle_value}") | ||
| elsif server_bundle_value.nil? | ||
| fallback_server_bundle = server_bundle_filename |
There was a problem hiding this comment.
When server_bundle_js_file is nil in the runtime config (i.e. no one ever called config.server_bundle_js_file =), server_bundle_filename falls back through initializer parsing and ultimately returns "server-bundle.js" — the hardcoded default. The label "(initializer/default)" then gets printed even for a plain new app with no initializer at all, which is confusing. Consider distinguishing the two sub-cases:
| fallback_server_bundle = server_bundle_filename | |
| elsif server_bundle_value.nil? | |
| fallback_server_bundle = server_bundle_filename | |
| label = File.exist?("config/initializers/react_on_rails.rb") ? "(from initializer/default)" : "(default)" | |
| checker.add_info(" server_bundle_js_file: #{fallback_server_bundle} #{label}") |
| @shakapacker_assets_bundler_config_path = nil | ||
| rescue StandardError => e | ||
| message = "ReactOnRails could not read Shakapacker assets_bundler_config_path: #{e.class}: #{e.message}" | ||
| warn(message) unless Rails.logger |
There was a problem hiding this comment.
The two sequential statements are correct — warn fires when logger is nil, &.debug fires when logger is present — but easy to misread as both always executing. A single if/else makes the mutual exclusion explicit:
| warn(message) unless Rails.logger | |
| if Rails.logger | |
| Rails.logger.debug { message } | |
| else | |
| warn(message) | |
| end |
| candidates.concat(WEBPACK_DEFAULT_CONFIG_CANDIDATES) | ||
| candidates.concat(RSPACK_DEFAULT_CONFIG_CANDIDATES) | ||
| candidates.uniq |
There was a problem hiding this comment.
The comment says .uniq removes "accidental" duplicates, but when Shakapacker's assets_bundler_config_path happens to equal a default candidate (e.g. config/webpack/webpack.config.js), that path is intentionally inserted twice — first at line 33 as the explicit Shakapacker path, then again here via concat(WEBPACK_DEFAULT_CONFIG_CANDIDATES). The .uniq is therefore load-bearing correctness for this case, not just a safety net. Worth clarifying the comment so future readers don't treat .uniq as removable cleanup:
| candidates.concat(WEBPACK_DEFAULT_CONFIG_CANDIDATES) | |
| candidates.concat(RSPACK_DEFAULT_CONFIG_CANDIDATES) | |
| candidates.uniq | |
| # Default fallback candidates intentionally mirror generator defaults | |
| # (`.js` / `.ts`), while `.cjs` / `.mjs` are probed only within resolved | |
| # shakapacker config directories above. | |
| # NOTE: When Shakapacker's assets_bundler_config_path is itself one of | |
| # these defaults (e.g. config/webpack/webpack.config.js), it will appear | |
| # in both the explicit-path slot above and here. The `.uniq` call below is | |
| # load-bearing for that case, not just a cleanup step. | |
| candidates.concat(WEBPACK_DEFAULT_CONFIG_CANDIDATES) | |
| candidates.concat(RSPACK_DEFAULT_CONFIG_CANDIDATES) |
| checker.add_info(" ExecJS available as fallback: #{ExecJS.runtime.name}") | ||
| else | ||
| elsif pro_execjs_fallback_enabled? | ||
| checker.add_warning(" ⚠️ ExecJS fallback is enabled but ExecJS is not available") |
There was a problem hiding this comment.
The warning text here is misleading. This branch fires when defined?(ExecJS) is truthy but ExecJS.runtime is nil — meaning the ExecJS gem is available (defined in the process), it just has no JavaScript runtime configured. Saying "ExecJS is not available" contradicts that.
| checker.add_warning(" ⚠️ ExecJS fallback is enabled but ExecJS is not available") | |
| checker.add_warning(" ⚠️ ExecJS fallback is enabled but no ExecJS runtime is configured") |
A follow-on for the tip line:
💡 Install mini_racer or configure an ExecJS runtime, or set renderer_use_fallback_exec_js = false
|
|
||
| def resolved_webpack_config_path | ||
| webpack_config_candidates.find { |path| File.exist?(path) } | ||
| webpack_config_candidates.find { |path| File.file?(path) } |
There was a problem hiding this comment.
resolved_webpack_config_path is called from at least three places — bundler_config_file_exists?, detect_bundler_config_path, and (indirectly) explicit_shakapacker_bundler_config_path? — and each call rebuilds webpack_config_candidates and does N×File.file? syscalls. shakapacker_assets_bundler_config_path was carefully memoized to avoid re-requiring Shakapacker; resolved_webpack_config_path should get the same treatment for consistency and to avoid redundant filesystem probing in a single doctor run.
| webpack_config_candidates.find { |path| File.file?(path) } | |
| def resolved_webpack_config_path | |
| return @resolved_webpack_config_path if instance_variable_defined?(:@resolved_webpack_config_path) | |
| @resolved_webpack_config_path = webpack_config_candidates.find { |path| File.file?(path) } | |
| end |
|
|
||
| # rubocop:disable Metrics/CyclomaticComplexity | ||
| def analyze_server_rendering_config(content) | ||
| # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity |
There was a problem hiding this comment.
Five of the six analyze_* methods now carry Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity disables. The dual-path structure (if runtime_config … else … end) is repeated in each one and accounts for most of the complexity. A lightweight display-value helper that resolves each setting once could eliminate the duplication:
# Resolves a setting to a display value.
# When runtime_config is available, calls the given block to extract it.
# Falls back to regex-parsing content when not.
def resolve_config_value(runtime_config, content, regex)
if runtime_config
yield runtime_config
else
match = content.match(regex)
match ? match[1] : nil
end
endNot a blocker, but the current structure will make future settings additions expensive to maintain since every new attribute requires touching both branches in each method.
| end | ||
|
|
||
| def pro_execjs_fallback_enabled? | ||
| return ReactOnRailsPro.configuration.renderer_use_fallback_exec_js if defined?(ReactOnRailsPro) |
There was a problem hiding this comment.
Minor: defined?(ReactOnRailsPro) returns a string ("constant") when the constant exists. The string is truthy, so calling .configuration.renderer_use_fallback_exec_js on the actual constant works correctly. However, defined? doesn't guard against ReactOnRailsPro being defined but not yet fully loaded (e.g. autoload stub). The existing code in resolved_pro_server_renderer uses the same pattern, so this is consistent — just worth a note that defined? alone won't catch a NameError from a broken autoload. Adding a rescue NameError to the rescue clause on line 2437 would make the two guard strategies uniform.
| @@ -308,40 +309,29 @@ def check_webpack_configuration | |||
| 🚫 Bundler configuration not found. | |||
There was a problem hiding this comment.
detect_bundler_config_path calls resolved_webpack_config_path first, then resolve_default_bundler_config_path which calls existing_bundler_config_paths which in turn calls webpack_config_candidates again. If resolved_webpack_config_path gets memoized (see comment on config_path_resolver.rb:27), this is free. Without memoization there are two full candidate-list builds plus two sets of File.file? syscalls for every detect_bundler_config_path invocation.
Review: Doctor runtime config migrationOverall this is a solid improvement — replacing fragile regex parsing of initializer files with Issues worth fixing1. Misleading warning text ( 2. Worth considering3. Dual-path boilerplate in 4. Looks good
|
|
Thanks for the detailed review — this is helpful. Status update on the points you listed:
Separately, I did address the actionable resolver bug around candidate skipping (sibling bundler candidate loss) in commit |
…2823) ## Summary - migrate doctor configuration analysis to prefer `ReactOnRails.configuration` runtime values after environment load - keep initializer parsing as fallback when Rails environment cannot be loaded - use Pro runtime renderer detection for server rendering engine checks, with initializer fallback - update server bundle/prerender consistency checks to use runtime config values - add specs covering runtime-over-file precedence and runtime-based NodeRenderer detection ## Testing - `bundle exec rspec react_on_rails/spec/lib/react_on_rails/doctor_spec.rb` - `bundle exec rspec react_on_rails/spec/lib/react_on_rails/doctor_rake_task_spec.rb` - `bundle exec rubocop react_on_rails/lib/react_on_rails/doctor.rb react_on_rails/spec/lib/react_on_rails/doctor_spec.rb` Fixes #2771 <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Moderate risk because it changes diagnostic/config-resolution behavior (runtime vs initializer parsing, SSR bundle checks, and Shakapacker config discovery), which may affect what setups are reported as healthy or broken across diverse project layouts. > > **Overview** > Doctor now **prefers loaded runtime configuration** (`ReactOnRails.configuration`) for reporting and consistency checks, falling back to initializer regex parsing only when the Rails environment can’t be loaded; SSR bundle existence checks are skipped when `server_bundle_js_file` is blank/whitespace and default-valued settings are omitted to reduce noise. > > Bundler config discovery was reworked to use Shakapacker’s `assets_bundler_config_path` (with memoized/normalized lookup) and to probe webpack/rspack config candidates deterministically (JS before TS, `.cjs/.mjs` only in Shakapacker-derived dirs), with clearer messaging in `Doctor`/`SystemChecker` and expanded specs covering these scenarios (including Pro `NodeRenderer` detection and ExecJS fallback messaging). > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 04a8ebc. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Improvements** * Doctor now prioritizes runtime configuration over initializer parsing for SSR and performance reporting, skips SSR bundle checks when runtime bundle filename is blank/whitespace, and reduces noise by omitting default-valued settings. * Clarified diagnostics and messaging around runtime vs initializer-sourced settings. * **Tests** * Expanded specs covering runtime-vs-initializer precedence, SSR skip behavior, prerender consistency scenarios, Pro renderer resolution, and memoization. <!-- end of auto-generated comment: release notes by coderabbit.ai -->


Summary
ReactOnRails.configurationruntime values after environment loadTesting
bundle exec rspec react_on_rails/spec/lib/react_on_rails/doctor_spec.rbbundle exec rspec react_on_rails/spec/lib/react_on_rails/doctor_rake_task_spec.rbbundle exec rubocop react_on_rails/lib/react_on_rails/doctor.rb react_on_rails/spec/lib/react_on_rails/doctor_spec.rbFixes #2771
Note
Medium Risk
Moderate risk because it changes diagnostic/config-resolution behavior (runtime vs initializer parsing, SSR bundle checks, and Shakapacker config discovery), which may affect what setups are reported as healthy or broken across diverse project layouts.
Overview
Doctor now prefers loaded runtime configuration (
ReactOnRails.configuration) for reporting and consistency checks, falling back to initializer regex parsing only when the Rails environment can’t be loaded; SSR bundle existence checks are skipped whenserver_bundle_js_fileis blank/whitespace and default-valued settings are omitted to reduce noise.Bundler config discovery was reworked to use Shakapacker’s
assets_bundler_config_path(with memoized/normalized lookup) and to probe webpack/rspack config candidates deterministically (JS before TS,.cjs/.mjsonly in Shakapacker-derived dirs), with clearer messaging inDoctor/SystemCheckerand expanded specs covering these scenarios (including ProNodeRendererdetection and ExecJS fallback messaging).Written by Cursor Bugbot for commit 04a8ebc. This will update automatically on new commits. Configure here.
Summary by CodeRabbit
Improvements
Tests