Skip to content

Fix #2606: reduce doctor false positives for custom layouts#2612

Merged
justin808 merged 8 commits intomasterfrom
jg-codex/issue-2606-doctor-paths
Mar 17, 2026
Merged

Fix #2606: reduce doctor false positives for custom layouts#2612
justin808 merged 8 commits intomasterfrom
jg-codex/issue-2606-doctor-paths

Conversation

@justin808
Copy link
Copy Markdown
Member

@justin808 justin808 commented Mar 15, 2026

Summary

  • resolve package.json from ReactOnRails.configuration.node_modules_location (instead of assuming repo root)
  • broaden webpack config discovery to common custom locations/extensions and Shakapacker-configured paths
  • downgrade missing webpack config in SystemChecker#check_webpack_configuration from hard error to contextual warning
  • add regression specs for non-root JS workspace and custom webpack config discovery

Why

Fixes false positives reported in react_on_rails:doctor for apps that keep JS dependencies in a subdirectory (for example client/package.json) and/or use non-default webpack config locations.

Testing

  • bundle exec rspec react_on_rails/spec/lib/react_on_rails/system_checker_spec.rb react_on_rails/spec/lib/react_on_rails/doctor_spec.rb
  • bundle exec rubocop react_on_rails/lib/react_on_rails/system_checker.rb react_on_rails/lib/react_on_rails/doctor.rb react_on_rails/spec/lib/react_on_rails/system_checker_spec.rb react_on_rails/spec/lib/react_on_rails/doctor_spec.rb

Note

Low Risk
Low risk because changes are limited to diagnostic path detection and messaging; no runtime application behavior or data/security logic is modified.

Overview
Reduces react_on_rails:doctor false positives in non-standard project layouts by introducing ConfigPathResolver, which resolves package.json via ReactOnRails.configuration.node_modules_location and discovers webpack/rspack config files across common extensions and Shakapacker-configured locations.

Updates Doctor and SystemChecker to use these resolved paths, and changes the Doctor key-file check to report missing bundler config as a contextual warning (instead of assuming config/webpack/webpack.config.js). Adds specs covering workspace package.json resolution and bundler config discovery behavior.

Written by Cursor Bugbot for commit 8da3444. This will update automatically on new commits. Configure here.

Summary by CodeRabbit

  • New Features

    • Enhanced configuration detection for webpack and package.json files across flexible project layouts, including custom workspace locations
    • Improved diagnostic messages when configuration files are missing or misconfigured
  • Bug Fixes

    • Robust error handling for missing configuration paths
  • Documentation

    • Code formatting improvements in troubleshooting guide
  • Tests

    • Added test coverage for dynamic configuration path resolution

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 15, 2026

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

This PR introduces dynamic path resolution for package.json and webpack configuration files within the ReactOnRails diagnostic system. New resolver methods are added to both Doctor and SystemChecker classes to discover configurations in non-root JS workspaces and custom Shakapacker directories, replacing hardcoded path references with resolved paths throughout existing checks.

Changes

Cohort / File(s) Summary
Path Resolution in Doctor
react_on_rails/lib/react_on_rails/doctor.rb
Adds three resolver methods: resolved_package_json_path (derives path from node_modules_location with fallback), resolved_webpack_config_path (discovers webpack.config.* variants), and shakapacker_webpack_config_directory. Updates npm and config checks to use resolved paths instead of static references; removes static webpack config entries.
Path Resolution in SystemChecker
react_on_rails/lib/react_on_rails/system_checker.rb
Implements matching resolver methods and adds webpack_config_candidates helper. Updates all package.json references to use resolved_package_json_path across npm, webpack, and dependency checks. Modifies check_webpack_configuration, check_webpack_config_content, and suggest_webpack_inspection to accept and use resolved webpack config paths; improves error handling for missing configurations.
Doctor Tests
react_on_rails/spec/lib/react_on_rails/doctor_spec.rb
Adds tests for path resolution behavior in workspace scenarios, including package.json discovery via node_modules_location configuration and webpack config preference logic when multiple candidates exist.
SystemChecker Tests
react_on_rails/spec/lib/react_on_rails/system_checker_spec.rb
Adds tests for npm package detection in non-root workspaces, shakapacker configuration discovery with custom paths, webpack config candidate preference, and shakapacker webpack config directory extraction.
Documentation Formatting
docs/oss/migrating/rsc-troubleshooting.md
Wraps two code examples with prettier-ignore markers to prevent formatting modifications on "Use Client" directive guidance; updates surrounding commentary.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 With whiskers high, the paths now dance,
No hardcoded stance!
From root to workspace, webpack does roam,
Each configuration finds its home,
Dynamic discovery—a bunny's delight! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main objective: fixing false positives in the doctor diagnostic by handling custom layout paths, which aligns with the core changes refactoring path resolution for package.json and webpack config.
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 jg-codex/issue-2606-doctor-paths
📝 Coding Plan
  • Generate coding plan for human review comments

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.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Mar 15, 2026

Greptile Summary

This PR fixes false positives in react_on_rails:doctor for apps that use non-root JS workspaces (e.g., client/package.json) or custom webpack config locations. It resolves package.json from ReactOnRails.configuration.node_modules_location, broadens webpack config discovery to common custom paths and Shakapacker-configured directories, and downgrades missing webpack config from a hard error to a contextual warning.

  • resolved_package_json_path now respects node_modules_location config in both Doctor and SystemChecker
  • resolved_webpack_config_path searches multiple candidate paths including Shakapacker-configured directories and various extensions (.js, .ts, .cjs, .mjs)
  • Missing webpack config in SystemChecker#check_webpack_configuration downgraded from add_error to add_warning
  • Webpack config removed from the static file checklist in Doctor#check_key_configuration_files and replaced with dynamic resolution
  • Regression specs added for both non-root JS workspace and custom webpack config discovery
  • Issue: The candidate ordering in resolved_webpack_config_path differs between doctor.rb and system_checker.rb, which could lead to inconsistent results in edge cases
  • Issue: check_shakapacker_configuration error message still hardcodes config/webpack/webpack.config.js despite the check now supporting custom paths

Confidence Score: 4/5

  • This PR is safe to merge with minor issues — it reduces false positives in a diagnostic tool and does not affect core runtime behavior.
  • The changes are well-scoped and address a real user pain point. The two issues found (inconsistent candidate ordering between Doctor and SystemChecker, and a stale error message) are unlikely to cause runtime failures but could lead to confusing diagnostics output. Good test coverage is included for the new behavior.
  • Pay attention to react_on_rails/lib/react_on_rails/doctor.rb (candidate ordering inconsistency) and react_on_rails/lib/react_on_rails/system_checker.rb (stale error message in check_shakapacker_configuration).

Important Files Changed

Filename Overview
react_on_rails/lib/react_on_rails/system_checker.rb Resolves package.json and webpack config from configured paths instead of hardcoded defaults; downgrades missing webpack config from error to warning. Error message in check_shakapacker_configuration still references hardcoded path.
react_on_rails/lib/react_on_rails/doctor.rb Mirrors SystemChecker changes for package.json and webpack config resolution, but candidate ordering in resolved_webpack_config_path differs from SystemChecker and all three helper methods are duplicated.
react_on_rails/spec/lib/react_on_rails/doctor_spec.rb Adds regression test for package.json resolution from a configured JS workspace directory. Test is well-structured with appropriate mocking.
react_on_rails/spec/lib/react_on_rails/system_checker_spec.rb Adds regression tests for non-root JS workspace package.json resolution and custom webpack config path discovery in shakapacker_configured?. Tests are thorough.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Doctor / SystemChecker needs package.json] --> B{node_modules_location configured?}
    B -->|empty / Rails.root| C[Use root package.json]
    B -->|custom path e.g. 'client'| D["Use client/package.json"]
    
    E[Doctor / SystemChecker needs webpack config] --> F[Build candidate list]
    F --> G["1. config/webpack/webpack.config.js"]
    F --> H["2. Shakapacker config directory paths"]
    F --> I["3. Dir.glob config/**/webpack.config.*"]
    G --> J{First candidate that exists?}
    H --> J
    I --> J
    J -->|Found| K[Return discovered path]
    J -->|None found| L["Return nil → warning instead of error"]
Loading

Comments Outside Diff (1)

  1. react_on_rails/lib/react_on_rails/system_checker.rb, line 130-137 (link)

    Stale error message references hardcoded path

    The shakapacker_configured? method now accepts custom webpack config paths via resolved_webpack_config_path, but this error message still hardcodes config/webpack/webpack.config.js as a bullet point. If a user has a custom webpack path and another required file (e.g., bin/shakapacker) is missing, this message will misleadingly suggest their webpack config is also missing.

    Consider updating the message to reflect that any discoverable webpack config path is acceptable:

Last reviewed commit: eea9116

Comment thread react_on_rails/lib/react_on_rails/doctor.rb Outdated
Comment thread react_on_rails/lib/react_on_rails/doctor.rb Outdated
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 15, 2026

Review

The direction here is solid — resolving package.json via node_modules_location and broadening webpack config discovery are good fixes for the reported false positives.

Code duplication between doctor.rb and system_checker.rb

resolved_package_json_path, resolved_webpack_config_path / webpack_config_candidates, and shakapacker_webpack_config_directory are implemented nearly identically in both files, but with a subtle inconsistency in candidate ordering (see inline comment). These helpers should live in a shared module (e.g. ReactOnRails::DiagnosticHelpers) so future changes do not silently diverge.

parse_package_json rescues only JSON::ParserError

File.read raises Errno::ENOENT when the file is absent — not JSON::ParserError. The method is safe today because check_react_dependencies guards with File.exist? beforehand, but parse_package_json is not independently safe to call. Consider adding Errno::ENOENT to the rescue clause or adding a guard inside the method itself.

Minor nits

  • check_webpack_config_content uses resolved_webpack_config_path as a default-argument expression. This re-runs the filesystem search on every call that omits the argument, creating a subtle performance trap for future callers.
  • The missing-webpack-config warning still ends with 'Run: rails generate react_on_rails:install', which is misleading for apps that intentionally use a custom config path.

Comment thread react_on_rails/lib/react_on_rails/doctor.rb Outdated
Comment thread react_on_rails/lib/react_on_rails/system_checker.rb
Comment thread react_on_rails/lib/react_on_rails/system_checker.rb
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Autofix Details

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: Inconsistent webpack config search order between files
    • I moved both classes to a shared resolver that prioritizes Shakapacker-configured webpack config candidates before glob matches, so they now resolve the same file.
  • ✅ Fixed: Duplicated helper methods across doctor and system checker
    • I extracted the duplicated path-resolution helpers into a single ConfigPathResolver module and included it in both classes to keep logic unified.

Create PR

Or push these changes by commenting:

@cursor push 2b3b6e1e5a
Preview (2b3b6e1e5a)
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
new file mode 100644
--- /dev/null
+++ b/react_on_rails/lib/react_on_rails/config_path_resolver.rb
@@ -1,0 +1,43 @@
+# frozen_string_literal: true
+
+module ReactOnRails
+  module ConfigPathResolver
+    private
+
+    def resolved_package_json_path
+      node_modules_location = ReactOnRails.configuration.node_modules_location.to_s
+      return "package.json" if node_modules_location.empty? || node_modules_location == Rails.root.to_s
+
+      File.join(node_modules_location, "package.json")
+    rescue StandardError
+      "package.json"
+    end
+
+    def resolved_webpack_config_path
+      webpack_config_candidates.find { |path| File.exist?(path) }
+    end
+
+    def webpack_config_candidates
+      candidates = ["config/webpack/webpack.config.js"]
+
+      shakapacker_config_dir = shakapacker_webpack_config_directory
+      if shakapacker_config_dir
+        candidates.concat(%w[js ts cjs mjs].map { |ext| File.join(shakapacker_config_dir, "webpack.config.#{ext}") })
+      end
+
+      candidates.concat(Dir.glob("config/**/webpack.config.{js,ts,cjs,mjs}"))
+      candidates.uniq
+    end
+
+    def shakapacker_webpack_config_directory
+      require "shakapacker"
+      path = Shakapacker.config.assets_bundler_config_path.to_s
+      return nil if path.empty?
+
+      rails_root = Rails.root.to_s
+      path.start_with?("#{rails_root}/") ? path.sub("#{rails_root}/", "") : path
+    rescue LoadError, StandardError
+      nil
+    end
+  end
+end

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
@@ -5,6 +5,7 @@
 require "yaml"
 require_relative "utils"
 require_relative "version_syntax_converter"
+require_relative "config_path_resolver"
 require_relative "system_checker"
 
 begin
@@ -39,6 +40,8 @@
 module ReactOnRails
   # rubocop:disable Metrics/ClassLength, Metrics/AbcSize
   class Doctor
+    include ConfigPathResolver
+
     MESSAGE_COLORS = {
       error: :red,
       warning: :yellow,
@@ -1178,37 +1181,6 @@
       "app/javascript/packs/#{bundle_filename}"
     end
 
-    def resolved_package_json_path
-      node_modules_location = ReactOnRails.configuration.node_modules_location.to_s
-      return "package.json" if node_modules_location.empty? || node_modules_location == Rails.root.to_s
-
-      File.join(node_modules_location, "package.json")
-    rescue StandardError
-      "package.json"
-    end
-
-    def resolved_webpack_config_path
-      candidates = ["config/webpack/webpack.config.js"]
-      candidates.concat(Dir.glob("config/**/webpack.config.{js,ts,cjs,mjs}"))
-      shakapacker_dir = shakapacker_webpack_config_directory
-      if shakapacker_dir
-        candidates.concat(%w[js ts cjs mjs].map { |ext| File.join(shakapacker_dir, "webpack.config.#{ext}") })
-      end
-
-      candidates.uniq.find { |path| File.exist?(path) }
-    end
-
-    def shakapacker_webpack_config_directory
-      require "shakapacker"
-      path = Shakapacker.config.assets_bundler_config_path.to_s
-      return nil if path.empty?
-
-      rails_root = Rails.root.to_s
-      path.start_with?("#{rails_root}/") ? path.sub("#{rails_root}/", "") : path
-    rescue LoadError, StandardError
-      nil
-    end
-
     def server_bundle_filename
       # Try to read from React on Rails initializer
       initializer_path = "config/initializers/react_on_rails.rb"

diff --git a/react_on_rails/lib/react_on_rails/system_checker.rb b/react_on_rails/lib/react_on_rails/system_checker.rb
--- a/react_on_rails/lib/react_on_rails/system_checker.rb
+++ b/react_on_rails/lib/react_on_rails/system_checker.rb
@@ -3,12 +3,15 @@
 require "erb"
 require "open3"
 require "yaml"
+require_relative "config_path_resolver"
 
 module ReactOnRails
   # SystemChecker provides validation methods for React on Rails setup
   # Used by install generator and doctor rake task
   # rubocop:disable Metrics/ClassLength
   class SystemChecker
+    include ConfigPathResolver
+
     attr_reader :messages
 
     def initialize
@@ -709,41 +712,6 @@
       end
     end
 
-    def resolved_package_json_path
-      node_modules_location = ReactOnRails.configuration.node_modules_location.to_s
-      return "package.json" if node_modules_location.empty? || node_modules_location == Rails.root.to_s
-
-      File.join(node_modules_location, "package.json")
-    rescue StandardError
-      "package.json"
-    end
-
-    def resolved_webpack_config_path
-      webpack_config_candidates.find { |path| File.exist?(path) }
-    end
-
-    def webpack_config_candidates
-      candidates = ["config/webpack/webpack.config.js"]
-
-      shakapacker_config_dir = shakapacker_webpack_config_directory
-      if shakapacker_config_dir
-        candidates.concat(%w[js ts cjs mjs].map { |ext| File.join(shakapacker_config_dir, "webpack.config.#{ext}") })
-      end
-
-      candidates.concat(Dir.glob("config/**/webpack.config.{js,ts,cjs,mjs}"))
-      candidates.uniq
-    end
-
-    def shakapacker_webpack_config_directory
-      require "shakapacker"
-      path = Shakapacker.config.assets_bundler_config_path.to_s
-      return nil if path.empty?
-
-      rails_root = Rails.root.to_s
-      path.start_with?("#{rails_root}/") ? path.sub("#{rails_root}/", "") : path
-    rescue LoadError, StandardError
-      nil
-    end
   end
   # rubocop:enable Metrics/ClassLength
 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
@@ -191,6 +191,23 @@
     end
   end
 
+  describe "webpack config path resolution" do
+    it "prefers shakapacker-configured paths over glob-discovered paths" do
+      allow(doctor).to receive(:shakapacker_webpack_config_directory).and_return("config/custom")
+      allow(Dir).to receive(:glob).with("config/**/webpack.config.{js,ts,cjs,mjs}")
+                                  .and_return(["config/old/webpack.config.js"])
+
+      allow(File).to receive(:exist?).with("config/webpack/webpack.config.js").and_return(false)
+      allow(File).to receive(:exist?).with("config/custom/webpack.config.js").and_return(false)
+      allow(File).to receive(:exist?).with("config/custom/webpack.config.ts").and_return(true)
+      allow(File).to receive(:exist?).with("config/custom/webpack.config.cjs").and_return(false)
+      allow(File).to receive(:exist?).with("config/custom/webpack.config.mjs").and_return(false)
+      allow(File).to receive(:exist?).with("config/old/webpack.config.js").and_return(true)
+
+      expect(doctor.send(:resolved_webpack_config_path)).to eq("config/custom/webpack.config.ts")
+    end
+  end
+
   describe "#check_async_usage" do
     let(:checker) { instance_double(ReactOnRails::SystemChecker) }
 

diff --git a/react_on_rails/spec/lib/react_on_rails/system_checker_spec.rb b/react_on_rails/spec/lib/react_on_rails/system_checker_spec.rb
--- a/react_on_rails/spec/lib/react_on_rails/system_checker_spec.rb
+++ b/react_on_rails/spec/lib/react_on_rails/system_checker_spec.rb
@@ -706,6 +706,23 @@
         expect(checker.send(:shakapacker_configured?)).to be true
       end
     end
+
+    describe "#resolved_webpack_config_path" do
+      it "prefers shakapacker-configured paths over glob-discovered paths" do
+        allow(checker).to receive(:shakapacker_webpack_config_directory).and_return("config/custom")
+        allow(Dir).to receive(:glob).with("config/**/webpack.config.{js,ts,cjs,mjs}")
+                                    .and_return(["config/old/webpack.config.js"])
+
+        allow(File).to receive(:exist?).with("config/webpack/webpack.config.js").and_return(false)
+        allow(File).to receive(:exist?).with("config/custom/webpack.config.js").and_return(false)
+        allow(File).to receive(:exist?).with("config/custom/webpack.config.ts").and_return(true)
+        allow(File).to receive(:exist?).with("config/custom/webpack.config.cjs").and_return(false)
+        allow(File).to receive(:exist?).with("config/custom/webpack.config.mjs").and_return(false)
+        allow(File).to receive(:exist?).with("config/old/webpack.config.js").and_return(true)
+
+        expect(checker.send(:resolved_webpack_config_path)).to eq("config/custom/webpack.config.ts")
+      end
+    end
   end
 
   describe "version reporting" do

Comment thread react_on_rails/lib/react_on_rails/doctor.rb Outdated
Comment thread react_on_rails/lib/react_on_rails/doctor.rb Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
react_on_rails/lib/react_on_rails/system_checker.rb (2)

430-435: ⚠️ Potential issue | 🟡 Minor

Keep the failed-configuration copy aligned with custom webpack paths.

This predicate now accepts any resolved webpack config, but check_shakapacker_configuration still enumerates config/webpack/webpack.config.js as a required file. Apps with a valid custom config will still get sent to the default path whenever one of the other prerequisites is missing.

🤖 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/system_checker.rb` around lines 430 - 435,
The failing-configuration message in check_shakapacker_configuration is still
hard-coded to list "config/webpack/webpack.config.js" even though
shakapacker_configured? accepts any resolved webpack config via
resolved_webpack_config_path; update check_shakapacker_configuration to
reference resolved_webpack_config_path (or include it in the enumerated required
files) so the error/copy shows the actual resolved webpack config path when
present, and fall back to the default path only when
resolved_webpack_config_path is nil. Ensure you update any messages that list
required files to use resolved_webpack_config_path and keep checks using
shakapacker_configured? consistent.

311-355: ⚠️ Potential issue | 🟡 Minor

Handle both CommonJS and TypeScript/ESM Shakapacker configs in debug hints.

The suggest_webpack_inspection method instructs users to add debugger; before module.exports, but since resolved_webpack_config_path can now resolve .ts / .mjs files, Shakapacker v9 stock configs using import/export default will receive incorrect guidance. Stock Shakapacker v9 configs use generateWebpackConfig with module.exports = generateWebpackConfig(...) in CommonJS or export default config in TypeScript; both are legitimate. Update standard_shakapacker_config? and the debug hints to recognize both patterns and suggest the appropriate syntax for each format.

🤖 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/system_checker.rb` around lines 311 - 355,
The debug hints in suggest_webpack_inspection assume a CommonJS file (tell users
to add "debugger;" before module.exports) but resolved_webpack_config_path may
point to .ts/.mjs ESM files; update suggest_webpack_inspection to detect file
format (use resolved_webpack_config_path extension and/or content) and emit
format-appropriate guidance (e.g., "add 'debugger;' before module.exports" for
CommonJS and "add 'debugger;' before export default / before
generateWebpackConfig(...)" or the TS/ESM equivalent for .ts/.mjs), and update
standard_shakapacker_config? to recognize both CommonJS patterns (module.exports
= generateWebpackConfig(...)) and ESM/TS patterns (export default ... or export
default generateWebpackConfig(...)) so the hint chooses the correct instruction;
use the existing method names suggest_webpack_inspection,
resolved_webpack_config_path, and standard_shakapacker_config? to locate where
to change behavior.
🤖 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 1190-1198: The resolver in resolved_webpack_config_path currently
checks generic candidates before Shakapacker, which can return a stale config;
update the method to prefer the Shakapacker-resolved paths (and any explicit
assets_bundler_config_path) before falling back to Dir.glob results so Doctor
and ReactOnRails::SystemChecker agree: compute shakapacker candidates from
shakapacker_webpack_config_directory and prepend them (and prepend
assets_bundler_config_path if set) to the candidates array before calling
candidates.uniq.find { |path| File.exist?(path) }.

---

Outside diff comments:
In `@react_on_rails/lib/react_on_rails/system_checker.rb`:
- Around line 430-435: The failing-configuration message in
check_shakapacker_configuration is still hard-coded to list
"config/webpack/webpack.config.js" even though shakapacker_configured? accepts
any resolved webpack config via resolved_webpack_config_path; update
check_shakapacker_configuration to reference resolved_webpack_config_path (or
include it in the enumerated required files) so the error/copy shows the actual
resolved webpack config path when present, and fall back to the default path
only when resolved_webpack_config_path is nil. Ensure you update any messages
that list required files to use resolved_webpack_config_path and keep checks
using shakapacker_configured? consistent.
- Around line 311-355: The debug hints in suggest_webpack_inspection assume a
CommonJS file (tell users to add "debugger;" before module.exports) but
resolved_webpack_config_path may point to .ts/.mjs ESM files; update
suggest_webpack_inspection to detect file format (use
resolved_webpack_config_path extension and/or content) and emit
format-appropriate guidance (e.g., "add 'debugger;' before module.exports" for
CommonJS and "add 'debugger;' before export default / before
generateWebpackConfig(...)" or the TS/ESM equivalent for .ts/.mjs), and update
standard_shakapacker_config? to recognize both CommonJS patterns (module.exports
= generateWebpackConfig(...)) and ESM/TS patterns (export default ... or export
default generateWebpackConfig(...)) so the hint chooses the correct instruction;
use the existing method names suggest_webpack_inspection,
resolved_webpack_config_path, and standard_shakapacker_config? to locate where
to change behavior.
🪄 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: 4d2df55f-c157-4de3-b28b-78f7e703a7ac

📥 Commits

Reviewing files that changed from the base of the PR and between 68a6f04 and eea9116.

📒 Files selected for processing (4)
  • 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

Comment thread react_on_rails/lib/react_on_rails/doctor.rb Outdated
Comment thread react_on_rails/lib/react_on_rails/system_checker.rb Outdated
Comment thread react_on_rails/lib/react_on_rails/doctor.rb Outdated
Comment thread react_on_rails/lib/react_on_rails/system_checker.rb Outdated
Comment thread react_on_rails/lib/react_on_rails/system_checker.rb Outdated
Comment thread react_on_rails/spec/lib/react_on_rails/system_checker_spec.rb Outdated
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 15, 2026

Review

The approach here is solid. Resolving package.json from node_modules_location and broadening webpack config discovery are the right fixes for the false-positive problem. One blocking bug and a few smaller issues to address before merging.

Bug: shakapacker_webpack_config_directory returns a file path, not a directory

Shakapacker.config.assets_bundler_config_path returns the path to the webpack config FILE (e.g. /app/config/webpack/webpack.config.js). After stripping the Rails root the method still returns config/webpack/webpack.config.js — a file path. It is then used as a directory in File.join(shakapacker_config_dir, webpack.config.EXT), yielding bogus paths like config/webpack/webpack.config.js/webpack.config.ts that will never exist.

The Shakapacker-specific candidates are therefore silently dead; only the Dir.glob fallback actually finds configs. This masks the bug but means the Shakapacker path logic provides zero benefit. Fix: add File.dirname before stripping the Rails root (see inline comment for a code suggestion). The same fix is needed in both system_checker.rb and doctor.rb.

Code duplication

resolved_package_json_path, resolved_webpack_config_path, webpack_config_candidates, and shakapacker_webpack_config_directory are now copy-pasted between SystemChecker and Doctor. This is a maintenance hazard. Extracting these into a shared module (e.g. ReactOnRails::ConfigPathResolver) would be cleaner, but at minimum they must stay in sync.

Minor issues

  • rescue StandardError in resolved_package_json_path is broader than needed and can silently swallow programming errors. Narrow to Errno::ENOENT, TypeError (or remove — File.join on a valid string cannot raise).
  • The test for the custom webpack config path mocks Dir.glob, so it never exercises the Shakapacker-based candidate logic. A test that stubs Shakapacker.config.assets_bundler_config_path would catch the File.dirname regression.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
react_on_rails/lib/react_on_rails/doctor.rb (1)

1190-1199: ⚠️ Potential issue | 🟠 Major

Prefer Shakapacker-resolved webpack candidates before default fallback.

On Line 1191, default config/webpack/webpack.config.js is checked before Shakapacker-derived paths. If both exist, doctor can report the wrong config and reintroduce false positives for custom layouts.

🔧 Suggested fix
 def resolved_webpack_config_path
-  candidates = ["config/webpack/webpack.config.js"]
   shakapacker_dir = shakapacker_webpack_config_directory
+  candidates = []
   if shakapacker_dir
     candidates.concat(%w[js ts cjs mjs].map { |ext| File.join(shakapacker_dir, "webpack.config.#{ext}") })
   end
+  candidates << "config/webpack/webpack.config.js"
   candidates.concat(Dir.glob("config/**/webpack.config.{js,ts,cjs,mjs}"))

   candidates.uniq.find { |path| File.exist?(path) }
 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 1190 - 1199, The
method resolved_webpack_config_path currently checks the default
"config/webpack/webpack.config.js" before Shakapacker-derived paths, which can
cause the wrong config to be selected; update resolved_webpack_config_path to
prefer Shakapacker candidates first by building the candidate list with
shakapacker_webpack_config_directory-derived entries placed before the default
path (and still include Dir.glob results), then call uniq.find { |path|
File.exist?(path) } so Shakapacker-resolved webpack.config.* files are selected
prior to the fallback default.
🤖 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/system_checker.rb`:
- Around line 723-733: The method webpack_config_candidates currently places the
hardcoded "config/webpack/webpack.config.js" before Shakapacker-derived paths,
causing the default to be chosen when both exist; update
webpack_config_candidates so that if shakapacker_webpack_config_directory
returns a value you first add the Shakapacker candidates
(File.join(shakapacker_config_dir, "webpack.config.<ext>") for js,ts,cjs,mjs)
and only then append the hardcoded default "config/webpack/webpack.config.js"
and the Dir.glob("config/**/webpack.config.{js,ts,cjs,mjs}") results, finally
returning candidates.uniq — this ensures shakapacker_webpack_config_directory
takes precedence over the default fallback.

---

Duplicate comments:
In `@react_on_rails/lib/react_on_rails/doctor.rb`:
- Around line 1190-1199: The method resolved_webpack_config_path currently
checks the default "config/webpack/webpack.config.js" before Shakapacker-derived
paths, which can cause the wrong config to be selected; update
resolved_webpack_config_path to prefer Shakapacker candidates first by building
the candidate list with shakapacker_webpack_config_directory-derived entries
placed before the default path (and still include Dir.glob results), then call
uniq.find { |path| File.exist?(path) } so Shakapacker-resolved webpack.config.*
files are selected prior to the fallback default.
🪄 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: d7663e15-c0df-4e28-b746-7dbbdee250b9

📥 Commits

Reviewing files that changed from the base of the PR and between eea9116 and d9e54a1.

📒 Files selected for processing (2)
  • react_on_rails/lib/react_on_rails/doctor.rb
  • react_on_rails/lib/react_on_rails/system_checker.rb

Comment thread react_on_rails/lib/react_on_rails/system_checker.rb Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

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/lib/react_on_rails/doctor.rb (1)

1181-1210: Consider consolidating resolver helpers shared with SystemChecker.

These resolver methods now exist in both classes with near-identical logic. Centralizing them in one shared helper/module would reduce drift risk and keep Doctor/SystemChecker behavior aligned over time.

🤖 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 1181 - 1210, The
resolver methods resolved_package_json_path, resolved_webpack_config_path and
shakapacker_webpack_config_directory are duplicated between Doctor and
SystemChecker; extract them into a single shared module (e.g.,
ReactOnRails::ResolverHelpers or PathResolvers) and remove the duplicates in
both classes, then include or extend that module from Doctor and SystemChecker
so both use the same implementations; ensure the module contains the same logic
(including the LoadError/StandardError rescue) and update any internal
references to call the module methods instead of the local copies.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@react_on_rails/lib/react_on_rails/doctor.rb`:
- Around line 1181-1210: The resolver methods resolved_package_json_path,
resolved_webpack_config_path and shakapacker_webpack_config_directory are
duplicated between Doctor and SystemChecker; extract them into a single shared
module (e.g., ReactOnRails::ResolverHelpers or PathResolvers) and remove the
duplicates in both classes, then include or extend that module from Doctor and
SystemChecker so both use the same implementations; ensure the module contains
the same logic (including the LoadError/StandardError rescue) and update any
internal references to call the module methods instead of the local copies.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fe95b184-c8ef-4e37-ab36-192e91bca1a1

📥 Commits

Reviewing files that changed from the base of the PR and between d9e54a1 and 3d4348d.

📒 Files selected for processing (6)
  • docs/README.md
  • docs/oss/migrating/rsc-troubleshooting.md
  • 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
✅ Files skipped from review due to trivial changes (1)
  • docs/oss/migrating/rsc-troubleshooting.md

Comment thread docs/oss/migrating/rsc-troubleshooting.md Outdated
Comment thread docs/oss/migrating/rsc-troubleshooting.md Outdated
Comment thread react_on_rails/lib/react_on_rails/system_checker.rb Outdated
Comment thread react_on_rails/lib/react_on_rails/doctor.rb Outdated
Comment thread react_on_rails/lib/react_on_rails/system_checker.rb Outdated
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 15, 2026

Review Summary

The core logic — resolving package.json from node_modules_location and broadening webpack config discovery — is sound and the regression tests cover the new paths well. A few things worth addressing before merge:

Must Fix

'use client' directive syntax in docs (two inline comments)
The ('use client') parenthesised form is not a valid directive prologue in the ECMAScript spec. Some parsers/bundlers check for a bare StringLiteral AST node as the first statement; wrapping it in parens produces a (StringLiteral) grouped expression which may silently fail. The change appears to silence a no-unused-expressions linter in CI — the right fix is a linter suppression comment in the example or an eslint directive option, not rewriting the documented syntax.

Should Fix

Code duplication across doctor.rb and system_checker.rb
resolved_package_json_path, resolved_webpack_config_path / webpack_config_candidates, and shakapacker_webpack_config_directory are now defined identically in both files. Future bug fixes or extensions will need to be applied twice. Extracting these into a shared module (e.g. ReactOnRails::WebpackConfigResolver) would eliminate the duplication and allow the test cases to be written once as well.

Inconsistency between the two resolved_webpack_config_path implementations
system_checker.rb cleanly delegates to webpack_config_candidates; doctor.rb builds the list inline. They should be kept consistent — another argument for the shared module above.

Nits

  • Dir.glob in doctor.rb:1195 already returns only existing paths, so the File.exist? check in the find block is redundant for those entries (harmless, but noisy).
  • The suggest_webpack_inspection default parameter ("config/webpack/webpack.config.js") is unreachable — the method is only called when a path has already been found.

Positive notes

  • Downgrading "missing webpack config" from error → warning is the right UX call given the variety of custom layouts.
  • The Shakapacker config path extraction (shakapacker_webpack_config_directory) is well-guarded with rescue LoadError, StandardError.
  • Test coverage for the new paths is thorough.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

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/lib/react_on_rails/system_checker.rb (1)

301-308: Consider listing searched locations in the warning message.

The warning mentions only config/webpack/webpack.config.js, but webpack_config_candidates now searches Shakapacker-derived paths and glob patterns. Listing the searched locations would help users understand whether their custom path was checked.

💡 Optional: More informative warning message
       else
         add_warning(<<~MSG.strip)
           ⚠️  Webpack configuration not found at common paths.

-          Expected default: config/webpack/webpack.config.js
+          Searched locations:
+          #{webpack_config_candidates.take(5).map { |p| "• #{p}" }.join("\n")}
           If your app uses a custom webpack config location, this may be informational.
           Run: rails generate react_on_rails:install
         MSG
       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/system_checker.rb` around lines 301 - 308,
The warning added in the add_warning call only names the default path but not
the actual locations checked; update the message emitted by the method that
calls add_warning (the code around add_warning in system_checker.rb) to include
the list returned by webpack_config_candidates (or otherwise reference
webpack_config_candidates and any Shakapacker-derived/glob candidate arrays) so
users see exactly which paths/globs were searched; construct the warning string
to append a joined, human-readable list of webpack_config_candidates results
before calling add_warning.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@react_on_rails/lib/react_on_rails/system_checker.rb`:
- Around line 301-308: The warning added in the add_warning call only names the
default path but not the actual locations checked; update the message emitted by
the method that calls add_warning (the code around add_warning in
system_checker.rb) to include the list returned by webpack_config_candidates (or
otherwise reference webpack_config_candidates and any Shakapacker-derived/glob
candidate arrays) so users see exactly which paths/globs were searched;
construct the warning string to append a joined, human-readable list of
webpack_config_candidates results before calling add_warning.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6c9d85c7-8728-4f73-b258-28596496a915

📥 Commits

Reviewing files that changed from the base of the PR and between 3d4348d and 8f7ba86.

📒 Files selected for processing (2)
  • docs/oss/migrating/rsc-troubleshooting.md
  • react_on_rails/lib/react_on_rails/system_checker.rb
✅ Files skipped from review due to trivial changes (1)
  • docs/oss/migrating/rsc-troubleshooting.md

Comment thread react_on_rails/lib/react_on_rails/system_checker.rb Outdated
Comment thread react_on_rails/lib/react_on_rails/system_checker.rb Outdated
Comment thread react_on_rails/lib/react_on_rails/system_checker.rb Outdated
Comment thread react_on_rails/lib/react_on_rails/system_checker.rb Outdated
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 15, 2026

Review Summary

The direction here is correct - resolving package.json via node_modules_location and broadening webpack config discovery are clear improvements over hard-coding config/webpack/webpack.config.js. A few issues worth addressing before merge:

Code duplication across two files (structural)

resolved_package_json_path, resolved_webpack_config_path / webpack_config_candidates, and shakapacker_webpack_config_directory are copy-pasted verbatim into both system_checker.rb and doctor.rb. Any bug fix or enhancement has to be applied twice. These should live in a shared module (e.g. ReactOnRails::WebpackPathHelpers) included in both classes, or a small utility object both can delegate to. (See inline comments on system_checker.rb lines 710-744.)

resolved_package_json_path: relative path assumption

When node_modules_location is a relative string like client, the method returns client/package.json - a path relative to the process CWD. This works as long as the CWD is Rails.root, which is conventional but not guaranteed. The node_modules_location == Rails.root.to_s guard also misses variants like a trailing slash or a symlink-resolved path, which would slip through and produce a doubly-joined path. Normalising via Pathname to an absolute path makes the contract explicit. (Inline suggestion on line 712.)

Dir.glob in candidate list produces non-deterministic ordering

Dir.glob already returns only existing paths, so the subsequent File.exist? call is redundant for those entries. More subtly, glob result ordering is filesystem-dependent; .find will return whichever file the OS lists first if multiple matches exist. For a diagnostic tool this is low risk, but a comment explaining the intended fallback semantics would help.

Minor

  • Downgrading check_webpack_configuration from add_error to add_warning is the right call for non-standard layouts.
  • parse_package_json now rescues Errno::ENOENT in addition to JSON::ParserError - correct fix.
  • The new specs cover the happy paths well; a negative test (e.g. confirming the method returns nil / falls back gracefully when neither the shakapacker-derived path nor the default exist) would round out the coverage.

candidates.concat(%w[js ts cjs mjs].map { |ext| File.join(shakapacker_config_dir, "webpack.config.#{ext}") })
end

candidates << "config/webpack/webpack.config.js"
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.

Default fallback only covers .js

The Shakapacker-derived candidates cover js, ts, cjs, and mjs, but the hardcoded fallback only covers .js. Projects that aren't using Shakapacker but store their webpack config at the default location with a non-.js extension (e.g. config/webpack/webpack.config.ts) won't be found.

Suggested change
candidates << "config/webpack/webpack.config.js"
candidates.concat(%w[js ts cjs mjs].map { |ext| "config/webpack/webpack.config.#{ext}" })

if webpack_config_path
checker.add_success("✅ Webpack configuration: #{webpack_config_path}")
else
checker.add_warning("⚠️ Missing Webpack configuration: config/webpack/webpack.config.js")
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.

Misleading warning: hardcodes the default path even when others were tried

When resolved_webpack_config_path returns nil, several candidates (including Shakapacker-derived paths) were already searched. Showing only config/webpack/webpack.config.js in the warning makes it look like only one location was checked and doesn't help the developer understand what was actually searched.

Consider listing all checked candidates or keeping the message generic:

Suggested change
checker.add_warning("⚠️ Missing Webpack configuration: config/webpack/webpack.config.js")
checker.add_warning("⚠️ No Webpack configuration file found in common locations")

msg[:type] == :success && msg[:content].include?("react-on-rails-pro NPM package")
end).to be true
end
end
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.

Existing tests may silently break after the refactor

The existing tests for check_react_on_rails_npm_package, check_package_version_sync, etc. (outside this PR's added blocks) still stub File.exist? with the relative "package.json". Those stubs only fire correctly if resolved_package_json_path still returns "package.json", which depends on ReactOnRails.configuration.node_modules_location.to_s == Rails.root.to_s at test time.

Since Rails.root is not stubbed in those tests, this is environment-dependent and fragile. Those tests should explicitly stub node_modules_location (or Rails.root + node_modules_location) to remain robust as the new resolver path is now in the call chain.

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 17, 2026

Review: Fix #2606 - reduce doctor false positives for custom layouts

Overall the direction is right -- resolving paths from node_modules_location instead of hardcoding repo-root assumptions is the correct fix. A few issues worth addressing before merge:

Bug: mixed relative/absolute paths from resolved_package_json_path

The default branch returns the bare string "package.json" (relative), while the workspace branch returns an absolute path via Rails.root.join(...).to_s. The relative form works only when Dir.pwd == Rails.root, which is not guaranteed in Rake tasks or certain CI environments. VersionChecker::NodePackageVersion.package_json_path already solves this and always returns an absolute path -- worth either delegating to it or applying the same pattern. See inline comment on line 9 of config_path_resolver.rb.

Code duplication with version_checker.rb

VersionChecker::NodePackageVersion.package_json_path already resolves package.json using node_modules_location. The new resolved_package_json_path reimplements the same logic. Centralising would mean one place to maintain if configuration semantics change.

shakapacker_webpack_config_directory path-stripping edge case

start_with?("#{rails_root}/") requires a trailing slash, so it does not match when the webpack config sits directly in Rails.root (i.e. directory == rails_root). The method then returns an absolute path, mixing absolute and relative strings in the candidates list. Using Pathname#relative_path_from handles this cleanly -- see the inline suggestion on line 37.

Default webpack-config fallback only covers .js

Shakapacker-derived candidates cover js/ts/cjs/mjs, but the hardcoded fallback config/webpack/webpack.config.js only covers .js. Non-Shakapacker projects using a .ts or .mjs config at the default location will not be found. The inline suggestion on line 26 applies the same extension set to the default path.

Warning message is misleading

When no webpack config is found, the warning still names only config/webpack/webpack.config.js, even though ts/cjs/mjs variants and Shakapacker paths were also searched. This can confuse developers into thinking only one location was checked. See inline comment on doctor.rb line 630.

Pre-existing tests may silently break

Tests outside the new workspace blocks stub File.exist? with "package.json" (relative). Now that all package.json reads go through resolved_package_json_path, those stubs only fire if node_modules_location.to_s == Rails.root.to_s in the test environment -- implicit and fragile. Explicitly stubbing Rails.root and node_modules_location in those tests (as the new workspace tests already do) would make them robust.

@justin808
Copy link
Copy Markdown
Member Author

@cursor review
@codex review

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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: Config resolver ignores rspack configs causing false positives
    • Updated config candidate resolution to include rspack filenames and fallback paths (with specs), so rspack configs are correctly detected instead of reported missing.

Create PR

Or push these changes by commenting:

@cursor push f9dc52a097
Preview (f9dc52a097)
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
@@ -20,10 +20,18 @@
 
       shakapacker_config_dir = shakapacker_webpack_config_directory
       if shakapacker_config_dir
-        candidates.concat(%w[js ts cjs mjs].map { |ext| File.join(shakapacker_config_dir, "webpack.config.#{ext}") })
+        candidates.concat(%w[js ts cjs mjs].flat_map do |ext|
+          [
+            File.join(shakapacker_config_dir, "webpack.config.#{ext}"),
+            File.join(shakapacker_config_dir, "rspack.config.#{ext}")
+          ]
+        end)
       end
 
       candidates << "config/webpack/webpack.config.js"
+      candidates << "config/webpack/webpack.config.ts"
+      candidates << "config/rspack/rspack.config.js"
+      candidates << "config/rspack/rspack.config.ts"
       candidates.uniq
     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
@@ -1302,6 +1302,22 @@
 
         expect(doctor.send(:resolved_webpack_config_path)).to eq("config/custom/webpack.config.ts")
       end
+
+      it "resolves rspack config candidates from the shakapacker-derived directory" do
+        allow(File).to receive(:exist?).and_return(false)
+        allow(File).to receive(:exist?).with("config/rspack/rspack.config.ts").and_return(true)
+        allow(doctor).to receive(:shakapacker_webpack_config_directory).and_return("config/rspack")
+
+        expect(doctor.send(:resolved_webpack_config_path)).to eq("config/rspack/rspack.config.ts")
+      end
+
+      it "falls back to default rspack config paths when shakapacker directory is unavailable" do
+        allow(File).to receive(:exist?).and_return(false)
+        allow(File).to receive(:exist?).with("config/rspack/rspack.config.js").and_return(true)
+        allow(doctor).to receive(:shakapacker_webpack_config_directory).and_return(nil)
+
+        expect(doctor.send(:resolved_webpack_config_path)).to eq("config/rspack/rspack.config.js")
+      end
     end
 
     describe "#shakapacker_webpack_config_directory" do

Comment thread react_on_rails/lib/react_on_rails/config_path_resolver.rb
@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

ℹ️ 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".

@justin808
Copy link
Copy Markdown
Member Author

@cursor push f9dc52a

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Webpack config discovery inconsistency causes contradictory doctor output
    • Updated SystemChecker to use the same resolved_webpack_config_path lookup as Doctor key-file checks so custom webpack config locations no longer trigger a contradictory hard error.

Create PR

Or push these changes by commenting:

@cursor push 4a92e0e876
Preview (4a92e0e876)
diff --git a/react_on_rails/lib/react_on_rails/system_checker.rb b/react_on_rails/lib/react_on_rails/system_checker.rb
--- a/react_on_rails/lib/react_on_rails/system_checker.rb
+++ b/react_on_rails/lib/react_on_rails/system_checker.rb
@@ -295,7 +295,7 @@
 
     # Webpack configuration validation
     def check_webpack_configuration
-      config_path = detect_bundler_config_path
+      config_path = resolved_webpack_config_path
       if config_path
         add_success("✅ Bundler configuration exists (#{config_path})")
         check_webpack_config_content(config_path)

diff --git a/react_on_rails/spec/lib/react_on_rails/system_checker_spec.rb b/react_on_rails/spec/lib/react_on_rails/system_checker_spec.rb
--- a/react_on_rails/spec/lib/react_on_rails/system_checker_spec.rb
+++ b/react_on_rails/spec/lib/react_on_rails/system_checker_spec.rb
@@ -559,6 +559,38 @@
     end
   end
 
+  describe "#check_webpack_configuration" do
+    context "when webpack config is resolved from a custom shakapacker location" do
+      before do
+        allow(checker).to receive(:resolved_webpack_config_path).and_return("config/custom/webpack.config.ts")
+        allow(checker).to receive(:check_webpack_config_content)
+        allow(checker).to receive(:suggest_webpack_inspection)
+      end
+
+      it "reports success instead of an error" do
+        checker.check_webpack_configuration
+
+        expect(checker.messages.any? do |msg|
+          msg[:type] == :success && msg[:content].include?("config/custom/webpack.config.ts")
+        end).to be true
+        expect(checker.errors?).to be false
+      end
+    end
+
+    context "when no webpack config can be resolved" do
+      before do
+        allow(checker).to receive(:resolved_webpack_config_path).and_return(nil)
+      end
+
+      it "reports an error" do
+        checker.check_webpack_configuration
+
+        expect(checker.errors?).to be true
+        expect(checker.messages.last[:content]).to include("Bundler configuration not found")
+      end
+    end
+  end
+
   describe "#check_react_dependencies" do
     let(:base_package_json) do
       {

# React dependencies validation
def check_react_dependencies
return unless File.exist?("package.json")
return unless File.exist?(resolved_package_json_path)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Webpack config discovery inconsistency causes contradictory doctor output

High Severity

Doctor#check_key_configuration_files now uses the new broad resolved_webpack_config_path (which checks Shakapacker-configured and custom locations), but SystemChecker#check_webpack_configuration — called by the Doctor in a separate section — still uses the narrow detect_bundler_config_path which only checks hardcoded paths. For apps with custom webpack config locations, the Doctor will show ✅ in "Key Configuration Files" and 🚫 ERROR in "Webpack Configuration", and exit(1). The PR description states this method was to be downgraded from hard error to contextual warning, but that change was not applied.

Additional Locations (1)
Fix in Cursor Fix in Web

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8da3444502

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +23 to +27
candidates.concat(%w[js ts cjs mjs].flat_map do |ext|
[
File.join(shakapacker_config_dir, "webpack.config.#{ext}"),
File.join(shakapacker_config_dir, "rspack.config.#{ext}")
]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Check configured bundler config path before guessing names

resolved_webpack_config_path now derives candidates from the configured directory but never checks Shakapacker.config.assets_bundler_config_path itself, so a missing configured file can be masked by any leftover webpack.config.*/rspack.config.* file in the same folder. In that case Doctor#check_key_configuration_files reports a success even though Shakapacker is pointed at a broken path, which hides a real configuration error.

Useful? React with 👍 / 👎.

@justin808 justin808 merged commit 9a9c31a into master Mar 17, 2026
32 of 33 checks passed
@justin808 justin808 deleted the jg-codex/issue-2606-doctor-paths branch March 17, 2026 23:14
justin808 added a commit that referenced this pull request Mar 30, 2026
- resolve `package.json` from
`ReactOnRails.configuration.node_modules_location` (instead of assuming
repo root)
- broaden webpack config discovery to common custom locations/extensions
and Shakapacker-configured paths
- downgrade missing webpack config in
`SystemChecker#check_webpack_configuration` from hard error to
contextual warning
- add regression specs for non-root JS workspace and custom webpack
config discovery

Fixes false positives reported in `react_on_rails:doctor` for apps that
keep JS dependencies in a subdirectory (for example
`client/package.json`) and/or use non-default webpack config locations.

- `bundle exec rspec
react_on_rails/spec/lib/react_on_rails/system_checker_spec.rb
react_on_rails/spec/lib/react_on_rails/doctor_spec.rb`
- `bundle exec rubocop
react_on_rails/lib/react_on_rails/system_checker.rb
react_on_rails/lib/react_on_rails/doctor.rb
react_on_rails/spec/lib/react_on_rails/system_checker_spec.rb
react_on_rails/spec/lib/react_on_rails/doctor_spec.rb`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Low risk because changes are limited to diagnostic path detection and
messaging; no runtime application behavior or data/security logic is
modified.
>
> **Overview**
> Reduces `react_on_rails:doctor` false positives in non-standard
project layouts by introducing `ConfigPathResolver`, which resolves
`package.json` via `ReactOnRails.configuration.node_modules_location`
and discovers webpack/rspack config files across common extensions and
Shakapacker-configured locations.
>
> Updates `Doctor` and `SystemChecker` to use these resolved paths, and
changes the Doctor key-file check to report missing bundler config as a
*contextual warning* (instead of assuming
`config/webpack/webpack.config.js`). Adds specs covering workspace
`package.json` resolution and bundler config discovery behavior.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
8da3444. 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
-->

* **New Features**
* Enhanced configuration detection for webpack and package.json files
across flexible project layouts, including custom workspace locations
* Improved diagnostic messages when configuration files are missing or
misconfigured

* **Bug Fixes**
  * Robust error handling for missing configuration paths

* **Documentation**
  * Code formatting improvements in troubleshooting guide

* **Tests**
  * Added test coverage for dynamic configuration path resolution
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
justin808 added a commit that referenced this pull request Apr 6, 2026
- resolve `package.json` from
`ReactOnRails.configuration.node_modules_location` (instead of assuming
repo root)
- broaden webpack config discovery to common custom locations/extensions
and Shakapacker-configured paths
- downgrade missing webpack config in
`SystemChecker#check_webpack_configuration` from hard error to
contextual warning
- add regression specs for non-root JS workspace and custom webpack
config discovery

Fixes false positives reported in `react_on_rails:doctor` for apps that
keep JS dependencies in a subdirectory (for example
`client/package.json`) and/or use non-default webpack config locations.

- `bundle exec rspec
react_on_rails/spec/lib/react_on_rails/system_checker_spec.rb
react_on_rails/spec/lib/react_on_rails/doctor_spec.rb`
- `bundle exec rubocop
react_on_rails/lib/react_on_rails/system_checker.rb
react_on_rails/lib/react_on_rails/doctor.rb
react_on_rails/spec/lib/react_on_rails/system_checker_spec.rb
react_on_rails/spec/lib/react_on_rails/doctor_spec.rb`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Low risk because changes are limited to diagnostic path detection and
messaging; no runtime application behavior or data/security logic is
modified.
>
> **Overview**
> Reduces `react_on_rails:doctor` false positives in non-standard
project layouts by introducing `ConfigPathResolver`, which resolves
`package.json` via `ReactOnRails.configuration.node_modules_location`
and discovers webpack/rspack config files across common extensions and
Shakapacker-configured locations.
>
> Updates `Doctor` and `SystemChecker` to use these resolved paths, and
changes the Doctor key-file check to report missing bundler config as a
*contextual warning* (instead of assuming
`config/webpack/webpack.config.js`). Adds specs covering workspace
`package.json` resolution and bundler config discovery behavior.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
8da3444. 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
-->

* **New Features**
* Enhanced configuration detection for webpack and package.json files
across flexible project layouts, including custom workspace locations
* Improved diagnostic messages when configuration files are missing or
misconfigured

* **Bug Fixes**
  * Robust error handling for missing configuration paths

* **Documentation**
  * Code formatting improvements in troubleshooting guide

* **Tests**
  * Added test coverage for dynamic configuration path resolution
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants