Skip to content

Conversation

@justin808
Copy link
Member

@justin808 justin808 commented Nov 13, 2025

Summary

Adds proactive doctor checks to detect :async loading strategy usage in projects without React on Rails Pro, which can cause component registration race conditions.

Changes

The doctor now checks for and reports errors when it detects:

  • javascript_pack_tag with :async in view files
  • config.generated_component_packs_loading_strategy = :async in initializer

When detected without Pro, provides clear guidance to either upgrade to Pro or use :defer/:sync loading strategies.

This complements PR #1993's configuration validation by adding runtime detection during development to help developers identify and fix async usage issues.

Testing

All new tests pass (155+ examples in doctor spec). The checks are skipped when React on Rails Pro is installed.


This change is Reviewable

Summary by CodeRabbit

  • New Features

    • Diagnostic that detects async component-loading usage when the Pro edition is not present, reporting affected view and initializer locations, distinguishing commented/false-positive cases, and providing guidance and upgrade options.
  • Tests

    • Expanded tests for async-detection across template formats, comment-aware and multiline scanning, initializer strategies, error handling, and skipping when Pro is installed.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 13, 2025

Walkthrough

Adds development-time diagnostics that scan view templates and the React on Rails initializer for async script-loading usage, and reports errors with file/config locations and guidance when Pro is not installed. Scanning is robust to missing files and encoding issues.

Changes

Cohort / File(s) Summary
Async Usage Detection
lib/react_on_rails/doctor.rb
Adds check_async_usage (invoked from check_development) and helpers: scan_view_files_for_async_pack_tag, scan_pattern_for_async, file_has_async_pack_tag?, content_has_only_commented_async?, config_has_async_loading_strategy?, relativize_path, and log_debug. Scans app/views/**/*.erb and app/views/**/*.haml, parses config/initializers/react_on_rails.rb, wraps file scanning in rescues, and reports actionable errors when :async usage is found without Pro.
Async Detection Tests
spec/lib/react_on_rails/doctor_spec.rb
Adds specs for #check_async_usage (Pro vs non‑Pro), view scanning across ERB/Haml (multi-line tags, comments, defer/async permutations), file-comment edge cases, and config_has_async_loading_strategy? detection behavior.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Dev as check_development
  participant Doctor as Doctor (lib/react_on_rails/doctor.rb)
  participant FS as File system (views + initializer)
  participant Reporter as Reporter (add_error / noop)
  rect rgba(0,128,128,0.06)
  Dev ->> Doctor: check_async_usage
  Doctor ->> Doctor: return if Pro installed
  alt Pro installed
    Doctor ->> Reporter: skip async checks
  else Pro not installed
    Doctor ->> FS: scan_view_files_for_async_pack_tag()
    FS -->> Doctor: list of async view files (may be empty)
    Doctor ->> FS: config_has_async_loading_strategy?()
    FS -->> Doctor: initializer indicates :async? (bool)
    alt any async detected
      Doctor ->> Reporter: add_error(details: file paths, config lines, guidance)
    else none detected
      Doctor ->> Reporter: noop
    end
  end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Areas to focus on:
    • Regex and comment-handling in file_has_async_pack_tag? / content_has_only_commented_async?.
    • File scanning and path relativization in scan_pattern_for_async / scan_view_files_for_async_pack_tag.
    • Robustness of config_has_async_loading_strategy? parsing (commented lines, quoting, whitespace) and logger usage.

Possibly related PRs

Suggested reviewers

  • AbanoubGhadban
  • alexeyr-ci2

Poem

🐇 I hop through views with careful eye,

I sniff the async tags that dart and fly.
When Pro is gone I softly chide,
I list the paths where those tags hide.
A tiny rabbit, fixing tide.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding doctor checks for :async usage without React on Rails Pro.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch justin808/doctor-check-async-requires-pro

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.

@claude
Copy link

claude bot commented Nov 13, 2025

Code Review - PR #2010: Add doctor checks for :async usage without React on Rails Pro

Summary

This PR adds proactive detection of :async loading strategy usage in projects without React on Rails Pro, helping developers identify configuration issues that can cause component registration race conditions. The implementation is solid and well-tested.

Strengths

  1. Excellent Test Coverage - 156 new test examples covering all scenarios including Pro installed vs not installed, both detection methods (view files and initializer), and edge cases (missing files, exceptions)

  2. Consistent Code Quality - Follows existing patterns in the Doctor class with proper error handling, clear informative error messages, and early returns for clarity

  3. User Experience - Clear error messages with emoji indicators, provides concrete solutions (upgrade to Pro OR use :defer/:sync), links to documentation, and shows specific file paths where issues are detected

  4. Integration - Properly integrated into check_development workflow, consistent with other doctor checks, and skips check when Pro is installed (no false positives)

Potential Issues and Suggestions

1. Regex Pattern Potential Edge Cases (Minor)

Location: lib/react_on_rails/doctor.rb:1195

The regex patterns may miss some edge cases like commented code, multi-line calls, or hash syntax variations (async: true). Consider using the /m flag to match newlines and handle more variations.

2. Performance Consideration (Low Priority)

Location: lib/react_on_rails/doctor.rb:1183-1204

Reads entire file contents into memory for each view file. For large projects this could be slow, but since doctor checks run on-demand (not in hot path), current implementation is acceptable. Only optimize if users report performance issues.

3. Configuration File Detection (Minor Edge Case)

Location: lib/react_on_rails/doctor.rb:1206-1215

Only checks config/initializers/react_on_rails.rb. Some projects might use different initializer filenames or configure in config/environments/development.rb. Not critical since standard name is well-documented.

4. Test Coverage Gap (Minor)

Tests cover async in views only, async in config only, and no async usage. Missing test case for async in BOTH views AND config simultaneously.

Security and Performance

Security: No concerns identified - reads files from known safe locations, no user input execution, proper exception handling

Performance: Acceptable - doctor checks run on-demand, file globbing efficient for typical Rails apps, early returns prevent unnecessary work

Final Assessment

Overall Quality: Excellent (5/5)

Recommendation: APPROVE with optional minor enhancements

The code is production-ready as-is. All identified issues are minor edge cases that do not block merging. This PR demonstrates thorough testing (156 examples), clear documentation, consistent code quality, helpful user experience, and proper integration with existing systems.

Great work!

Copy link
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

🧹 Nitpick comments (2)
lib/react_on_rails/doctor.rb (2)

1151-1181: Consider adding top-level error handling.

The method logic is sound and correctly checks for async usage without Pro. However, consider wrapping the entire method body in a rescue clause to ensure that any unexpected errors during scanning don't break the entire doctor run.

Apply this diff to add defensive error handling:

 def check_async_usage
   # When Pro is installed, async is fully supported and is the default behavior
   # No need to check for async usage in this case
   return if ReactOnRails::Utils.react_on_rails_pro?

   async_issues = []

   # Check 1: javascript_pack_tag with :async in view files
   view_files_with_async = scan_view_files_for_async_pack_tag
   unless view_files_with_async.empty?
     async_issues << "javascript_pack_tag with :async found in view files:"
     view_files_with_async.each do |file|
       async_issues << "  • #{file}"
     end
   end

   # Check 2: generated_component_packs_loading_strategy = :async
   if config_has_async_loading_strategy?
     async_issues << "config.generated_component_packs_loading_strategy = :async in initializer"
   end

   return if async_issues.empty?

   # Report errors if async usage is found without Pro
   checker.add_error("🚫 :async usage detected without React on Rails Pro")
   async_issues.each { |issue| checker.add_error("  #{issue}") }
   checker.add_info("  💡 :async can cause race conditions. Options:")
   checker.add_info("    1. Upgrade to React on Rails Pro (recommended for :async support)")
   checker.add_info("    2. Change to :defer or :sync loading strategy")
   checker.add_info("  📖 https://www.shakacode.com/react-on-rails/docs/guides/configuration/")
+rescue StandardError => e
+  checker.add_warning("⚠️  Could not complete async usage check: #{e.message}")
+  Rails.logger.debug("Async usage check error: #{e.full_message}") if defined?(Rails) && Rails.logger
 end

1209-1224: Note potential edge case with multi-line comments.

The comment filtering logic correctly handles single-line comments in ERB, HAML, and HTML. However, it may not correctly handle multi-line scenarios:

  1. Multi-line ERB tags split across lines
  2. Multi-line HTML comments wrapping ERB tags

These edge cases are rare in practice, but if false positives occur, consider using a more sophisticated parsing approach or documenting this limitation.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 044d7de and 8382e94.

📒 Files selected for processing (2)
  • lib/react_on_rails/doctor.rb (2 hunks)
  • spec/lib/react_on_rails/doctor_spec.rb (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • spec/lib/react_on_rails/doctor_spec.rb
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.074Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The `react_on_rails_pro?` method validates licenses and should raise errors early (including during path resolution in methods like `server_bundle?`) to enforce licensing requirements rather than failing later with obscure errors.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.
Learnt from: alexeyr-ci
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/ReactOnRailsRSC.ts:87-87
Timestamp: 2024-12-12T13:07:09.929Z
Learning: When handling errors in 'node_package/src/ReactOnRailsRSC.ts', include the error stack in error messages in development and test environments to aid debugging.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/turbolinksUtils.ts:34-36
Timestamp: 2025-02-13T16:50:26.861Z
Learning: In React on Rails, when checking for Turbolinks version 5 using `turbolinksVersion5()`, always ensure `Turbolinks` exists first by checking `turbolinksInstalled()` to prevent TypeError when accessing properties.
Learnt from: Romex91
Repo: shakacode/react_on_rails PR: 1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
📚 Learning: 2025-10-23T17:22:01.074Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.074Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The `react_on_rails_pro?` method validates licenses and should raise errors early (including during path resolution in methods like `server_bundle?`) to enforce licensing requirements rather than failing later with obscure errors.

Applied to files:

  • lib/react_on_rails/doctor.rb
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.

Applied to files:

  • lib/react_on_rails/doctor.rb
📚 Learning: 2025-09-15T21:24:48.207Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.

Applied to files:

  • lib/react_on_rails/doctor.rb
📚 Learning: 2024-12-12T13:07:09.929Z
Learnt from: alexeyr-ci
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/ReactOnRailsRSC.ts:87-87
Timestamp: 2024-12-12T13:07:09.929Z
Learning: When handling errors in 'node_package/src/ReactOnRailsRSC.ts', include the error stack in error messages in development and test environments to aid debugging.

Applied to files:

  • lib/react_on_rails/doctor.rb
📚 Learning: 2025-02-13T16:50:26.861Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/turbolinksUtils.ts:34-36
Timestamp: 2025-02-13T16:50:26.861Z
Learning: In React on Rails, when checking for Turbolinks version 5 using `turbolinksVersion5()`, always ensure `Turbolinks` exists first by checking `turbolinksInstalled()` to prevent TypeError when accessing properties.

Applied to files:

  • lib/react_on_rails/doctor.rb
🧬 Code graph analysis (1)
lib/react_on_rails/doctor.rb (2)
lib/react_on_rails/utils.rb (1)
  • react_on_rails_pro? (256-264)
lib/react_on_rails/system_checker.rb (2)
  • add_error (16-18)
  • add_info (28-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build
  • GitHub Check: claude-review
🔇 Additional comments (3)
lib/react_on_rails/doctor.rb (3)

176-176: LGTM!

The integration of check_async_usage into the development checks flow is well-placed and follows the existing pattern.


1183-1191: LGTM!

Good error handling for file scanning operations. The method appropriately catches file and encoding errors, logs them for debugging, and returns an empty array to prevent the doctor run from failing.


1226-1240: LGTM!

The config checking logic is sound:

  • Correctly filters out commented lines while preserving lines with inline comments (which should still be flagged if they set async)
  • Proper regex pattern with flexible whitespace handling
  • Appropriate error handling with debug logging

Copy link
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: 0

🧹 Nitpick comments (1)
lib/react_on_rails/doctor.rb (1)

1221-1247: Consider clarifying multi-line tag comment detection behavior.

The method filters out commented async usage by checking if lines containing javascript_pack_tag are commented. For multi-line tags, this checks the opening line containing javascript_pack_tag. While the comment on line 1230 explains this, the behavior means that a tag like:

<%= javascript_pack_tag "app",
  :async %>

is considered uncommented (correctly), but

<%# javascript_pack_tag "app",
  :async %>

is considered commented (correctly). The implementation is sound, but you might consider adding a test case explicitly documenting the expected behavior for edge cases like when the opening tag is uncommented but contains only the method name, with :async on a subsequent line within the same ERB block.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8382e94 and 9f6dc24.

📒 Files selected for processing (2)
  • lib/react_on_rails/doctor.rb (2 hunks)
  • spec/lib/react_on_rails/doctor_spec.rb (1 hunks)
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.074Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The `react_on_rails_pro?` method validates licenses and should raise errors early (including during path resolution in methods like `server_bundle?`) to enforce licensing requirements rather than failing later with obscure errors.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.
Learnt from: alexeyr-ci
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/ReactOnRailsRSC.ts:87-87
Timestamp: 2024-12-12T13:07:09.929Z
Learning: When handling errors in 'node_package/src/ReactOnRailsRSC.ts', include the error stack in error messages in development and test environments to aid debugging.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/turbolinksUtils.ts:34-36
Timestamp: 2025-02-13T16:50:26.861Z
Learning: In React on Rails, when checking for Turbolinks version 5 using `turbolinksVersion5()`, always ensure `Turbolinks` exists first by checking `turbolinksInstalled()` to prevent TypeError when accessing properties.
Learnt from: Romex91
Repo: shakacode/react_on_rails PR: 1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/clientStartup.ts:18-21
Timestamp: 2025-02-13T16:50:47.848Z
Learning: In the react_on_rails module, the `reactOnRailsPageUnloaded` function in clientStartup.ts is intentionally kept private as it's only used internally as a callback for `onPageUnloaded`.
📚 Learning: 2025-10-23T17:22:01.074Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.074Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The `react_on_rails_pro?` method validates licenses and should raise errors early (including during path resolution in methods like `server_bundle?`) to enforce licensing requirements rather than failing later with obscure errors.

Applied to files:

  • spec/lib/react_on_rails/doctor_spec.rb
  • lib/react_on_rails/doctor.rb
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.

Applied to files:

  • spec/lib/react_on_rails/doctor_spec.rb
  • lib/react_on_rails/doctor.rb
📚 Learning: 2025-09-15T21:24:48.207Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.

Applied to files:

  • spec/lib/react_on_rails/doctor_spec.rb
  • lib/react_on_rails/doctor.rb
📚 Learning: 2024-12-12T13:07:09.929Z
Learnt from: alexeyr-ci
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/ReactOnRailsRSC.ts:87-87
Timestamp: 2024-12-12T13:07:09.929Z
Learning: When handling errors in 'node_package/src/ReactOnRailsRSC.ts', include the error stack in error messages in development and test environments to aid debugging.

Applied to files:

  • lib/react_on_rails/doctor.rb
📚 Learning: 2025-02-13T16:50:26.861Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/turbolinksUtils.ts:34-36
Timestamp: 2025-02-13T16:50:26.861Z
Learning: In React on Rails, when checking for Turbolinks version 5 using `turbolinksVersion5()`, always ensure `Turbolinks` exists first by checking `turbolinksInstalled()` to prevent TypeError when accessing properties.

Applied to files:

  • lib/react_on_rails/doctor.rb
🧬 Code graph analysis (1)
lib/react_on_rails/doctor.rb (2)
lib/react_on_rails/utils.rb (1)
  • react_on_rails_pro? (256-264)
lib/react_on_rails/system_checker.rb (2)
  • add_error (16-18)
  • add_info (28-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: lint-js-and-ruby
  • GitHub Check: build-dummy-app-webpack-test-bundles
  • GitHub Check: build-dummy-app-webpack-test-bundles
  • GitHub Check: build
  • GitHub Check: claude-review
🔇 Additional comments (8)
spec/lib/react_on_rails/doctor_spec.rb (3)

192-268: Excellent test coverage for async usage detection.

The test suite comprehensively covers the Pro-aware behavior, async detection in views and config, and the absence of async usage. The structure is clear and properly isolated with mocks.


270-410: Thorough testing of view file scanning with edge cases.

The tests cover various syntax forms (:async, async: true), false positive prevention (defer: "async"), comment scenarios across ERB/HAML/HTML, and multi-line tag handling. This comprehensive coverage ensures the scanner behaves correctly across different template formats.


412-458: Complete config strategy detection test coverage.

The tests validate detection of :async strategy, differentiation from other strategies (:defer), handling of missing config files, and proper filtering of commented-out configuration. This ensures robust config file parsing.

lib/react_on_rails/doctor.rb (5)

176-176: Good integration of async check into development diagnostics.

The new check_async_usage method is appropriately placed in the development checks section, ensuring it runs during normal doctor diagnosis.


1151-1181: Well-implemented Pro-aware async usage validation.

The implementation correctly:

  • Skips validation when Pro is installed (async is supported)
  • Aggregates findings from both view files and config
  • Reports clear error messages with actionable guidance
  • Provides documentation links for resolution

The early return for Pro installations prevents false positives and aligns with the learning that Pro features should be validated through proper channels.

Based on learnings


1183-1203: Robust view file scanning with proper error handling.

The implementation scans both ERB and HAML templates, filters out commented usage, relativizes paths for display, and includes appropriate error handling for file system and encoding issues. The use of Rails.logger.debug for error logging is appropriate for a diagnostic tool.


1205-1219: Regex pattern correctly addresses past review feedback.

The single regex pattern /javascript_pack_tag[^<]*(?::async\b|async:\s*true)/ correctly matches:

  • :async symbol syntax
  • async: true hash syntax

The pattern appropriately avoids false positives like defer: "async" (where "async" is a string value, not the async option). The use of word boundary \b and the [^<]* construct for matching within ERB tags handles multi-line tags correctly.

The past review comment regarding the overly broad second regex pattern has been properly addressed in commit 9f6dc24.


1249-1268: Solid config detection with appropriate filtering.

The method correctly:

  • Filters out commented lines before checking for :async strategy
  • Uses word boundary \b to match the complete symbol, not partial matches like :async_mode
  • Includes error handling for file system and encoding errors
  • Returns false on errors (safe default)

The implementation properly detects the async loading strategy while avoiding false positives from commented configuration.

justin808 and others added 3 commits November 13, 2025 14:28
Adds proactive detection of :async loading strategy usage in projects
without React on Rails Pro, which can cause component registration race
conditions.

The doctor now checks for:
1. javascript_pack_tag with :async in view files
2. config.generated_component_packs_loading_strategy = :async in initializer

When detected without Pro, provides clear guidance:
- Upgrade to React on Rails Pro (recommended)
- Change to :defer or :sync loading strategy

This complements PR #1993's configuration validation by adding runtime
doctor checks that help users identify and fix async usage issues during
development.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Addressed code review feedback to make the async usage detection more
robust and accurate:

1. Enhanced error handling:
   - Replace broad StandardError catches with specific exceptions
     (Errno::ENOENT, Encoding::InvalidByteSequenceError,
     Encoding::UndefinedConversionError)
   - Add debug logging when errors occur (if Rails logger available)

2. Add comment filtering to reduce false positives:
   - Filter out commented Ruby lines in config files (lines starting with #)
   - Skip ERB comments (<%# ... %>), HAML comments (-# ...), and HTML
     comments (<!-- ... -->) in view files
   - Prevent false alarms from commented-out code

3. Refactor for code quality:
   - Extract scan_pattern_for_async and file_has_async_pack_tag? helpers
   - Reduce cyclomatic complexity in scan_view_files_for_async_pack_tag
   - All RuboCop checks pass

4. Expand test coverage:
   - Add tests for ERB, HAML, and HTML comment scenarios
   - Add test for commented config with async strategy
   - All 13 async-related tests pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
…nsive tests

- Remove overly broad string form regex that could cause false positives
- Add support for both :async symbol and async: true hash syntax
- Add word boundary \b to prevent matching :async_mode or similar
- Improve multi-line javascript_pack_tag detection
- Add comprehensive inline documentation with examples
- Add test for async: true pattern detection
- Add test to confirm defer: "async" does not trigger false positive
- Add test for multi-line javascript_pack_tag calls
- Improve comment filtering logic to handle multi-line tags correctly
- Use String#include? for better performance (RuboCop compliance)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@justin808 justin808 force-pushed the justin808/doctor-check-async-requires-pro branch from 9f6dc24 to 4cdff17 Compare November 14, 2025 00:28
Copy link
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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9f6dc24 and 4cdff17.

📒 Files selected for processing (2)
  • lib/react_on_rails/doctor.rb (2 hunks)
  • spec/lib/react_on_rails/doctor_spec.rb (1 hunks)
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.074Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The `react_on_rails_pro?` method validates licenses and should raise errors early (including during path resolution in methods like `server_bundle?`) to enforce licensing requirements rather than failing later with obscure errors.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.
Learnt from: alexeyr-ci
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/ReactOnRailsRSC.ts:87-87
Timestamp: 2024-12-12T13:07:09.929Z
Learning: When handling errors in 'node_package/src/ReactOnRailsRSC.ts', include the error stack in error messages in development and test environments to aid debugging.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/turbolinksUtils.ts:34-36
Timestamp: 2025-02-13T16:50:26.861Z
Learning: In React on Rails, when checking for Turbolinks version 5 using `turbolinksVersion5()`, always ensure `Turbolinks` exists first by checking `turbolinksInstalled()` to prevent TypeError when accessing properties.
Learnt from: Romex91
Repo: shakacode/react_on_rails PR: 1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
📚 Learning: 2025-10-23T17:22:01.074Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.074Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The `react_on_rails_pro?` method validates licenses and should raise errors early (including during path resolution in methods like `server_bundle?`) to enforce licensing requirements rather than failing later with obscure errors.

Applied to files:

  • lib/react_on_rails/doctor.rb
  • spec/lib/react_on_rails/doctor_spec.rb
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.

Applied to files:

  • lib/react_on_rails/doctor.rb
  • spec/lib/react_on_rails/doctor_spec.rb
📚 Learning: 2025-09-15T21:24:48.207Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.

Applied to files:

  • lib/react_on_rails/doctor.rb
  • spec/lib/react_on_rails/doctor_spec.rb
📚 Learning: 2024-12-12T13:07:09.929Z
Learnt from: alexeyr-ci
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/ReactOnRailsRSC.ts:87-87
Timestamp: 2024-12-12T13:07:09.929Z
Learning: When handling errors in 'node_package/src/ReactOnRailsRSC.ts', include the error stack in error messages in development and test environments to aid debugging.

Applied to files:

  • lib/react_on_rails/doctor.rb
📚 Learning: 2025-02-13T16:50:26.861Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/turbolinksUtils.ts:34-36
Timestamp: 2025-02-13T16:50:26.861Z
Learning: In React on Rails, when checking for Turbolinks version 5 using `turbolinksVersion5()`, always ensure `Turbolinks` exists first by checking `turbolinksInstalled()` to prevent TypeError when accessing properties.

Applied to files:

  • lib/react_on_rails/doctor.rb
🧬 Code graph analysis (1)
lib/react_on_rails/doctor.rb (2)
lib/react_on_rails/utils.rb (1)
  • react_on_rails_pro? (256-264)
lib/react_on_rails/system_checker.rb (2)
  • add_error (16-18)
  • add_info (28-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build
  • GitHub Check: claude-review
🔇 Additional comments (9)
lib/react_on_rails/doctor.rb (6)

176-176: LGTM!

The integration of check_async_usage into the development checks flow is well-placed and consistent with other diagnostic checks.


1151-1181: LGTM!

The check_async_usage method correctly implements the Pro feature gate and provides clear, actionable error messages with guidance. The early return when Pro is installed is appropriate, and the aggregation of issues before reporting is well-structured.


1183-1191: LGTM!

Robust error handling with appropriate fallback behavior for a diagnostic tool. The debug logging helps troubleshooting without cluttering output.


1193-1203: LGTM!

The filtering logic is efficient and correctly skips files where async is only in comments. Using filter_map is a good choice for combining filter and map operations.


1205-1219: Well-crafted regex pattern with excellent documentation.

The regex correctly matches both :async symbol and async: true hash syntax while avoiding false positives. The word boundary \b ensures it doesn't match longer symbols like :async_mode. The documented theoretical edge case data: { async: true } is acceptable as it would only produce a harmless warning in extremely unlikely scenarios.


1249-1268: LGTM!

The config detection logic correctly filters out commented lines and uses word boundaries to ensure precise matching. Error handling with debug logging is appropriate for a diagnostic tool.

spec/lib/react_on_rails/doctor_spec.rb (3)

192-268: LGTM!

Comprehensive test coverage for the Pro/non-Pro branching and async detection in both view files and initializer config. The tests correctly verify error reporting and the no-issue scenarios.


270-410: Good test coverage for common scenarios.

The tests comprehensively cover detection of :async symbol, async: true hash syntax, false positive prevention for defer: "async", various comment styles (ERB, HAML, HTML), and multi-line tags.

Note: Additional test coverage is needed for the false positive scenario identified in the implementation review (uncommented javascript_pack_tag without :async coexisting with commented :async).


412-458: LGTM!

Complete test coverage for all branches of config_has_async_loading_strategy?, including the :async detection, different strategies, missing file, and commented-out configuration scenarios.

Extract comment pattern regexes to named constants for better
maintainability and reusability. Add defensive log_debug helper
method to safely handle Rails.logger access.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Copy link
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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4cdff17 and cda9a31.

📒 Files selected for processing (1)
  • lib/react_on_rails/doctor.rb (2 hunks)
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.074Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The `react_on_rails_pro?` method validates licenses and should raise errors early (including during path resolution in methods like `server_bundle?`) to enforce licensing requirements rather than failing later with obscure errors.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.
Learnt from: alexeyr-ci
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/ReactOnRailsRSC.ts:87-87
Timestamp: 2024-12-12T13:07:09.929Z
Learning: When handling errors in 'node_package/src/ReactOnRailsRSC.ts', include the error stack in error messages in development and test environments to aid debugging.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/turbolinksUtils.ts:34-36
Timestamp: 2025-02-13T16:50:26.861Z
Learning: In React on Rails, when checking for Turbolinks version 5 using `turbolinksVersion5()`, always ensure `Turbolinks` exists first by checking `turbolinksInstalled()` to prevent TypeError when accessing properties.
Learnt from: justin808
Repo: shakacode/react_on_rails PR: 1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.
Learnt from: alexeyr-ci2
Repo: shakacode/react_on_rails PR: 1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.
Learnt from: Romex91
Repo: shakacode/react_on_rails PR: 1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/clientStartup.ts:18-21
Timestamp: 2025-02-13T16:50:47.848Z
Learning: In the react_on_rails module, the `reactOnRailsPageUnloaded` function in clientStartup.ts is intentionally kept private as it's only used internally as a callback for `onPageUnloaded`.
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.

Applied to files:

  • lib/react_on_rails/doctor.rb
📚 Learning: 2025-10-23T17:22:01.074Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.074Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The `react_on_rails_pro?` method validates licenses and should raise errors early (including during path resolution in methods like `server_bundle?`) to enforce licensing requirements rather than failing later with obscure errors.

Applied to files:

  • lib/react_on_rails/doctor.rb
📚 Learning: 2025-09-15T21:24:48.207Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.

Applied to files:

  • lib/react_on_rails/doctor.rb
📚 Learning: 2025-04-26T21:55:55.874Z
Learnt from: alexeyr-ci2
Repo: shakacode/react_on_rails PR: 1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.

Applied to files:

  • lib/react_on_rails/doctor.rb
📚 Learning: 2024-12-12T13:07:09.929Z
Learnt from: alexeyr-ci
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/ReactOnRailsRSC.ts:87-87
Timestamp: 2024-12-12T13:07:09.929Z
Learning: When handling errors in 'node_package/src/ReactOnRailsRSC.ts', include the error stack in error messages in development and test environments to aid debugging.

Applied to files:

  • lib/react_on_rails/doctor.rb
📚 Learning: 2025-02-13T16:50:26.861Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/turbolinksUtils.ts:34-36
Timestamp: 2025-02-13T16:50:26.861Z
Learning: In React on Rails, when checking for Turbolinks version 5 using `turbolinksVersion5()`, always ensure `Turbolinks` exists first by checking `turbolinksInstalled()` to prevent TypeError when accessing properties.

Applied to files:

  • lib/react_on_rails/doctor.rb
🧬 Code graph analysis (1)
lib/react_on_rails/doctor.rb (2)
lib/react_on_rails/utils.rb (1)
  • react_on_rails_pro? (256-264)
lib/react_on_rails/system_checker.rb (2)
  • add_error (16-18)
  • add_info (28-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build
  • GitHub Check: claude-review
🔇 Additional comments (7)
lib/react_on_rails/doctor.rb (7)

1151-1154: LGTM! Well-organized comment pattern constants.

The comment patterns for ERB, HAML, and HTML are clearly defined and properly support the filtering logic used later in the async detection.


1156-1186: LGTM! Well-structured async usage detection.

The method correctly:

  • Skips checks when Pro is installed (async is fully supported)
  • Aggregates issues from both view files and config
  • Provides clear error messages with actionable guidance
  • Links to relevant documentation

1188-1196: LGTM! Robust file scanning with appropriate error handling.

The method properly scans ERB and HAML files, handles file system and encoding errors gracefully, and logs issues for debugging.


1198-1208: LGTM! Clean file processing logic.

The method correctly filters files by checking for uncommented async usage and returns relativized paths for better readability.


1210-1224: LGTM! Well-designed regex pattern with clear documentation.

The regex correctly matches both :async symbol and async: true hash syntax while using word boundaries to avoid false positives. The [^<]* pattern properly handles multi-line tags within ERB blocks, and the comment thoroughly explains edge cases.


1254-1273: LGTM! Correct config parsing with proper comment filtering.

The method correctly:

  • Checks file existence before reading
  • Filters out commented lines
  • Uses word boundary to match the complete :async symbol
  • Handles errors gracefully with safe fallback

1275-1279: LGTM! Clean logging helper with proper guards.

The method safely logs debug messages only when Rails.logger is available, preventing errors in environments where logging may not be configured.

@justin808
Copy link
Member Author

@claude review this PR

@claude
Copy link

claude bot commented Nov 16, 2025

Claude finished @justin808's task —— View job


Code Review Complete

Tasks:

  • Read changed files to understand implementation
  • Analyze git diff to see exact changes
  • Review code for bugs and issues
  • Evaluate test coverage
  • Formulate comprehensive review feedback

Summary

This PR adds proactive detection for :async loading strategy usage in projects without React on Rails Pro. While the implementation is well-intentioned and has good test coverage, there is a critical false positive bug that must be fixed before merging.


Critical Issue: False Positive Bug 🔴

Location: lib/react_on_rails/doctor.rb:1238

Problem: The content_has_only_commented_async? method produces false positives when a file contains:

  1. An uncommented javascript_pack_tag WITHOUT :async
  2. A commented javascript_pack_tag WITH :async

Example that triggers false positive:

<%= javascript_pack_tag "app", defer: true %>
<%# javascript_pack_tag "other", :async %>

Current behavior: Reports async usage ❌ (false positive)
Expected behavior: Should NOT report (only the commented line has :async) ✅

Root cause: Line 1238 checks line.include?("javascript_pack_tag") but doesn't verify that the line actually contains :async. So any uncommented pack tag triggers detection even when it has no async option.

Fix required:

# Line 1236-1238 should be:
has_uncommented_async = content.each_line.any? do |line|
  # Skip lines that don't contain javascript_pack_tag with :async
  next unless file_has_async_pack_tag?(line)  # Changed from line.include?("javascript_pack_tag")
  
  # Skip ERB comments (<%# ... %>)
  # ... rest of the code

Missing test coverage: There's no test case for this specific scenario. The existing test "when view files contain defer: \"async\" (false positive check)" tests a different edge case.


Other Review Findings

Strengths ✅

  1. Excellent test coverage - 268 new test examples covering most scenarios
  2. Good error messages - Clear, actionable guidance with emoji indicators
  3. Proper Pro gate - Correctly skips check when Pro is installed
  4. Multiple detection methods - Checks both view files and config
  5. Robust error handling - Catches file system and encoding errors
  6. Good regex pattern - The file_has_async_pack_tag? regex correctly matches both :async and async: true syntax

Code Quality Observations

  1. Well-structured constants (ERB_COMMENT_PATTERN, etc.) - Good organization
  2. Good documentation - Inline comments explain edge cases and regex patterns
  3. Proper use of filter_map - Efficient combining of filter and map operations
  4. Safe debug logging - Guards against missing Rails.logger

Minor Suggestions (non-blocking)

  1. Line 1220 - The documented edge case data: { async: true } is acceptable as mentioned
  2. Multi-line comment handling - Current implementation checks if opening line is commented, which is reasonable for most cases
  3. Performance - Scanning all view files could be slow in large projects, but acceptable for a development-time doctor check

Recommendation

❌ DO NOT MERGE until the critical false positive bug is fixed.

Required changes:

  1. Fix line 1238 to use file_has_async_pack_tag?(line) instead of line.include?("javascript_pack_tag")
  2. Add test case for the false positive scenario described above

After fix:

  • This will be a valuable addition to help developers avoid async race conditions
  • The implementation will be solid and ready for production use

Test Case to Add

context "when file has uncommented pack tag without :async and commented one with :async" do
  before do
    allow(Dir).to receive(:glob).with("app/views/**/*.erb")
                                .and_return(["app/views/layouts/application.html.erb"])
    allow(Dir).to receive(:glob).with("app/views/**/*.haml").and_return([])
    allow(File).to receive(:exist?).with("app/views/layouts/application.html.erb").and_return(true)
    allow(File).to receive(:read).with("app/views/layouts/application.html.erb")
                                 .and_return('<%= javascript_pack_tag "app", defer: true %>
<%# javascript_pack_tag "other", :async %>')
  end

  it "does not return files (only commented line has :async)" do
    files = doctor.send(:scan_view_files_for_async_pack_tag)
    expect(files).to be_empty
  end
end

Great work on the comprehensive testing and thoughtful implementation! Just need to fix this one bug and you're good to go.


… tags

Critical bug fix: The content_has_only_commented_async? method was
incorrectly flagging files that had:
- Uncommented javascript_pack_tag WITHOUT :async
- Commented javascript_pack_tag WITH :async

The previous implementation checked if ANY uncommented javascript_pack_tag
existed, not specifically ones with :async. This caused false positives.

Solution: Refactored to remove all commented lines first, then check if
:async exists in the remaining content. This handles both single-line
and multi-line tags correctly and is more robust.

Added comprehensive test case to prevent regression.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@claude
Copy link

claude bot commented Nov 16, 2025

Code Review - PR #2010: Add doctor checks for :async usage without React on Rails Pro

Thank you for this well-implemented feature! I've reviewed the changes and overall the implementation is solid with excellent test coverage. Here's my detailed feedback:


Strengths

  1. Excellent Test Coverage - 285 lines of comprehensive tests covering:

    • Multiple comment formats (ERB, HAML, HTML)
    • Multi-line javascript_pack_tag detection
    • Edge cases like defer: "async" (string value vs option)
    • Both view file and initializer checks
    • Pro gem detection
  2. Robust Error Handling - Proper exception handling for:

    • Missing files (Errno::ENOENT)
    • Encoding errors (Encoding::InvalidByteSequenceError)
    • Graceful degradation with empty array returns
  3. Clear User Guidance - Error messages provide:

    • Clear problem identification
    • Actionable solutions
    • Documentation link
  4. Smart Comment Detection - Properly filters out commented-out code in multiple formats

  5. Good Regex Patterns - Uses word boundaries (\b) to avoid false positives like :async_mode


🔍 Potential Issues & Suggestions

1. Performance Consideration

Location: doctor.rb:1188-1196

The scan_view_files_for_async_pack_tag method uses Dir.glob which can be slow on large codebases with many view files.

Suggestion:

def scan_view_files_for_async_pack_tag
  view_patterns = ["app/views/**/*.erb", "app/views/**/*.haml"]
  files_with_async = view_patterns.flat_map { |pattern| scan_pattern_for_async(pattern) }
  files_with_async.compact
rescue Errno::ENOENT, Encoding::InvalidByteSequenceError, Encoding::UndefinedConversionError => e
  log_debug("Error scanning view files for async: #{e.message}")
  []
end

Consider adding early exit if no files match patterns, though this is likely negligible in practice.


2. Regex Pattern Potential Edge Case

Location: doctor.rb:1223

The regex /javascript_pack_tag[^<]*(?::async\b|async:\s*true)/ uses [^<]* to match content until the closing ERB tag.

Potential Issue:
This could theoretically match across multiple ERB tags if content like this exists:

<%= javascript_pack_tag "app", defer: true
<div>Some content</div>
<%= some_other_tag :async %>

Analysis: This is extremely unlikely in real code and would be caught by the comment detection logic, but worth noting.

Suggestion (optional): Consider using %>.*?(?::async\b|async:\s*true) with multiline mode if you want stricter ERB tag boundaries, though the current approach is probably fine for 99.9% of cases.


3. Missing Edge Case in Tests

Observation: Tests cover many scenarios but could add:

context "when file contains async: false (explicitly disabled)" do
  before do
    # Test that async: false doesn't trigger false positive
    allow(File).to receive(:read).with("app/views/layouts/application.html.erb")
                                 .and_return('<%= javascript_pack_tag "app", async: false %>')
  end

  it "does not return files (async is explicitly disabled)" do
    files = doctor.send(:scan_view_files_for_async_pack_tag)
    expect(files).to be_empty
  end
end

Impact: Low - the regex async:\s*true correctly only matches true, not false, but explicit test coverage would be nice.


4. Potential Enhancement: Line Numbers

Location: doctor.rb:1167-1169

When reporting files with async usage, it could be helpful to include line numbers:

async_issues << "  • #{file}:#{line_number}"

This would help developers quickly locate the issue in large view files.

Trade-off: Requires refactoring to track line numbers during scanning, so maybe not worth the complexity for this use case.


🛡️ Security Considerations

No security concerns identified

  • File reading is limited to app/views and config directories
  • No user input is processed unsafely
  • Proper encoding error handling

📊 Code Quality

  • ✅ Well-documented with inline comments explaining regex patterns
  • ✅ Good method naming and single responsibility
  • ✅ Follows existing codebase patterns
  • ✅ Error messages are user-friendly with emoji indicators

🎯 Testing Recommendations

  1. Consider adding integration test: Test the full check_development flow to ensure check_async_usage is called correctly
  2. Test the rescue block: Mock a file read error to verify the exception handling path
  3. Add async: false test case as mentioned above

📝 Minor Nitpicks

  1. Consistent quoting: Mix of single and double quotes in tests (Ruby convention prefers single quotes when no interpolation needed)
  2. Line 1231: Comment says "return true unless" which reads a bit awkwardly - consider "return true if there's no async tag" for clarity

Recommendation

APPROVE with minor suggestions. This is production-ready code that:

  • Solves the stated problem effectively
  • Has excellent test coverage (155+ examples)
  • Handles edge cases well
  • Provides clear user guidance

The suggestions above are optimizations and enhancements, not blockers.


📚 Additional Notes

Per CLAUDE.md:

  • ✅ Ensure bundle exec rubocop passes before merging
  • ✅ Files should end with newline (both files appear to comply)
  • ✅ Update CHANGELOG.md if this is user-visible (this appears to be a user-facing feature, so should be documented)

Great work! 🎉

Copy link
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: 0

🧹 Nitpick comments (1)
lib/react_on_rails/doctor.rb (1)

1210-1224: Well-crafted regex with thorough documentation.

The regex correctly handles both :async symbol and async: true hash syntax with word boundaries preventing false positives. The detailed comment is helpful.

One rare edge case: [^<]* stops at < characters, so string literals containing < in arguments (e.g., data: { message: "<hello>" }) would prevent matching :async after them. This is extremely unlikely in practice.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cda9a31 and eb0b353.

📒 Files selected for processing (2)
  • lib/react_on_rails/doctor.rb (2 hunks)
  • spec/lib/react_on_rails/doctor_spec.rb (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • spec/lib/react_on_rails/doctor_spec.rb
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.074Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The `react_on_rails_pro?` method validates licenses and should raise errors early (including during path resolution in methods like `server_bundle?`) to enforce licensing requirements rather than failing later with obscure errors.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.
Learnt from: alexeyr-ci
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/ReactOnRailsRSC.ts:87-87
Timestamp: 2024-12-12T13:07:09.929Z
Learning: When handling errors in 'node_package/src/ReactOnRailsRSC.ts', include the error stack in error messages in development and test environments to aid debugging.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/turbolinksUtils.ts:34-36
Timestamp: 2025-02-13T16:50:26.861Z
Learning: In React on Rails, when checking for Turbolinks version 5 using `turbolinksVersion5()`, always ensure `Turbolinks` exists first by checking `turbolinksInstalled()` to prevent TypeError when accessing properties.
Learnt from: justin808
Repo: shakacode/react_on_rails PR: 1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/clientStartup.ts:18-21
Timestamp: 2025-02-13T16:50:47.848Z
Learning: In the react_on_rails module, the `reactOnRailsPageUnloaded` function in clientStartup.ts is intentionally kept private as it's only used internally as a callback for `onPageUnloaded`.
Learnt from: theforestvn88
Repo: shakacode/react_on_rails PR: 1620
File: spec/dummy/client/app/startup/HelloTurboStream.jsx:3-3
Timestamp: 2024-10-08T20:53:47.076Z
Learning: The `RailsContext` import in `spec/dummy/client/app/startup/HelloTurboStream.jsx` is used later in the project, as clarified by the user theforestvn88.
Learnt from: theforestvn88
Repo: shakacode/react_on_rails PR: 1620
File: spec/dummy/client/app/startup/HelloTurboStream.jsx:3-3
Timestamp: 2024-07-27T10:08:35.868Z
Learning: The `RailsContext` import in `spec/dummy/client/app/startup/HelloTurboStream.jsx` is used later in the project, as clarified by the user theforestvn88.
Learnt from: Romex91
Repo: shakacode/react_on_rails PR: 1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.

Applied to files:

  • lib/react_on_rails/doctor.rb
📚 Learning: 2025-10-23T17:22:01.074Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.074Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The `react_on_rails_pro?` method validates licenses and should raise errors early (including during path resolution in methods like `server_bundle?`) to enforce licensing requirements rather than failing later with obscure errors.

Applied to files:

  • lib/react_on_rails/doctor.rb
📚 Learning: 2024-12-12T13:07:09.929Z
Learnt from: alexeyr-ci
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/ReactOnRailsRSC.ts:87-87
Timestamp: 2024-12-12T13:07:09.929Z
Learning: When handling errors in 'node_package/src/ReactOnRailsRSC.ts', include the error stack in error messages in development and test environments to aid debugging.

Applied to files:

  • lib/react_on_rails/doctor.rb
📚 Learning: 2025-04-26T21:55:55.874Z
Learnt from: alexeyr-ci2
Repo: shakacode/react_on_rails PR: 1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.

Applied to files:

  • lib/react_on_rails/doctor.rb
📚 Learning: 2025-09-15T21:24:48.207Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.

Applied to files:

  • lib/react_on_rails/doctor.rb
📚 Learning: 2025-02-13T16:50:26.861Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/turbolinksUtils.ts:34-36
Timestamp: 2025-02-13T16:50:26.861Z
Learning: In React on Rails, when checking for Turbolinks version 5 using `turbolinksVersion5()`, always ensure `Turbolinks` exists first by checking `turbolinksInstalled()` to prevent TypeError when accessing properties.

Applied to files:

  • lib/react_on_rails/doctor.rb
🧬 Code graph analysis (1)
lib/react_on_rails/doctor.rb (2)
lib/react_on_rails/utils.rb (1)
  • react_on_rails_pro? (256-264)
lib/react_on_rails/system_checker.rb (2)
  • add_error (16-18)
  • add_info (28-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: build-dummy-app-webpack-test-bundles
  • GitHub Check: lint-js-and-ruby
  • GitHub Check: build-dummy-app-webpack-test-bundles
  • GitHub Check: build
  • GitHub Check: claude-review
🔇 Additional comments (7)
lib/react_on_rails/doctor.rb (7)

176-176: LGTM - Clean integration into development checks.

The async usage check is appropriately placed alongside other development environment validations.


1150-1155: Well-designed comment detection patterns.

The patterns correctly handle ERB, HAML, and HTML comment syntaxes to filter out commented async usage.


1156-1186: Excellent validation logic with helpful guidance.

The Pro check correctly prevents false positives, and the error messages provide actionable guidance with clear upgrade/alternative options.


1188-1208: Solid file scanning implementation.

Good use of filter_map for efficiency, appropriate error handling for file-related exceptions, and relative paths for user-friendly output.


1226-1244: Correct comment filtering logic.

The implementation correctly handles the scenario from past review comments: when uncommented javascript_pack_tag exists alongside commented async usage, it properly filters the commented lines and only checks uncommented content for async. This resolves the previous false-positive concern.


1246-1265: Robust config detection with proper comment handling.

The method correctly filters commented config lines and uses word boundaries to avoid false matches with similar symbols like :async_mode.


1267-1271: LGTM - Defensive logging helper.

Proper guard against missing Rails.logger for robust operation across different runtime contexts.

Extended the async detection functionality to support Slim templates
in addition to ERB and HAML:

- Added SLIM_COMMENT_PATTERN constant for Slim comment syntax (/)
- Added app/views/**/*.slim to view file scanning patterns
- Included Slim comment filtering in content_has_only_commented_async?
- Added comprehensive test cases for Slim files:
  * Slim files with uncommented :async (should detect)
  * Slim files with commented :async (should not detect)
- Added slim stub mocks to all existing test cases for consistency

This ensures users of Slim templates get the same async detection
warnings as ERB and HAML users.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Copy link
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: 0

🧹 Nitpick comments (5)
lib/react_on_rails/doctor.rb (3)

1151-1156: Comment patterns cover common single-line comment syntaxes but miss multi-line blocks.

The ERB/HAML/SLIM/HTML comment patterns are correct for single-line comments and align with the specs. However, they will not filter async usage inside multi-line HTML or HAML/SLIM block comments (e.g., -# or / followed by indented lines), which could produce rare false positives.

If you want to be stricter, consider extending the filtering logic in content_has_only_commented_async? to track HAML/SLIM block comments and multi-line HTML comments, rather than relying on per-line regexes alone. This can be done by walking each_line with a simple state machine for “inside block comment”.

-    ERB_COMMENT_PATTERN = /<%\s*#.*javascript_pack_tag/
-    HAML_COMMENT_PATTERN = /^\s*-#.*javascript_pack_tag/
-    SLIM_COMMENT_PATTERN = %r{^\s*/.*javascript_pack_tag}
-    HTML_COMMENT_PATTERN = /<!--.*javascript_pack_tag/
+    ERB_COMMENT_PATTERN  = /<%\s*#.*javascript_pack_tag/
+    HAML_COMMENT_PATTERN = /^\s*-#.*javascript_pack_tag/
+    SLIM_COMMENT_PATTERN = %r{^\s*/.*javascript_pack_tag}
+    HTML_COMMENT_PATTERN = /<!--.*javascript_pack_tag/
+
+    # NOTE: These patterns only handle single-line comments. For block comments
+    # (e.g., HAML `-#` or HTML `<!--` with async on a following line), we rely
+    # on additional logic in content_has_only_commented_async?.

1227-1246: Behavior and comment for content_has_only_commented_async? are slightly out of sync.

The method currently returns true when:

  • there is only commented async usage, or
  • there is no async usage at all (because of the early return true unless file_has_async_pack_tag?(content)).

This matches how it’s used (skip files with no active async), but the comments say “ONLY commented async usage exists”. To avoid confusion for future maintainers, consider clarifying the docstring to reflect both “none” and “commented-only” cases.

-    def content_has_only_commented_async?(content)
-      # Check if all occurrences of javascript_pack_tag with :async are in comments
-      # Returns true if ONLY commented async usage exists (no active async usage)
+    def content_has_only_commented_async?(content)
+      # Check if there is any *active* javascript_pack_tag with :async.
+      # Returns true when there is no active async usage (either none at all,
+      # or all occurrences are in comments).

1248-1267: Config detection works for common cases; can be made more resilient to multi-line assignments.

config_has_async_loading_strategy? correctly ignores commented lines and detects config.generated_component_packs_loading_strategy = :async on a single line. However, it will miss a configuration split across lines (e.g., config.generated_component_packs_loading_strategy =\n :async), which is valid Ruby and not uncommon in longer configs.

You can handle this by stripping commented lines and applying a single regex over the whole string instead of per-line matching; \s* will naturally consume newlines.

-    def config_has_async_loading_strategy?
-      config_path = "config/initializers/react_on_rails.rb"
-      return false unless File.exist?(config_path)
-
-      content = File.read(config_path)
-      # Check if generated_component_packs_loading_strategy is set to :async
-      # Filter out commented lines (lines starting with # after optional whitespace)
-      content.each_line.any? do |line|
-        # Skip lines that start with # (after optional whitespace)
-        next if line.match?(/^\s*#/)
-
-        # Match: config.generated_component_packs_loading_strategy = :async
-        # Use word boundary \b to ensure :async is the complete symbol, not part of :async_mode etc.
-        line.match?(/config\.generated_component_packs_loading_strategy\s*=\s*:async\b/)
-      end
-    rescue Errno::ENOENT, Encoding::InvalidByteSequenceError, Encoding::UndefinedConversionError => e
+    def config_has_async_loading_strategy?
+      config_path = "config/initializers/react_on_rails.rb"
+      return false unless File.exist?(config_path)
+
+      content = File.read(config_path)
+
+      # Remove commented-out lines, then look for an :async assignment (handles multi-line)
+      uncommented = content.each_line.reject { |line| line.match?(/^\s*#/) }.join
+      uncommented.match?(/config\.generated_component_packs_loading_strategy\s*=\s*:async\b/)
+    rescue Errno::ENOENT, Encoding::InvalidByteSequenceError, Encoding::UndefinedConversionError => e
       # Log the error if Rails logger is available
       log_debug("Error checking async loading strategy: #{e.message}")
       false
     end
spec/lib/react_on_rails/doctor_spec.rb (2)

271-476: Comprehensive view-scanning specs with good regression coverage.

The tests around #scan_view_files_for_async_pack_tag are thorough:

  • They validate both :async and async: true.
  • They guard against the defer: "async" false positive.
  • They verify commented-only async across ERB/HAML/HTML/SLIM, the mixed commented/uncommented scenario, Slim templates, and multi-line pack tags.

These examples align well with the helper methods and would have caught the earlier false-positive issues. Once you extend comment handling for multi-line HTML/HAML/SLIM block comments (if you choose to), adding a couple of specs for those patterns will slot naturally into this block.


478-524: Initializer config specs mirror the doctor behavior well.

The tests for #config_has_async_loading_strategy? cover:

  • positive detection for :async,
  • negative for :defer,
  • missing config file, and
  • commented-out async.

This matches the current implementation. If you adopt the suggested multi-line assignment handling in the implementation, consider adding a spec for a split assignment (= \n :async) to lock in that behavior.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb0b353 and 1f61137.

📒 Files selected for processing (2)
  • lib/react_on_rails/doctor.rb (2 hunks)
  • spec/lib/react_on_rails/doctor_spec.rb (1 hunks)
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.074Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The `react_on_rails_pro?` method validates licenses and should raise errors early (including during path resolution in methods like `server_bundle?`) to enforce licensing requirements rather than failing later with obscure errors.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.
Learnt from: alexeyr-ci
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/ReactOnRailsRSC.ts:87-87
Timestamp: 2024-12-12T13:07:09.929Z
Learning: When handling errors in 'node_package/src/ReactOnRailsRSC.ts', include the error stack in error messages in development and test environments to aid debugging.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/turbolinksUtils.ts:34-36
Timestamp: 2025-02-13T16:50:26.861Z
Learning: In React on Rails, when checking for Turbolinks version 5 using `turbolinksVersion5()`, always ensure `Turbolinks` exists first by checking `turbolinksInstalled()` to prevent TypeError when accessing properties.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1745
File: node_package/src/RSCRequestTracker.ts:8-14
Timestamp: 2025-07-08T05:57:29.630Z
Learning: The global `generateRSCPayload` function in React on Rails Pro (RORP) is provided by the framework during rendering requests, not implemented in application code. The `declare global` statements are used to document the expected interface that RORP will inject at runtime.
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.

Applied to files:

  • spec/lib/react_on_rails/doctor_spec.rb
  • lib/react_on_rails/doctor.rb
📚 Learning: 2025-10-23T17:22:01.074Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.074Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The `react_on_rails_pro?` method validates licenses and should raise errors early (including during path resolution in methods like `server_bundle?`) to enforce licensing requirements rather than failing later with obscure errors.

Applied to files:

  • spec/lib/react_on_rails/doctor_spec.rb
  • lib/react_on_rails/doctor.rb
📚 Learning: 2025-09-15T21:24:48.207Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.

Applied to files:

  • spec/lib/react_on_rails/doctor_spec.rb
  • lib/react_on_rails/doctor.rb
📚 Learning: 2025-04-26T21:55:55.874Z
Learnt from: alexeyr-ci2
Repo: shakacode/react_on_rails PR: 1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.

Applied to files:

  • lib/react_on_rails/doctor.rb
📚 Learning: 2025-02-13T16:50:26.861Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/turbolinksUtils.ts:34-36
Timestamp: 2025-02-13T16:50:26.861Z
Learning: In React on Rails, when checking for Turbolinks version 5 using `turbolinksVersion5()`, always ensure `Turbolinks` exists first by checking `turbolinksInstalled()` to prevent TypeError when accessing properties.

Applied to files:

  • lib/react_on_rails/doctor.rb
🧬 Code graph analysis (1)
lib/react_on_rails/doctor.rb (2)
lib/react_on_rails/utils.rb (1)
  • react_on_rails_pro? (256-264)
lib/react_on_rails/system_checker.rb (2)
  • add_error (16-18)
  • add_info (28-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: build
  • GitHub Check: lint-js-and-ruby
  • GitHub Check: build-dummy-app-webpack-test-bundles
  • GitHub Check: build-dummy-app-webpack-test-bundles
  • GitHub Check: claude-review
🔇 Additional comments (8)
lib/react_on_rails/doctor.rb (6)

171-177: Good integration of async check into development diagnostics.

Hooking check_async_usage into check_development ensures async misuse is surfaced alongside other dev-time issues without affecting other sections. No changes needed here.


1157-1187: Async usage gating correctly respects Pro installations and provides actionable guidance.

The check_async_usage flow looks solid: it short-circuits when ReactOnRails::Utils.react_on_rails_pro? is true, aggregates findings from both view scanning and config, and reports a single primary error plus detailed bullets and remediation steps. The messaging is clear and consistent with the docs and Pro behavior. No changes needed.


1189-1197: View scanning is robust and safely handles IO/encoding errors.

scan_view_files_for_async_pack_tag covers ERB/HAML/SLIM, delegates to scan_pattern_for_async, and gracefully logs and ignores filesystem/encoding errors via log_debug. This matches the goal of a best-effort doctor check without crashing the app. Looks good.


1199-1209: Efficient pattern-based scanning with correct path relativization.

Using Dir.glob(pattern).filter_map with content_has_only_commented_async? and file_has_async_pack_tag? keeps scanning simple and efficient, and returning relativize_path(file) gives user-friendly output. This is a good balance between simplicity and usability.


1211-1225: Regex for detecting async pack tags is appropriate and avoids known false positives.

The pattern /javascript_pack_tag[^<]*(?::async\b|async:\s*true)/ correctly matches both :async and async: true, supports multi-line tags (as covered by specs), and avoids string-valued async like defer: "async". The word boundary on :async also prevents matching :async_mode. The noted data: { async: true } case is an acceptable, documented false positive. No changes needed.


1269-1273: log_debug helper is safe and keeps doctor logs unobtrusive.

Guarding on defined?(Rails.logger) && Rails.logger before calling .debug prevents noisy errors when Rails isn’t fully loaded. This is an appropriate lightweight logging strategy for doctor internals.

spec/lib/react_on_rails/doctor_spec.rb (2)

192-212: Solid coverage of Pro vs non‑Pro branching in check_async_usage.

The specs correctly verify that the check is skipped when react_on_rails_pro? is true and that no errors/warnings are recorded in that case, which guards against unnecessary noise for Pro users.


214-268: Clear scenarios for async usage in views vs config and “no issue” case.

These examples nicely exercise:

  • async usage in a view file,
  • async loading strategy configured in the initializer, and
  • a defer-only config that should not report errors.

The expectations match the implementation’s error messages and give good confidence that user-facing doctor output is stable. No changes needed.

@justin808 justin808 merged commit cbee429 into master Nov 16, 2025
22 checks passed
@justin808 justin808 deleted the justin808/doctor-check-async-requires-pro branch November 16, 2025 05:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants