Skip to content

Harden migration generator for legacy shakapacker setups#2901

Merged
justin808 merged 3 commits intomainfrom
jg-codex/migration-one-shot-hardening
Apr 1, 2026
Merged

Harden migration generator for legacy shakapacker setups#2901
justin808 merged 3 commits intomainfrom
jg-codex/migration-one-shot-hardening

Conversation

@justin808
Copy link
Copy Markdown
Member

@justin808 justin808 commented Mar 31, 2026

Summary

  • make JS dependency installation robust when the package_json gem is unavailable
  • add package-manager-aware fallback installs that only install missing packages
  • reduce duplicate warning noise when package_json is unavailable
  • document migration preflight guidance and expand generator specs

Why

Legacy react-rails + older shakapacker combinations can fail one-shot migration because generator dependency insertion paths assume package_json is available and because fallback installs previously over-upgraded existing dependencies.

Validation

  • bundle exec rspec react_on_rails/spec/react_on_rails/generators/js_dependency_manager_spec.rb
  • bundle exec rspec react_on_rails/spec/react_on_rails/generators/generator_helper_spec.rb
  • fresh OSS migration run: open-flights (generator + compile + tests pass)
  • second OSS app run (Rails-React-Boilerplate) now migrates cleanly through generator flow; compile failure is baseline upstream (missing babel-plugin-transform-react-remove-prop-types)

Note

Medium Risk
Touches core generator install paths (dependency installation, Shakapacker setup, and config rewriting), so mis-detection or edge cases could break installs for some apps, though changes are bounded to scaffolding/migration tooling.

Overview
Improves the react_on_rails:install migration experience for older/legacy Shakapacker setups by making JS dependency management resilient when the package_json gem is missing, including package-manager-aware fallback installs that avoid reinstalling already-present packages.

The generator now (a) detects/records the active package manager from lockfiles (including package-lock.json), seeds package.json#packageManager with the detected tool+version before running shakapacker:install, cleans up browserslist in package.json when .browserslistrc exists, and auto-switches javascript_transpiler from SWC to Babel when JSX is found in .js files (adding required Babel deps).

Separately, RSpec helper injection is made indentation-safe, warning noise is reduced, migration docs add new preflight guidance, and specs are expanded to cover the new behaviors.

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

Summary by CodeRabbit

  • New Features

    • Auto-detect and seed packageManager in package.json from lockfiles before installs; fallback to run the detected package manager for JS installs when needed
    • Detect JSX in .js files and auto-configure Babel compatibility when required
  • Bug Fixes

    • Resolve Browserslist conflicts by preferring .browserslistrc over package.json
  • Documentation

    • Expanded migration preflight checklist with packageManager guidance, example commands, and JSX/transpiler notes
  • Tests

    • Added/expanded tests for package manager detection, install fallbacks, and JSX compatibility flows

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 31, 2026

Walkthrough

This PR updates docs and generators to seed package manager info from lockfiles, remove duplicate Browserslist entries, detect JSX in .js to auto-switch SWC→Babel and add Babel deps, add a JS install fallback calling the system package manager when package_json is unavailable, and updates tests accordingly.

Changes

Cohort / File(s) Summary
Documentation
docs/oss/migrating/migrating-from-react-rails.md
Extended Preflight: require packageManager when lockfiles exist, recommend single Browserslist source, note install generator may auto-switch to Babel for JSX in .js, and added example npm pkg set packageManager=... commands.
Install Generator
react_on_rails/lib/generators/react_on_rails/install_generator.rb
Added seeding of packageManager from lockfiles (uses Open3.capture3), removal of browserslist from package.json when .browserslistrc exists, detection of JSX in app/javascript/**/*.js to switch config/shakapacker.yml from SWC→Babel, and wiring into install flow with warnings on failures.
JS Dependency Manager
react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb
Refactored package add/install to prefer package_json manager, then fallback to invoking the detected package manager via system(...); builds per-manager install args, filters existing packages, and returns explicit booleans with targeted warnings.
Generator Helpers / Messages
react_on_rails/lib/generators/react_on_rails/generator_helper.rb, react_on_rails/lib/generators/react_on_rails/generator_messages.rb
Memoized suppression of repeated package_json LoadError warnings; added SUPPORTED_PACKAGE_MANAGERS and helper methods for lockfile-based package-manager detection and validation; adjusted npm-add return semantics.
RSpec Helper Generation
react_on_rails/lib/generators/react_on_rails/base_generator.rb
Changed insertion logic to match `RSpec.configure do
Tests
react_on_rails/spec/.../*.rb
Added/updated specs: seeding packageManager, browserslist removal, SWC→Babel switching when JSX present, system-call fallback behavior for package installs, suppression of repeated warnings, and Ripper syntax validation for generated helper content; stubs for Open3.capture3 and system added.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant InstallGen as Install Generator
    participant JSDepMgr as JS Dependency Manager
    participant Shell as Shell/Open3
    participant FS as File System

    User->>InstallGen: run generator
    InstallGen->>FS: inspect lockfiles & package.json
    alt packageManager missing
        InstallGen->>Shell: run <pm> --version
        Shell-->>InstallGen: return version
        InstallGen->>FS: write packageManager to package.json
    end

    InstallGen->>FS: check .browserslistrc & package.json
    alt conflict found
        InstallGen->>FS: remove browserslist key from package.json
    end

    InstallGen->>FS: scan app/javascript/**/*.js for JSX
    alt JSX found & swc configured
        InstallGen->>FS: update config/shakapacker.yml -> javascript_transpiler: "babel"
        InstallGen->>JSDepMgr: add_packages(["babel-loader"], dev: true)
    end

    InstallGen->>JSDepMgr: setup_js_dependencies
    JSDepMgr->>JSDepMgr: attempt package_json manager add/install
    alt primary add fails or package_json unavailable
        JSDepMgr->>Shell: system <pm> install args
        Shell-->>JSDepMgr: success/failure
    end
    JSDepMgr-->>InstallGen: install result
    InstallGen-->>User: complete (with warnings if any)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I nibbled locks and parsed package trees,
I sniffed for JSX in hidden .js leaves,
When SWC met JSX, I hopped to Babel land,
Seeded package managers with a careful hand,
Cleared duplicate browserslist — a tidy little stand.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.82% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Harden migration generator for legacy shakapacker setups' accurately and concisely describes the PR's main objective: improving robustness of the migration generator for legacy setups.

✏️ 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/migration-one-shot-hardening

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.

return unless version

package_json_content["packageManager"] = "#{manager}@#{version}"
File.write("package.json", "#{JSON.pretty_generate(package_json_content)}\n")
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.

JSON.pretty_generate reformats the entire package.json. This will normalize all whitespace, ordering, and trailing commas, creating a noisy diff and potentially losing any non-standard formatting the user intentionally has. The same issue applies to resolve_browserslist_conflict_after_shakapacker_install on line 669.

A safer approach would be to do a targeted string-level insertion (e.g. injecting "packageManager": "..." with a regex or a minimal JSON patch) so that only the required change appears in git diff, rather than a full reformat.

def jsx_in_js_files_present?
Dir.glob("app/javascript/**/*.js").any? do |path|
content = File.read(path)
content.match?(%r{<\s*[A-Za-z][\w:-]*(\s|>|/)}) || content.match?(/<\s*>\s*$/)
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.

This regex has a meaningful false-positive risk. The pattern <\s*[A-Za-z][\w:-]*(\s|>|/) will match:

  • HTML strings in test fixtures: assert_match "<div class=...", response.body
  • HTML in comments: # returns <Component/>
  • String templates: fetch("/<div>")
  • Comparison operators that happen to align: x < abc > 0 (only if no space before <)

A false positive will silently switch the project from SWC (fast) to Babel (slower) without the user asking for it, which could be surprising in non-migration runs.

Consider restricting the scan to known JSX entry-point files (e.g. files that also contain import React or from 'react') to reduce false positives, or at minimum gate this only during initial migration generator invocations where --from-react-rails is set.

say "⚙️ Detected JSX in .js files; switching shakapacker javascript_transpiler to babel for compatibility",
:yellow
set_javascript_transpiler_to_babel
add_packages(["babel-loader"], dev: true)
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.

The return values of add_packages and install_js_dependencies are not checked here. If add_packages(["babel-loader"], dev: true) fails (returns false), install_js_dependencies is still called, and both failures are silently swallowed. At a minimum, it's worth surfacing a warning to the user when either step fails, so they know to manually add babel-loader.

base_args + packages
end

def dev_flag_for(package_manager)
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.

dev_flag_for returns nil when the case doesn't match. While fallback_package_manager currently guarantees only %w[npm yarn pnpm bun], the else-less case means any future caller that passes an unguarded value would silently append nil to the install_args array passed to system(), causing a TypeError at runtime. Add an explicit else branch that raises or returns a sensible default:

else "--save-dev" # safe fallback for unknown managers

install_args = build_install_args(package_manager, dev, packages_to_install)

system(*install_args)
rescue StandardError
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.

All StandardErrors are silently swallowed here with no log output. This makes it very hard to diagnose why fallback installs fail. Even a simple GeneratorMessages.add_warning("⚠️ Fallback install failed: #{e.message}") before returning false would help users debug issues like Errno::ENOENT (command not found) or permission errors.

GeneratorMessages.add_warning("⚠️ Failed to clean browserslist conflict: #{e.message}")
end

def detect_package_manager_from_lockfiles
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.

Two separate package-manager detection mechanisms are now in play: detect_package_manager_from_lockfiles here (lockfile-based) and GeneratorMessages.detect_package_manager used by fallback_package_manager in js_dependency_manager.rb. These could return different values for the same project (e.g. the lockfile says yarn but GeneratorMessages.detect_package_manager returns npm if it uses a different heuristic). This could result in seed_package_manager_in_package_json_from_lockfile! writing [email protected] but the fallback installer then calling npm install.

Consider unifying both call sites to use the same detection method.

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 31, 2026

Review Summary

The PR's goal is solid — making the install generator resilient for legacy shakapacker setups is a real pain point and the fallback approach is well-structured. A few issues worth addressing before merge:

Medium priority

JSON.pretty_generate reformats entire package.json (lines 654, 669 in install_generator.rb)
Both seed_package_manager_in_package_json_from_lockfile! and resolve_browserslist_conflict_after_shakapacker_install round-trip through JSON.parse + JSON.pretty_generate, which normalizes all whitespace and key ordering. This produces noisy diffs and can surprise users who have carefully formatted their package.json. A targeted string-level patch (regex insert or a minimal diff) would be less invasive.

JSX regex false positives in jsx_in_js_files_present? (line 805 in install_generator.rb)
The pattern <\s*[A-Za-z][\w:-]*(\s|>|/) will match HTML in string literals, comments, and fixture files. A false positive silently switches the project from SWC to Babel — a build-speed regression — without user intent. At minimum, gate this detection to files that also contain a React import, or only trigger during --from-react-rails migration runs.

Inconsistent package-manager detection (lines 677 vs js_dependency_manager.rb fallback_package_manager)
seed_package_manager_in_package_json_from_lockfile! uses lockfile-based detection, while fallback_package_manager delegates to GeneratorMessages.detect_package_manager. These can diverge: e.g. seed_package_manager_in_package_json_from_lockfile! writes [email protected] into package.json, but the fallback installer then calls npm install. Unifying both to the same detection path would remove this inconsistency.

Low priority

Unchecked return values in ensure_jsx_in_js_compatibility (line 252)
If add_packages(["babel-loader"], dev: true) fails, install_js_dependencies is still called unconditionally and both failures are silently dropped. Users won't know they need to manually add babel-loader.

Silent error swallowing in install_packages_with_fallback (line 513)
rescue StandardError; false; end discards the exception with no warning. Even a single-line warning with e.message would help users diagnose ENOENT / permission failures.

dev_flag_for returns nil for unmatched case (line 538)
Currently safe because fallback_package_manager only returns known values, but the case has no else, so any future caller passing an unrecognized manager would append nil to system() args and raise a TypeError. An explicit else fallback is a cheap guard.


What looks good

  • The @package_json_unavailable_warned deduplication fix is clean.
  • The add_configure_rspec_to_compile_assets rewrite (inserting inside the existing RSpec.configure block rather than replacing the header) is a clear correctness improvement, and the Ripper.sexp validation in the spec is a nice touch.
  • Spec coverage for seed_package_manager_in_package_json_from_lockfile! and resolve_browserslist_conflict_after_shakapacker_install is thorough.
  • filter_missing_packages / package_name_from_spec correctly handles scoped packages (@scope/pkg@version).

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: 4bfa1fa9ac

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

Comment on lines +251 to +253
set_javascript_transpiler_to_babel
add_packages(["babel-loader"], dev: true)
install_js_dependencies
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Add @babel/preset-react when switching SWC projects to Babel

This branch switches javascript_transpiler from SWC to Babel for JSX-in-.js, but it only adds babel-loader. In SWC-default installs, @babel/preset-react is not added on this path, yet the generated babel.config.js still references it, so compilation fails with a missing preset unless the app already had it installed. Please add the preset dependency when this compatibility switch runs.

Useful? React with 👍 / 👎.

Comment on lines +549 to +551
packages.reject do |package_spec|
package_name = package_name_from_spec(package_spec)
package_name && existing.include?(package_name)
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 Preserve versioned installs in fallback package filtering

The fallback installer treats a package as satisfied by name only, so versioned specs are dropped when a package already exists. In legacy setups where package_json is unavailable, requests like react-on-rails@<gem_version> or react@~19.0.4 are skipped if any react-on-rails/react entry exists, leaving stale versions in package.json and potentially mismatching required runtime versions.

Useful? React with 👍 / 👎.

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 3 potential issues.

Autofix Details

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

  • ✅ Fixed: Switching to babel missing @babel/preset-react installation
    • When JSX-in-.js forces a switch from SWC to babel, the generator now also installs Babel React preset dependencies before running install.
  • ✅ Fixed: Fallback package install missing exact version pinning flag
    • Fallback install commands now include exact-version flags for npm, pnpm, yarn, and bun so dependency specs remain pinned.
  • ✅ Fixed: Duplicated lockfile detection logic across three locations
    • Lockfile-based package manager detection and supported-manager validation were centralized in GeneratorMessages and reused by the other generator code paths.

Create PR

Or push these changes by commenting:

@cursor push c1d727b37c
Preview (c1d727b37c)
diff --git a/react_on_rails/lib/generators/react_on_rails/generator_messages.rb b/react_on_rails/lib/generators/react_on_rails/generator_messages.rb
--- a/react_on_rails/lib/generators/react_on_rails/generator_messages.rb
+++ b/react_on_rails/lib/generators/react_on_rails/generator_messages.rb
@@ -5,6 +5,7 @@
 module GeneratorMessages
   PRO_UPGRADE_HINT = "\n\n    💎 For RSC, streaming SSR, and 10-100x faster SSR, try React on Rails Pro:" \
                      "\n       #{Rainbow('https://reactonrails.com/docs/pro/upgrading-to-pro/').cyan.underline}".freeze
+  SUPPORTED_PACKAGE_MANAGERS = %w[npm pnpm yarn bun].freeze
 
   # rubocop:disable Metrics/ClassLength
   class << self
@@ -106,17 +107,25 @@
     # this while the current working directory is the target Rails app root.
     def detect_package_manager
       env_package_manager = ENV.fetch("REACT_ON_RAILS_PACKAGE_MANAGER", nil)&.strip&.downcase
-      return env_package_manager if %w[npm pnpm yarn bun].include?(env_package_manager)
+      return env_package_manager if supported_package_manager?(env_package_manager)
 
-      # Check for lock files to determine package manager
+      # Default to npm (Shakapacker 8.x default) - covers package-lock.json and no lockfile
+      detect_package_manager_from_lockfiles || "npm"
+    end
+
+    def detect_package_manager_from_lockfiles
       return "yarn" if File.exist?("yarn.lock")
       return "pnpm" if File.exist?("pnpm-lock.yaml")
       return "bun" if File.exist?("bun.lock") || File.exist?("bun.lockb")
+      return "npm" if File.exist?("package-lock.json")
 
-      # Default to npm (Shakapacker 8.x default) - covers package-lock.json and no lockfile
-      "npm"
+      nil
     end
 
+    def supported_package_manager?(package_manager)
+      SUPPORTED_PACKAGE_MANAGERS.include?(package_manager)
+    end
+
     private
 
     def build_render_example(component_name:, route:, rsc:)

diff --git a/react_on_rails/lib/generators/react_on_rails/install_generator.rb b/react_on_rails/lib/generators/react_on_rails/install_generator.rb
--- a/react_on_rails/lib/generators/react_on_rails/install_generator.rb
+++ b/react_on_rails/lib/generators/react_on_rails/install_generator.rb
@@ -250,6 +250,7 @@
             :yellow
         set_javascript_transpiler_to_babel
         add_packages(["babel-loader"], dev: true)
+        add_babel_react_dependencies
         install_js_dependencies
       end
 
@@ -675,12 +676,7 @@
       end
 
       def detect_package_manager_from_lockfiles
-        return "yarn" if File.exist?("yarn.lock")
-        return "pnpm" if File.exist?("pnpm-lock.yaml")
-        return "bun" if File.exist?("bun.lock") || File.exist?("bun.lockb")
-        return "npm" if File.exist?("package-lock.json")
-
-        nil
+        GeneratorMessages.detect_package_manager_from_lockfiles
       end
 
       def detect_package_manager_version(package_manager)

diff --git a/react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb b/react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb
--- a/react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb
+++ b/react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb
@@ -516,18 +516,17 @@
 
       def fallback_package_manager
         package_manager = GeneratorMessages.detect_package_manager
-        allowed_package_managers = %w[npm yarn pnpm bun].freeze
-        return package_manager if allowed_package_managers.include?(package_manager)
+        return package_manager if GeneratorMessages.supported_package_manager?(package_manager)
 
         "npm"
       end
 
       def build_install_args(package_manager, dev, packages)
         base_commands = {
-          "npm" => %w[npm install],
-          "yarn" => %w[yarn add],
-          "pnpm" => %w[pnpm add],
-          "bun" => %w[bun add]
+          "npm" => %w[npm install --save-exact],
+          "yarn" => %w[yarn add --exact],
+          "pnpm" => %w[pnpm add --save-exact],
+          "bun" => %w[bun add --exact]
         }
 
         base_args = base_commands.fetch(package_manager).dup

diff --git a/react_on_rails/spec/react_on_rails/generators/generator_messages_spec.rb b/react_on_rails/spec/react_on_rails/generators/generator_messages_spec.rb
--- a/react_on_rails/spec/react_on_rails/generators/generator_messages_spec.rb
+++ b/react_on_rails/spec/react_on_rails/generators/generator_messages_spec.rb
@@ -96,6 +96,10 @@
   end
 
   describe ".detect_package_manager" do
+    before do
+      ENV.delete("REACT_ON_RAILS_PACKAGE_MANAGER")
+    end
+
     it "returns bun when bun.lock exists" do
       allow(File).to receive(:exist?).and_call_original
       allow(File).to receive(:exist?).with("yarn.lock").and_return(false)
@@ -114,5 +118,16 @@
 
       expect(described_class.detect_package_manager).to eq("bun")
     end
+
+    it "returns npm when package-lock.json exists" do
+      allow(File).to receive(:exist?).and_call_original
+      allow(File).to receive(:exist?).with("yarn.lock").and_return(false)
+      allow(File).to receive(:exist?).with("pnpm-lock.yaml").and_return(false)
+      allow(File).to receive(:exist?).with("bun.lock").and_return(false)
+      allow(File).to receive(:exist?).with("bun.lockb").and_return(false)
+      allow(File).to receive(:exist?).with("package-lock.json").and_return(true)
+
+      expect(described_class.detect_package_manager).to eq("npm")
+    end
   end
 end

diff --git a/react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb b/react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb
--- a/react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb
+++ b/react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb
@@ -3109,10 +3109,11 @@
           return <div>Hello</div>
         }
       JS
-      allow(install_generator).to receive_messages(using_swc?: true, add_packages: true, install_js_dependencies: true)
+      allow(install_generator).to receive_messages(using_swc?: true, add_packages: true, add_babel_react_dependencies: true,
+                                                   install_js_dependencies: true)
     end
 
-    it "switches to babel and installs babel-loader when JSX is found in .js files" do
+    it "switches to babel and installs babel dependencies when JSX is found in .js files" do
       Dir.chdir(destination_root) do
         install_generator.send(:ensure_jsx_in_js_compatibility)
       end
@@ -3120,6 +3121,7 @@
       shakapacker_yml = File.read(File.join(destination_root, "config/shakapacker.yml"))
       expect(shakapacker_yml).to include('javascript_transpiler: "babel"')
       expect(install_generator).to have_received(:add_packages).with(["babel-loader"], dev: true)
+      expect(install_generator).to have_received(:add_babel_react_dependencies)
       expect(install_generator).to have_received(:install_js_dependencies)
     end
   end

diff --git a/react_on_rails/spec/react_on_rails/generators/js_dependency_manager_spec.rb b/react_on_rails/spec/react_on_rails/generators/js_dependency_manager_spec.rb
--- a/react_on_rails/spec/react_on_rails/generators/js_dependency_manager_spec.rb
+++ b/react_on_rails/spec/react_on_rails/generators/js_dependency_manager_spec.rb
@@ -196,7 +196,7 @@
       instance.add_npm_dependencies_result = false
       result = instance.send(:add_packages, %w[package1])
       expect(result).to be(true)
-      expect(instance.system_calls).to include(%w[npm install package1])
+      expect(instance.system_calls).to include(%w[npm install --save-exact package1])
     end
 
     it "returns false when add_npm_dependencies and fallback both fail" do
@@ -238,7 +238,7 @@
       instance.add_npm_dependencies_result = false
       result = instance.send(:add_package, "some-package")
       expect(result).to be(true)
-      expect(instance.system_calls).to include(%w[npm install some-package])
+      expect(instance.system_calls).to include(%w[npm install --save-exact some-package])
     end
 
     it "returns false when fallback install fails" do

You can send follow-ups to this agent here.

Comment thread react_on_rails/lib/generators/react_on_rails/install_generator.rb
Comment thread react_on_rails/lib/generators/react_on_rails/install_generator.rb
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Mar 31, 2026

Greptile Summary

This PR hardens the react_on_rails install/migration generator for legacy Shakapacker setups where the package_json gem may not be available, adds package-manager-aware fallback install paths, suppresses duplicate "gem unavailable" warnings, auto-detects the package manager from lockfiles and seeds it into package.json before shakapacker:install, resolves browserslist conflicts, and auto-switches javascript_transpiler to babel when JSX-in-.js files are detected.

Key changes:

  • install_generator.rb: adds seed_package_manager_in_package_json_from_lockfile!, resolve_browserslist_conflict_after_shakapacker_install, and ensure_jsx_in_js_compatibility.
  • js_dependency_manager.rb: refactors add_package/add_packages to try add_npm_dependencies first and fall back to direct system(pm, ...) calls; adds filter_missing_packages to avoid re-installing already-declared dependencies.
  • generator_helper.rb: deduplicates "package_json gem not available" warnings with a once-per-instance flag.
  • base_generator.rb: refactors the configure_rspec_to_compile_assets injection to insert the call body inside the existing RSpec.configure do |config| block, verified with a Ripper syntax check.

Two P2 concerns:

  • ensure_jsx_in_js_compatibility adds only babel-loader; it does not add @babel/preset-react, which Babel needs to process JSX.
  • using_swc? is memoised as @using_swc before the transpiler switch, leaving unused SWC packages in package.json and a stale cache value for the remainder of the generator run.

Confidence Score: 5/5

Safe to merge; all remaining findings are P2 improvement suggestions that do not block the primary migration use case.

The PR solves a real, verified problem. Fallback logic is well-guarded, warning deduplication is correct, and the Ripper-validated RSpec injection fix is a clear improvement. Both P2 findings are non-breaking in the primary migration scenario.

install_generator.rb (ensure_jsx_in_js_compatibility) and js_dependency_manager.rb (dev_flag_for) warrant a second look for the P2 improvements.

Important Files Changed

Filename Overview
react_on_rails/lib/generators/react_on_rails/install_generator.rb Adds seed_package_manager_in_package_json_from_lockfile!, resolve_browserslist_conflict_after_shakapacker_install, and ensure_jsx_in_js_compatibility; the JSX-detection path adds babel-loader but not @babel/preset-react and leaves a stale @using_swc cache.
react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb Adds package-manager-aware fallback install path; dev_flag_for lacks an else guard (safe today but fragile).
react_on_rails/lib/generators/react_on_rails/generator_helper.rb Deduplicates 'package_json gem not available' warnings with a once-per-instance flag; logic is correct and minimal.
react_on_rails/lib/generators/react_on_rails/base_generator.rb Refactors configure_rspec_to_compile_assets injection to insert inside existing RSpec.configure block with proper indentation; Ripper-validated.
react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb Adds targeted specs for the three new methods; good coverage of happy path and guard conditions.
react_on_rails/spec/react_on_rails/generators/js_dependency_manager_spec.rb Updates tests to use new add_npm_dependencies mock; adds fallback-path coverage including skip-if-already-installed scenario.
react_on_rails/spec/react_on_rails/generators/base_generator_spec.rb Adds Ripper.sexp validity check to the rspec-compile-assets injection test.
docs/oss/migrating/migrating-from-react-rails.md Adds three new preflight checklist items and a bash snippet for seeding packageManager; clear and accurate.

Sequence Diagram

sequenceDiagram
    participant IG as InstallGenerator
    participant SPI as seed_package_manager!
    participant SHK as shakapacker:install
    participant BLC as resolve_browserslist
    participant SRD as setup_react_dependencies
    participant EJX as ensure_jsx_in_js_compat
    participant JDM as JsDependencyManager
    participant PM as Package Manager CLI
    participant PJG as package_json gem

    IG->>SPI: seed_package_manager_in_package_json_from_lockfile!
    SPI->>SPI: detect PM from lockfiles
    SPI->>PM: pm --version (Open3.capture3)
    PM-->>SPI: version string
    SPI->>SPI: write packageManager field to package.json
    IG->>SHK: bundle exec rails shakapacker:install
    SHK-->>IG: success
    IG->>BLC: resolve_browserslist_conflict_after_shakapacker_install
    BLC->>BLC: if .browserslistrc AND package.json[browserslist] exists, remove key
    IG->>SRD: setup_react_dependencies
    SRD->>JDM: add_transpiler_dependencies (using_swc? cached true)
    JDM->>PJG: add @swc/core swc-loader
    PJG-->>JDM: success OR LoadError
    JDM->>PM: fallback npm/yarn add if package_json unavailable
    IG->>EJX: ensure_jsx_in_js_compatibility
    EJX->>EJX: jsx_in_js_files_present?
    EJX->>EJX: set_javascript_transpiler_to_babel
    EJX->>JDM: add_packages babel-loader dev true
    JDM->>PJG: add_npm_dependencies OR fallback PM CLI
    EJX->>JDM: install_js_dependencies
Loading

Reviews (1): Last reviewed commit: "Harden migration generator for legacy sh..." | Re-trigger Greptile

Comment on lines +244 to +254
def ensure_jsx_in_js_compatibility
return if options[:pretend]
return unless using_swc?
return unless jsx_in_js_files_present?

say "⚙️ Detected JSX in .js files; switching shakapacker javascript_transpiler to babel for compatibility",
:yellow
set_javascript_transpiler_to_babel
add_packages(["babel-loader"], dev: true)
install_js_dependencies
end
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 @babel/preset-react not added when switching from SWC to Babel

ensure_jsx_in_js_compatibility switches the transpiler to Babel and adds babel-loader, but does not add @babel/preset-react (the BABEL_REACT_DEPENDENCIES constant). babel-loader is only the Webpack loader that invokes Babel; processing JSX also requires @babel/preset-react (or an equivalent preset) in the Babel config.

In the primary migration scenario (from react-rails) this is typically safe — the user already has @babel/preset-react in package.json from their existing Babel setup. However, an app that arrived at SWC + JSX-in-.js via another path (e.g. upgrading Shakapacker to 9.3+ on a project that already had .js JSX files) would get a broken transpiler setup after this generator runs.

Consider calling add_babel_react_dependencies alongside the babel-loader install so the method is self-contained:

def ensure_jsx_in_js_compatibility
  return if options[:pretend]
  return unless using_swc?
  return unless jsx_in_js_files_present?

  say "⚙️  Detected JSX in .js files; switching shakapacker javascript_transpiler to babel for compatibility",
      :yellow
  set_javascript_transpiler_to_babel
  add_packages(["babel-loader"], dev: true)
  add_babel_react_dependencies   # also adds @babel/preset-react
  install_js_dependencies
end

Comment on lines +244 to +254
def ensure_jsx_in_js_compatibility
return if options[:pretend]
return unless using_swc?
return unless jsx_in_js_files_present?

say "⚙️ Detected JSX in .js files; switching shakapacker javascript_transpiler to babel for compatibility",
:yellow
set_javascript_transpiler_to_babel
add_packages(["babel-loader"], dev: true)
install_js_dependencies
end
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 Stale @using_swc cache leaves unused SWC packages after Babel switch

using_swc? in GeneratorHelper is memoised into @using_swc. By the time ensure_jsx_in_js_compatibility is reached, setup_react_dependencies → setup_js_dependencies → add_transpiler_dependencies has already been called, which:

  1. Evaluated using_swc?true (memoised)
  2. Added @swc/core and swc-loader to package.json
  3. Skipped add_babel_react_dependencies

When ensure_jsx_in_js_compatibility subsequently changes shakapacker.yml to babel, @using_swc is still true. The SWC packages already added to package.json remain there, unused by Babel. Downstream code in the same generator instance that calls using_swc? would also get the stale value.

The fix is to invalidate the cache after the transpiler switch:

def set_javascript_transpiler_to_babel
  shakapacker_config_path = "config/shakapacker.yml"
  return unless File.exist?(shakapacker_config_path)

  gsub_file(
    shakapacker_config_path,
    /^(\s*javascript_transpiler:\s*)["']?swc["']?(\s*(?:#.*)?)$/,
    '\1"babel"\2'
  )

  # Invalidate the cached value so subsequent using_swc? calls reflect the change
  @using_swc = false
end

Comment on lines +538 to +542
def dev_flag_for(package_manager)
case package_manager
when "npm", "pnpm" then "--save-dev"
when "yarn", "bun" then "--dev"
end
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 dev_flag_for missing else branch — returns nil silently

The case statement has no else clause, so Ruby returns nil for any package manager not explicitly listed. If that happens, build_install_args appends nil to the args array (e.g., ["npm", "install", nil]), which would cause Kernel#system to misbehave.

fallback_package_manager already constrains the value to the four known managers, so this cannot fire today. Still, an explicit guard prevents silent breakage if the allowed list ever changes:

Suggested change
def dev_flag_for(package_manager)
case package_manager
when "npm", "pnpm" then "--save-dev"
when "yarn", "bun" then "--dev"
end
def dev_flag_for(package_manager)
case package_manager
when "npm", "pnpm" then "--save-dev"
when "yarn", "bun" then "--dev"
else
raise ArgumentError, "Unknown package manager for dev flag: #{package_manager}"
end
end

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: 3

🧹 Nitpick comments (1)
react_on_rails/lib/generators/react_on_rails/install_generator.rb (1)

802-809: Consider relaxing the JSX fragment regex anchor.

The fragment pattern /<\s*>\s*$/ only matches <> at the end of a line. Fragments like <>content</> or <> followed by content on the same line won't be detected.

💡 Suggested fix to detect mid-line fragments
 def jsx_in_js_files_present?
   Dir.glob("app/javascript/**/*.js").any? do |path|
     content = File.read(path)
-    content.match?(%r{<\s*[A-Za-z][\w:-]*(\s|>|/)}) || content.match?(/<\s*>\s*$/)
+    content.match?(%r{<\s*[A-Za-z][\w:-]*(\s|>|/)}) || content.match?(/<\s*>/)
   rescue StandardError
     false
   end
 end
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@react_on_rails/lib/generators/react_on_rails/install_generator.rb` around
lines 802 - 809, The regex that checks for JSX fragments in
jsx_in_js_files_present? is too strict (/<\s*>\s*$/) because it only matches an
opening fragment at line end; update that check to look for an opening fragment
anywhere in the file (for example use an unanchored pattern like /<\s*>/ or a
slightly more permissive /<\s*>/ that matches mid-line fragments such as
<>content</>) so the method detects fragments like <>content</> or <> followed
by inline content; modify the pattern used in the content.match? call inside
jsx_in_js_files_present? accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/oss/migrating/migrating-from-react-rails.md`:
- Around line 12-14: Update the example for setting the packageManager field so
it is manager-agnostic: when referencing package.json and the packageManager
field alongside lockfiles (yarn.lock, pnpm-lock.yaml, bun.lock*), replace the
hardcoded example `[email protected]` with either a set of manager-specific examples
(e.g., npm@<version>, yarn@<version>, pnpm@<version>) or a clear instruction to
substitute the actual tool and exact version used by the project; ensure the
text explicitly lists npm, yarn, and pnpm as examples and shows the format
consumers should use for their chosen manager/version.

In `@react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb`:
- Around line 545-552: The current filter_missing_packages method incorrectly
treats versioned specs like "[email protected]" or "react@~19.0.4" as
satisfied if the package name exists; update filter_missing_packages to detect
when package_spec includes a version/range (use package_name_from_spec to get
the name and check package_spec for a version token or regex) and, in that case,
compare the spec's version/range against the installed version (look up
installed versions via existing_package_names/extend or an
existing_package_versions helper); only reject the spec if the installed version
satisfies the spec range, otherwise keep it so the installer will add/upgrade
the package.
- Around line 467-470: The add_npm_dependencies method currently swallows
pj.manager.add(...) results and returns true unconditionally, preventing
add_packages from falling back to install_packages_with_fallback; update
add_npm_dependencies to capture the boolean result of pj.manager.add(packages,
...) and return that value (so add_packages sees falsy and calls
install_packages_with_fallback), ensuring symbols to change are
add_npm_dependencies and the pj.manager.add(...) call used by
add_packages/install_packages_with_fallback.

---

Nitpick comments:
In `@react_on_rails/lib/generators/react_on_rails/install_generator.rb`:
- Around line 802-809: The regex that checks for JSX fragments in
jsx_in_js_files_present? is too strict (/<\s*>\s*$/) because it only matches an
opening fragment at line end; update that check to look for an opening fragment
anywhere in the file (for example use an unanchored pattern like /<\s*>/ or a
slightly more permissive /<\s*>/ that matches mid-line fragments such as
<>content</>) so the method detects fragments like <>content</> or <> followed
by inline content; modify the pattern used in the content.match? call inside
jsx_in_js_files_present? accordingly.
🪄 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: 520f6044-a31f-453b-aff3-e0b8d220dbd2

📥 Commits

Reviewing files that changed from the base of the PR and between c5288af and 4bfa1fa.

📒 Files selected for processing (8)
  • docs/oss/migrating/migrating-from-react-rails.md
  • react_on_rails/lib/generators/react_on_rails/base_generator.rb
  • react_on_rails/lib/generators/react_on_rails/generator_helper.rb
  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
  • react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb
  • react_on_rails/spec/react_on_rails/generators/base_generator_spec.rb
  • react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb
  • react_on_rails/spec/react_on_rails/generators/js_dependency_manager_spec.rb

Comment thread docs/oss/migrating/migrating-from-react-rails.md Outdated
Comment on lines +545 to +552
def filter_missing_packages(packages)
existing = existing_package_names
return packages if existing.empty?

packages.reject do |package_spec|
package_name = package_name_from_spec(package_spec)
package_name && existing.include?(package_name)
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.

⚠️ Potential issue | 🟠 Major

Don't treat versioned specs as already satisfied by name alone.

This filter drops the version/range before checking package.json, so react-on-rails@..., Pro package pins, and the RSC react@~19.0.4 pins are all skipped as soon as the bare package name exists. On legacy apps the fallback path can therefore return success while leaving an incompatible version in place.

♻️ Possible fix
 def filter_missing_packages(packages)
   existing = existing_package_names
   return packages if existing.empty?

   packages.reject do |package_spec|
     package_name = package_name_from_spec(package_spec)
-    package_name && existing.include?(package_name)
+    package_name == package_spec && existing.include?(package_name)
   end
 end

Please add a regression around a versioned spec such as react-on-rails@... or react@~19.0.4 so this does not silently regress.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb` around
lines 545 - 552, The current filter_missing_packages method incorrectly treats
versioned specs like "[email protected]" or "react@~19.0.4" as satisfied if
the package name exists; update filter_missing_packages to detect when
package_spec includes a version/range (use package_name_from_spec to get the
name and check package_spec for a version token or regex) and, in that case,
compare the spec's version/range against the installed version (look up
installed versions via existing_package_names/extend or an
existing_package_versions helper); only reject the spec if the installed version
satisfies the spec range, otherwise keep it so the installer will add/upgrade
the package.

@coderabbitai coderabbitai Bot added the codex PRs created from codex-named branches label Mar 31, 2026
else
pj.manager.add(packages, exact: true)
end
!result.nil?
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.

Fragile return-value check may suppress the fallback path

pj.manager.add(...) returns the parsed package.json Hash on success (always truthy/non-nil for a real write). But the check !result.nil? would return true even if the gem returned false for some reason—hiding a real failure and skipping the fallback install.

The previous code simply fell through to true after a successful call. A clearer approach:

Suggested change
!result.nil?
!result.nil? && result != false

Or, since the gem either succeeds (returns something non-nil/non-false) or raises, the original true pattern was safer. At minimum the intent should be result (truthy check) rather than !result.nil?.

def jsx_in_js_files_present?
Dir.glob("app/javascript/**/*.js").any? do |path|
content = File.read(path)
content.match?(%r{<\s*[A-Za-z][\w:-]*(\s|>|/)}) || content.match?(/<\s*>/)
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.

JSX detection regex has significant false-positive risk

The pattern %r{<\s*[A-Za-z][\w:-]*(\s|>|/)} will match things that aren't JSX:

  • TypeScript-style generic annotations in .js: new Array<Item>() matches as <Item>
  • Comparison operators: if (x < ErrorHandler ...) — the space after the identifier satisfies \s
  • HTML in string literals or comments: // returns <div>
  • Multi-word <= won't match, but < SomeVar will

A false positive silently switches the entire project from SWC to Babel—a meaningful, hard-to-reverse build change. Consider:

  1. Narrowing the heuristic by also requiring the match to be outside comments/strings (hard in regex alone).
  2. Requiring the pattern to appear at the beginning of an expression (e.g., preceded by return, (, =, ,, or newline) to reduce noise.
  3. Asking for user confirmation before switching transpilers, since this is a consequential irreversible change.

At minimum the current heuristic should be documented so future maintainers understand why false positives can occur.

return unless version

package_json_content["packageManager"] = "#{manager}@#{version}"
File.write("package.json", "#{JSON.pretty_generate(package_json_content)}\n")
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.

JSON.pretty_generate silently reformats the entire package.json

JSON.pretty_generate always uses 2-space indentation. Many projects (particularly older ones being migrated) use 4-space indent, tabs, or have formatter-enforced styles. This will silently reformat package.json everywhere—creating a large noisy diff of unrelated whitespace changes in the user's repo.

The same issue occurs at line 676 in resolve_browserslist_conflict_after_shakapacker_install.

Consider detecting and preserving the original indentation before re-serialising:

original = File.read("package.json")
indent_str = original.match(/\n(\s+)/)&.[](1) || "  "
File.write("package.json", "#{JSON.generate(package_json_content, indent: indent_str)}\n")

Or use a narrowly-targeted string substitution that only adds/removes the one key rather than re-serialising the whole file.


install_args = build_install_args(package_manager, dev, packages_to_install)

system(*install_args)
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.

system() can return nil (not false) when the command isn't found

Kernel#system returns nil when the command cannot be executed (e.g., the package manager binary isn't on PATH). nil is falsy, so callers treat it as failure—which is correct—but no warning is generated for this case since rescue StandardError won't catch it.

Consider normalising the return value and emitting a warning:

Suggested change
system(*install_args)
result = system(*install_args)
if result.nil?
GeneratorMessages.add_warning("⚠️ Fallback package install failed: command not found — #{install_args.first}")
return false
end
result

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 31, 2026

Overall this is a well-motivated set of changes with good test coverage. The fallback path for missing package_json gem, deduplication of warning noise, and the browserslist/packageManager seeding all address real pain points in legacy migrations.

Issues found (inline comments posted):

  1. Fragile return value in add_npm_dependencies (generator_helper.rb:37)pj.manager.add() returns the updated package.json hash on success (always truthy), so !result.nil? works today. But if the gem ever returns false, the method signals success, the fallback is skipped, and the package silently goes uninstalled. A plain truthiness check (!!result) is clearer and safer.

  2. JSON.pretty_generate reformats entire package.json (install_generator.rb:661, 676) — Both seed_package_manager_in_package_json_from_lockfile! and resolve_browserslist_conflict_after_shakapacker_install parse and re-emit the whole file with 2-space indentation, discarding whatever style the project used. On a 4-space or tab-indented project this creates a large noisy diff of unrelated formatting changes.

  3. JSX detection regex has meaningful false-positive risk (install_generator.rb:807) — The pattern matches TypeScript-style generics (new Array<Item>()), comparison operators, and HTML in string literals or comments. A false positive silently and permanently switches the project from SWC to Babel, a consequential build change. Either narrow the heuristic or ask for user confirmation before switching.

  4. system() nil return unhandled in install_packages_with_fallback (js_dependency_manager.rb:514)Kernel#system returns nil (not false) when the binary is not found. No warning is emitted in that case because rescue StandardError does not catch it.

Minor observations:

  • SWC deps installed unnecessarily: setup_react_dependencies runs before ensure_jsx_in_js_compatibility, so SWC packages are added to package.json before being rendered unused by the Babel switch.
  • Dir.glob + File.read on all JS files: File.foreach for line-by-line scanning would be faster for large files.
  • Hardcoded version numbers in docs ([email protected], [email protected], etc.) will become stale quickly; phrasing like "replace with your installed version" would age better.

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: !result.nil? returns true for false results
    • Changed add_npm_dependencies to return !!result so a false manager.add result correctly propagates and fallback logic can run.

Create PR

Or push these changes by commenting:

@cursor push b7a7538dc5
Preview (b7a7538dc5)
diff --git a/react_on_rails/lib/generators/react_on_rails/generator_helper.rb b/react_on_rails/lib/generators/react_on_rails/generator_helper.rb
--- a/react_on_rails/lib/generators/react_on_rails/generator_helper.rb
+++ b/react_on_rails/lib/generators/react_on_rails/generator_helper.rb
@@ -34,7 +34,7 @@
                else
                  pj.manager.add(packages, exact: true)
                end
-      !result.nil?
+      !!result
     rescue StandardError => e
       say_status :warning, "Could not add packages via package_json gem: #{e.message}", :yellow
       say_status :warning, "Will fall back to direct npm commands.", :yellow

You can send follow-ups to this agent here.

Comment thread react_on_rails/lib/generators/react_on_rails/generator_helper.rb Outdated
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: 754923e88f

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

else
pj.manager.add(packages, exact: true)
end
!result.nil?
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Return false when package_json add reports failure

add_npm_dependencies now returns !result.nil?, which treats false as success. In the failure mode where pj.manager.add returns false, callers will skip fallback installation and continue as if dependencies were added, leaving required packages missing for subsequent build/generator steps. This should return the real success boolean (not non-nil) so fallback logic is actually triggered.

Useful? React with 👍 / 👎.

Comment on lines +521 to +522
package_manager = GeneratorMessages.detect_package_manager
return package_manager if GeneratorMessages.supported_package_manager?(package_manager)
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 Verify detected fallback manager is installed

The fallback path chooses a package manager purely from lockfiles/env and only checks whether the name is supported, not whether the executable exists. In legacy installs (where package_json is unavailable), a stale lockfile can select yarn/pnpm even when only npm is installed, causing all fallback system(...) installs to fail even though a working manager is present. Add a CLI availability check and fall back to an installed manager.

Useful? React with 👍 / 👎.

def jsx_in_js_files_present?
Dir.glob("app/javascript/**/*.js").any? do |path|
content = File.read(path)
content.match?(%r{<\s*[A-Za-z][\w:-]*(\s|>|/)}) || content.match?(/<\s*>/)
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 Narrow JSX detection to actual JSX syntax

This pattern also matches normal comparison code such as a < b && ..., because it accepts < followed by an identifier and whitespace. That can incorrectly trigger the SWC→Babel switch in projects that do not use JSX in .js files, unexpectedly changing transpiler behavior and dependency installation. The detector should only match JSX-specific tokens like opening/closing tags or fragments.

Useful? React with 👍 / 👎.

@justin808
Copy link
Copy Markdown
Member Author

Addressed the latest review feedback in 754923e:

  • fixed the failing install_generator_spec expectations for the RSpec helper insertion shape
  • made migration docs package-manager guidance manager-agnostic (npm/yarn/pnpm/bun examples)
  • broadened JSX fragment detection (<>) to detect inline fragments, not just line-end
  • when switching SWC -> Babel for JSX-in-.js, now install babel-loader plus Babel React preset deps and warn if any dependency step fails
  • invalidated cached using_swc? state after switching transpiler
  • unified lockfile package-manager detection through GeneratorMessages
  • hardened fallback dependency installs: exact pin flags, warning on fallback exceptions, explicit unknown manager guard
  • adjusted fallback filtering so versioned specs are not skipped just because the package name already exists
  • added/updated generator specs to cover the new behavior

CI is running on this commit now.

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: 4

🤖 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/generators/react_on_rails/install_generator.rb`:
- Around line 813-823: The method set_javascript_transpiler_to_babel currently
always sets `@using_swc` = false even when gsub_file made no change (e.g., SWC was
only the implicit Shakapacker default); change it to first read
shakapacker_config_path and test the same regex used for gsub_file to see if an
explicit javascript_transpiler: swc line exists, only call gsub_file and set
`@using_swc` = false when that match is found. Reference symbols:
set_javascript_transpiler_to_babel, shakapacker_config_path, gsub_file,
`@using_swc`, and detect_swc_configuration.
- Around line 252-255: Remove the redundant full install by deleting the call to
install_js_dependencies (and its assignment to dependencies_installed); leave
the targeted installs (babel_loader_added and babel_preset_added from
add_packages/add_babel_react_dependencies) and keep the existing return if those
targeted installs succeeded — i.e., eliminate the line that calls
install_js_dependencies so only the specific package installs are performed.

In `@react_on_rails/spec/react_on_rails/generators/generator_helper_spec.rb`:
- Around line 101-109: The method add_npm_dependencies currently returns
!result.nil? which turns a false result from manager.add into true; change the
return to propagate the actual boolean from manager.add (e.g., return result
directly or coerce with !!result) so that when mock_manager.add(packages, exact:
true) returns false, add_npm_dependencies also returns false and allows the
fallback installation to run.

In `@react_on_rails/spec/react_on_rails/generators/generator_messages_spec.rb`:
- Around line 99-101: The before hook deletes REACT_ON_RAILS_PACKAGE_MANAGER
which mutates global ENV; capture the original value (e.g. orig =
ENV["REACT_ON_RAILS_PACKAGE_MANAGER"]) before changing it in the before block
and add an after hook that restores it (set
ENV["REACT_ON_RAILS_PACKAGE_MANAGER"] = orig if orig; otherwise
ENV.delete("REACT_ON_RAILS_PACKAGE_MANAGER")). Update the spec in
generator_messages_spec.rb where the before hook runs so the env is always
restored after the example group.
🪄 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: e68db6f9-cd9c-4b45-9512-89146c41f6ca

📥 Commits

Reviewing files that changed from the base of the PR and between 4bfa1fa and 754923e.

📒 Files selected for processing (9)
  • docs/oss/migrating/migrating-from-react-rails.md
  • react_on_rails/lib/generators/react_on_rails/generator_helper.rb
  • react_on_rails/lib/generators/react_on_rails/generator_messages.rb
  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
  • react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb
  • react_on_rails/spec/react_on_rails/generators/generator_helper_spec.rb
  • react_on_rails/spec/react_on_rails/generators/generator_messages_spec.rb
  • react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb
  • react_on_rails/spec/react_on_rails/generators/js_dependency_manager_spec.rb
✅ Files skipped from review due to trivial changes (1)
  • react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb
🚧 Files skipped from review as they are similar to previous changes (4)
  • react_on_rails/lib/generators/react_on_rails/generator_helper.rb
  • docs/oss/migrating/migrating-from-react-rails.md
  • react_on_rails/spec/react_on_rails/generators/js_dependency_manager_spec.rb
  • react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb

Comment thread react_on_rails/lib/generators/react_on_rails/install_generator.rb Outdated
Comment thread react_on_rails/lib/generators/react_on_rails/install_generator.rb
Comment thread react_on_rails/spec/react_on_rails/generators/generator_messages_spec.rb Outdated

install_args = build_install_args(package_manager, dev, packages_to_install)

system(*install_args)
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.

Bug: system() can return nil, which silently propagates as a falsy non-false value.

Kernel#system returns true, false, or nil (when the command cannot be executed — e.g. the binary is missing or a permissions error). Returning nil here means no warning is shown to the user about what went wrong, unlike the false case which shows a clear message in install_js_dependencies.

Suggested change
system(*install_args)
result = system(*install_args)
return result == true

Or explicitly handle nil:

case system(*install_args)
when true then true
when false
  GeneratorMessages.add_warning("⚠️  Fallback install command failed (non-zero exit).")
  false
else # nil
  GeneratorMessages.add_warning("⚠️  Could not execute #{install_args.first} — is it on PATH?")
  false
end

return unless version

package_json_content["packageManager"] = "#{manager}@#{version}"
File.write("package.json", "#{JSON.pretty_generate(package_json_content)}\n")
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.

JSON.pretty_generate will reformat the entire package.json, creating noisy git diffs.

JSON.pretty_generate normalizes indentation (2 spaces) and key ordering, so even a file that previously used 4-space indentation, tabs, or different key order will be rewritten in full. This will produce a large diff in the user's working directory that has nothing to do with adding packageManager.

Consider a targeted string injection (e.g. inserting a line after "private": true) or using a JSON serializer that preserves original formatting. At a minimum, document the side effect in a comment so users aren't surprised by the reformatting:

# NOTE: JSON.pretty_generate reformats the entire file; this is intentional to
# ensure a canonical format before shakapacker:install.
File.write("package.json", "#{JSON.pretty_generate(package_json_content)}\n")

The same issue exists at line 675 in resolve_browserslist_conflict_after_shakapacker_install.

def jsx_in_js_files_present?
Dir.glob("app/javascript/**/*.js").any? do |path|
content = File.read(path)
content.match?(%r{<\s*[A-Za-z][\w:-]*(\s|>|/)}) || content.match?(/<\s*>/)
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.

JSX detection regex has false positive risk that could unnecessarily switch projects from SWC to Babel.

The pattern <\s*[A-Za-z][\w:-]*(\s|>|/) matches:

  • Regular HTML strings in template literals or JS strings: `<div class="foo">`
  • HTML comments in JS: // <Component example>
  • Comparison operators mixed with capitalized identifiers in some edge cases

A developer with SWC set up, .js files containing HTML in strings or comments, but no actual JSX syntax would have their transpiler silently downgraded. This could mask build issues rather than surfacing them.

Consider making this opt-in via a generator flag (e.g., --auto-detect-jsx) rather than running it unconditionally, or at minimum logging what file and what match triggered the switch so users can audit it:

Dir.glob("app/javascript/**/*.js").each do |path|
  content = File.read(path)
  if content.match?(%r{<\s*[A-Za-z][\w:-]*(\s|>|/)}) || content.match?(/<\s*>/)
    say "  → #{path}", :yellow  # show which file triggered the detection
    return true
  end
  false
rescue StandardError
  false
end

swc_transpiler_pattern,
'\1"babel"\2'
)
@using_swc = false
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.

Direct @using_swc assignment bypasses the memoization pattern used elsewhere.

@using_swc = false sets the raw ivar directly, but the caching/initialization convention (e.g. @using_swc.nil? ? ... : @using_swc) elsewhere means false and "not yet computed" are distinguishable. Directly assigning false here is intentional, but it tightly couples this method to the implementation details of using_swc?.

If using_swc? is ever refactored to use ||= or similar memoization, this manual reset could silently stop working. Consider using a dedicated reset method or a comment explaining the coupling:

# Reset the memoized SWC flag so subsequent using_swc? calls reflect the YAML change.
@using_swc = false

else
pj.manager.add(packages, exact: true)
end
result ? true : false
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.

result ? true : false is equivalent to !!result. Both are valid, but either !!result or just returning result (since the caller already coerces to boolean) would be more idiomatic Ruby.

Suggested change
result ? true : false
result ? true : false

!!result or simply return result if the callers all treat it as truthy/falsy.

detect_package_manager_from_lockfiles || "npm"
end

def detect_package_manager_from_lockfiles
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.

detect_package_manager_from_lockfiles is now a public class method but is called internally as an implementation detail of detect_package_manager.

Exposing it publicly means external callers could call it directly and get nil (when no lockfile exists) rather than the "npm" default that detect_package_manager provides. This nil return is silently consumed in seed_package_manager_in_package_json_from_lockfile! (which correctly returns early on nil), but a caller who forgets to handle nil could get unexpected behavior.

Consider either:

  1. Keeping it private and delegating from install_generator.rb through detect_package_manager (which already provides the nil-safe default), or
  2. Documenting the nil return contract clearly so callers know to guard against it.

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: Fallback install silently returns nil on system failure
    • Updated fallback package installation to emit a warning with the attempted command and always return explicit false on system failure instead of propagating nil/false silently.

Create PR

Or push these changes by commenting:

@cursor push 32e77c6b92
Preview (32e77c6b92)
diff --git a/react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb b/react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb
--- a/react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb
+++ b/react_on_rails/lib/generators/react_on_rails/js_dependency_manager.rb
@@ -511,7 +511,15 @@
 
         install_args = build_install_args(package_manager, dev, packages_to_install)
 
-        system(*install_args)
+        return true if system(*install_args)
+
+        GeneratorMessages.add_warning(<<~MSG.strip)
+          ⚠️  JavaScript package addition failed via #{package_manager}.
+
+          Please run manually:
+            #{install_args.join(' ')}
+        MSG
+        false
       rescue StandardError => e
         GeneratorMessages.add_warning("⚠️  Fallback package install failed: #{e.message}")
         false

diff --git a/react_on_rails/spec/react_on_rails/generators/js_dependency_manager_spec.rb b/react_on_rails/spec/react_on_rails/generators/js_dependency_manager_spec.rb
--- a/react_on_rails/spec/react_on_rails/generators/js_dependency_manager_spec.rb
+++ b/react_on_rails/spec/react_on_rails/generators/js_dependency_manager_spec.rb
@@ -204,6 +204,9 @@
       instance.system_result = false
       result = instance.send(:add_packages, %w[package1])
       expect(result).to be(false)
+      expect(warnings.size).to be > 0
+      expect(warnings.first.to_s).to include("JavaScript package addition failed via npm")
+      expect(warnings.first.to_s).to include("npm install --save-exact package1")
     end
 
     it "skips fallback install for packages already present in package.json" do

You can send follow-ups to this agent here.


install_args = build_install_args(package_manager, dev, packages_to_install)

system(*install_args)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Fallback install silently returns nil on system failure

Medium Severity

install_packages_with_fallback returns the raw result of system(*install_args), which can be nil (command not found) or false (non-zero exit) without emitting any warning. This contrasts with install_js_dependencies, which explicitly checks the system return, emits a warning on failure, and returns false. The nil return also violates the documented @return [Boolean] contract of add_packages. When the detected package manager isn't installed, the fallback silently fails with no user-facing message about which command was attempted.

Additional Locations (1)
Fix in Cursor Fix in Web

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 31, 2026

Review: Harden migration generator for legacy shakapacker setups. Overall this is a solid improvement - the fallback install path, deduplication of the package_json unavailable warning, and the indentation-safe RSpec helper injection are all good fixes. Test coverage was expanded meaningfully. A few issues to address before merging. BUGS: 1) system() returns nil silently in the fallback install path (js_dependency_manager.rb:514) - Kernel#system returns true, false, or nil. The nil case propagates out of install_packages_with_fallback with no user-visible warning. See inline comment. 2) JSON.pretty_generate rewrites the entire package.json (install_generator.rb:660 and 675) - both seed_package_manager_in_package_json_from_lockfile! and resolve_browserslist_conflict_after_shakapacker_install write back with JSON.pretty_generate, normalising all indentation and whitespace across the file and producing a noisy unrelated diff. Should be documented; ideally use a targeted string-level patch. DESIGN CONCERNS: 3) JSX detection regex can cause false positives silently downgrading SWC to Babel - the regex matches HTML in JS string literals, template literals, and comments. At minimum, log which file triggered the detection so users can audit it. 4) detect_package_manager_from_lockfiles is now public but returns nil when no lockfile is found, unlike detect_package_manager which falls back to npm. The nil contract is handled correctly today but is an easy footgun for future callers. MINOR: @using_swc = false in set_javascript_transpiler_to_babel directly assigns the ivar coupled to using_swc? caching convention - a comment would help. result ? true : false in generator_helper.rb:37 should be !!result. CONFIGURE_RSPEC_TO_COMPILE_ASSETS now names a fragment not a full block - mildly misleading.

Copy link
Copy Markdown
Member Author

Addressed the latest actionable CodeRabbit comments in commit 8850c376c:

  • fixed add_npm_dependencies return semantics to preserve false results
  • removed redundant full install call in JSX/SWC→Babel compatibility path
  • only flip @using_swc when an explicit javascript_transpiler: swc line is present and replaced
  • updated spec env handling to restore REACT_ON_RAILS_PACKAGE_MANAGER

Targeted specs and RuboCop pass locally; CI is running on the updated branch.

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.

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

249-253: ⚠️ Potential issue | 🔴 Critical

Handle implicit SWC defaults before announcing the Babel switch.

Line 817 still bails unless config/shakapacker.yml already contains an explicit javascript_transpiler: swc. For Shakapacker 9.3+ apps where using_swc? is true via the default instead of a YAML entry, Lines 249-253 will print the switch message and install Babel deps, but the config stays on SWC and JSX-in-.js continues to fail. Please inject an explicit Babel setting when the file relies on the implicit default, and add a regression example for that path.

💡 Suggested fix
 def ensure_jsx_in_js_compatibility
   return if options[:pretend]
   return unless using_swc?
   return unless jsx_in_js_files_present?

+  return unless set_javascript_transpiler_to_babel
+
   say "⚙️  Detected JSX in .js files; switching shakapacker javascript_transpiler to babel for compatibility",
       :yellow
-  set_javascript_transpiler_to_babel
   babel_loader_added = add_packages(["babel-loader"], dev: true)
   babel_preset_added = add_babel_react_dependencies
   return if babel_loader_added && babel_preset_added
@@
 def set_javascript_transpiler_to_babel
   shakapacker_config_path = "config/shakapacker.yml"
-  return unless File.exist?(shakapacker_config_path)
+  return false unless File.exist?(shakapacker_config_path)

   swc_transpiler_pattern = /^(\s*javascript_transpiler:\s*)["']?swc["']?(\s*(?:#.*)?)$/
-  return unless File.read(shakapacker_config_path).match?(swc_transpiler_pattern)
+  config = File.read(shakapacker_config_path)
+
+  if config.match?(swc_transpiler_pattern)
+    gsub_file(
+      shakapacker_config_path,
+      swc_transpiler_pattern,
+      '\1"babel"\2'
+    )
+  elsif config.match?(/^\s*default:.*$/)
+    inject_into_file(
+      shakapacker_config_path,
+      "  javascript_transpiler: \"babel\"\n",
+      after: /^\s*default:.*$/
+    )
+  else
+    return false
+  end

-  gsub_file(
-    shakapacker_config_path,
-    swc_transpiler_pattern,
-    '\1"babel"\2'
-  )
   `@using_swc` = false
+  true
 end

Expected result: ReactOnRails::Generators::InstallGenerator#detect_swc_configuration should show the Shakapacker 9.3+ fallback path, while the current spec coverage should still only exercise an explicit javascript_transpiler: "swc" fixture.

#!/bin/bash
set -euo pipefail

rg -nC4 'def using_swc\?|def detect_swc_configuration|def set_javascript_transpiler_to_babel' react_on_rails/lib/generators/react_on_rails

echo '---'

rg -nC4 'describe "#ensure_jsx_in_js_compatibility"|javascript_transpiler:\s*"swc"' react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb

Also applies to: 816-824

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@react_on_rails/lib/generators/react_on_rails/install_generator.rb` around
lines 249 - 253, When switching to Babel in the install flow, handle implicit
SWC defaults by detecting when using_swc? returns true but the YAML has no
explicit javascript_transpiler entry (see detect_swc_configuration and
using_swc?): in that case call set_javascript_transpiler_to_babel and also write
an explicit javascript_transpiler: "babel" into the shakapacker config so the
file no longer relies on the implicit default; update the install generator path
that prints the switch (where babel_loader_added/babel_preset_added are set) to
perform this write-before-announcing, and add a regression spec exercising the
Shakapacker 9.3+ implicit-default path (a new case under describe
"#ensure_jsx_in_js_compatibility") to assert the YAML now contains the explicit
"babel" setting while keeping the existing explicit-SWC fixture tests unchanged.
🧹 Nitpick comments (1)
react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb (1)

325-328: Keep these assertions scoped to the RSpec.configure block.

Both include(...) checks now pass even if the helper call is appended outside RSpec.configure do |config|, which is the malformed-injection case this area is trying to catch. A block-scoped matcher keeps the test flexible without dropping placement coverage.

🧪 Suggested matcher
- expect(contents).to include("ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)")
+ expect(contents).to match(
+   /RSpec\.configure do \|config\|.*^\s+ReactOnRails::TestHelper\.configure_rspec_to_compile_assets\(config\)/m
+ )

Also applies to: 366-369

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb`
around lines 325 - 328, The current assertion checks the entire
spec/rails_helper.rb for the helper call and can pass if the call is injected
outside the RSpec.configure block; update the test to extract the
RSpec.configure block from contents (e.g., using a regexp matching
/RSpec\.configure.*?end/m) and assert that the extracted block includes
"ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)"; apply the
same scoped-block change for the other assertion mentioned (the one around lines
366-369) so both checks verify placement inside the RSpec.configure block.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@react_on_rails/lib/generators/react_on_rails/install_generator.rb`:
- Around line 249-253: When switching to Babel in the install flow, handle
implicit SWC defaults by detecting when using_swc? returns true but the YAML has
no explicit javascript_transpiler entry (see detect_swc_configuration and
using_swc?): in that case call set_javascript_transpiler_to_babel and also write
an explicit javascript_transpiler: "babel" into the shakapacker config so the
file no longer relies on the implicit default; update the install generator path
that prints the switch (where babel_loader_added/babel_preset_added are set) to
perform this write-before-announcing, and add a regression spec exercising the
Shakapacker 9.3+ implicit-default path (a new case under describe
"#ensure_jsx_in_js_compatibility") to assert the YAML now contains the explicit
"babel" setting while keeping the existing explicit-SWC fixture tests unchanged.

---

Nitpick comments:
In `@react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb`:
- Around line 325-328: The current assertion checks the entire
spec/rails_helper.rb for the helper call and can pass if the call is injected
outside the RSpec.configure block; update the test to extract the
RSpec.configure block from contents (e.g., using a regexp matching
/RSpec\.configure.*?end/m) and assert that the extracted block includes
"ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)"; apply the
same scoped-block change for the other assertion mentioned (the one around lines
366-369) so both checks verify placement inside the RSpec.configure block.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7813486a-e6ba-47c5-bb3b-1eabd9670aa1

📥 Commits

Reviewing files that changed from the base of the PR and between 754923e and 8850c37.

📒 Files selected for processing (4)
  • react_on_rails/lib/generators/react_on_rails/generator_helper.rb
  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
  • react_on_rails/spec/react_on_rails/generators/generator_messages_spec.rb
  • react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb
🚧 Files skipped from review as they are similar to previous changes (1)
  • react_on_rails/spec/react_on_rails/generators/generator_messages_spec.rb

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

Labels

codex PRs created from codex-named branches full-ci review-needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant