From 0d4481f8e04d7668b7671fbb8d2cf3ea9fa4f878 Mon Sep 17 00:00:00 2001 From: ihabadham Date: Fri, 10 Apr 2026 05:48:25 +0200 Subject: [PATCH 1/5] chore: remove --rsc-pro install generator flag The --rsc-pro flag was redundant with --rsc (which already implies Pro) and was originally closed as an "unnecessary parallel mode" in #2821 before shipping unintentionally alongside the Pro upgrade automation in #2822. Fold the useful behaviors previously gated on --rsc-pro into --rsc: - Pro verification checklist message now fires on any --rsc install instead of only when use_rsc_pro_mode? was true. - Prerelease installation note (rsc_prerelease_note) now fires on any --rsc install with a prerelease gem version. - Exact version pin for the Pro gem now triggers on Gem::Version.new(ReactOnRails::VERSION).prerelease? instead of the flag. This is the real underlying reason: Bundler's ~> does not match prerelease versions, so stable --rsc installs continue to use ~> while prerelease cycles get an exact pin that actually installs. Side effect: plain --rsc on a prerelease gem version now installs correctly where it previously would have failed against ~> pinning. Removes: - class_option :rsc_pro - use_rsc_pro_mode? helper - the --rsc-pro branches in use_pro?, use_rsc?, pro_requirement_flag, recovery_install_command - all --rsc-pro test contexts in install_generator_spec and the RSC Pro mode helpers describe block in generator_helper_spec Does not touch pro_generator.rb or pro_generator_spec.rb; those cover the #2626 upgrade automation scope (independent of this flag), and the silent-failure bugs there are tracked separately in #3104. Co-Authored-By: Claude Opus 4.6 (1M context) --- CHANGELOG.md | 4 + .../react_on_rails/generator_helper.rb | 18 +- .../react_on_rails/install_generator.rb | 18 +- .../generators/react_on_rails/pro_setup.rb | 24 ++- .../generators/generator_helper_spec.rb | 23 +-- .../generators/install_generator_spec.rb | 182 +----------------- 6 files changed, 46 insertions(+), 223 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 470d279907..c49ffe40cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,10 @@ After a release, run `/update-changelog` in Claude Code to analyze commits, writ ### [Unreleased] +#### Removed + +- **[Pro]** **Removed the `--rsc-pro` install generator flag**: `--rsc` already implies Pro, so the separate mode was unnecessary. Behaviors previously gated on `--rsc-pro` (Pro verification checklist, prerelease install note, exact Pro gem pin on prereleases) now fire on `--rsc` installs. [PR 3105](https://github.com/shakacode/react_on_rails/pull/3105) by [ihabadham](https://github.com/ihabadham). + #### Changed - **[Pro]** **Pro generator now creates the Node Renderer at `renderer/node-renderer.js`**: The canonical location for the Node Renderer entry point is now a dedicated top-level `renderer/` directory instead of `client/`, making it straightforward to exclude from production Docker builds that strip JS sources after bundling. Docs and Pro `spec/dummy` now use the new path consistently. Existing apps are unaffected — the generator skips files that already exist (including a legacy `client/node-renderer.js`). Fixes [Issue 3073](https://github.com/shakacode/react_on_rails/issues/3073). [PR 3165](https://github.com/shakacode/react_on_rails/pull/3165) by [justin808](https://github.com/justin808). 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 index ea884d562b..0036d20d94 100644 --- a/react_on_rails/lib/generators/react_on_rails/generator_helper.rb +++ b/react_on_rails/lib/generators/react_on_rails/generator_helper.rb @@ -152,28 +152,20 @@ def mark_pro_gem_installed! @pro_gem_installed = true end - # Check if first-class RSC Pro mode should be enabled. - # Returns true when --rsc-pro is set, or when users explicitly pass both --rsc and --pro. - # - # @return [Boolean] true if RSC Pro mode semantics should be applied - def use_rsc_pro_mode? - options[:rsc_pro] || (options[:rsc] && options[:pro]) - end - # Check if Pro features should be enabled. - # Returns true if --pro, --rsc, or --rsc-pro is set (RSC implies Pro). + # Returns true if --pro or --rsc is set (RSC implies Pro). # # @return [Boolean] true if Pro setup should be included def use_pro? - options[:pro] || options[:rsc] || options[:rsc_pro] + options[:pro] || options[:rsc] end - # Check if RSC (React Server Components) should be enabled - # Returns true if --rsc or --rsc-pro is explicitly set + # Check if RSC (React Server Components) should be enabled. + # Returns true if --rsc is set. # # @return [Boolean] true if RSC setup should be included def use_rsc? - options[:rsc] || options[:rsc_pro] + options[:rsc] end # Determine if the project is using rspack as the bundler. 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 index fc2f502b4c..58251dc7d2 100644 --- a/react_on_rails/lib/generators/react_on_rails/install_generator.rb +++ b/react_on_rails/lib/generators/react_on_rails/install_generator.rb @@ -57,21 +57,13 @@ class InstallGenerator < Rails::Generators::Base class_option :pro, type: :boolean, default: false, - desc: "Install React on Rails Pro with Node Renderer. " \ - "Combined with --rsc, uses --rsc-pro mode. Default: false" + desc: "Install React on Rails Pro with Node Renderer. Default: false" # --rsc class_option :rsc, type: :boolean, default: false, - desc: "Install React Server Components support (includes Pro). " \ - "Combined with --pro, uses --rsc-pro mode. Default: false" - - # --rsc-pro - class_option :rsc_pro, - type: :boolean, - default: false, - desc: "Install first-class Pro RSC mode with matched Pro/RSC defaults. Default: false" + desc: "Install React Server Components support (includes Pro). Default: false" # Hidden option: allows tests (and advanced users) to signal that Shakapacker # was just installed, triggering force-overwrite of shakapacker.yml with RoR's template. @@ -644,7 +636,7 @@ def add_post_install_message ci_workflow_generated: @ci_workflow_generated == true, app_root: destination_root )) - GeneratorMessages.add_info(rsc_pro_verification_message) if use_rsc_pro_mode? + GeneratorMessages.add_info(rsc_pro_verification_message) if use_rsc? end def shakapacker_setup_incomplete? @@ -658,9 +650,7 @@ def recovery_install_command flags << "--typescript" if options.typescript? flags << "--rspack" if options.rspack? - if use_rsc_pro_mode? - flags << "--rsc-pro" - elsif options.rsc? + if options.rsc? flags << "--rsc" elsif options.pro? flags << "--pro" diff --git a/react_on_rails/lib/generators/react_on_rails/pro_setup.rb b/react_on_rails/lib/generators/react_on_rails/pro_setup.rb index 4242d8def0..5c61a11e59 100644 --- a/react_on_rails/lib/generators/react_on_rails/pro_setup.rb +++ b/react_on_rails/lib/generators/react_on_rails/pro_setup.rb @@ -61,7 +61,7 @@ def missing_pro_gem?(force: false) return false if attempt_pro_gem_auto_install context_line = pro_gem_requirement_context_line - prerelease_note = rsc_pro_prerelease_note + prerelease_note = rsc_prerelease_note GeneratorMessages.add_error(<<~MSG.strip) 🚫 Failed to auto-install #{PRO_GEM_NAME} gem. @@ -94,20 +94,17 @@ def pro_flag_specified_for_context? end def pro_requirement_flag - return "--rsc-pro" if use_rsc_pro_mode? return "--rsc" if options[:rsc] "--pro" end - def rsc_pro_prerelease_note - return "" unless use_rsc_pro_mode? - return "" unless Gem::Version.new(ReactOnRails::VERSION).prerelease? + def rsc_prerelease_note + return "" unless use_rsc? + return "" unless prerelease_ror_version? "Note: #{PRO_GEM_NAME} #{ReactOnRails::VERSION} may not be published yet. " \ "If you are testing from source, use a local Gemfile `path:` option." - rescue ArgumentError - "" end # Attempt to auto-install the Pro gem via bundle add. @@ -537,13 +534,20 @@ def pro_gem_auto_install_command end def pro_gem_version_requirement - # RSC Pro uses exact pinning so the Pro gem version always matches the - # paired RSC package version generated in the same run. - return ReactOnRails::VERSION if use_rsc_pro_mode? + # Prerelease gem versions need an exact pin: Bundler's pessimistic operator + # (~>) does not match prerelease versions, so a stable range would fail to + # install during prerelease cycles. + return ReactOnRails::VERSION if prerelease_ror_version? "~> #{recommended_pro_gem_version}" end + def prerelease_ror_version? + Gem::Version.new(ReactOnRails::VERSION).prerelease? + rescue ArgumentError + false + end + # Keep manual fallback pinned to the latest stable release (drop pre-release suffixes like .rc.N). # react_on_rails_pro follows the same version number as react_on_rails by policy. # Both gems are released in lockstep; if this ever changes, replace with a dedicated constant. diff --git a/react_on_rails/spec/react_on_rails/generators/generator_helper_spec.rb b/react_on_rails/spec/react_on_rails/generators/generator_helper_spec.rb index a7c413637d..5e469e4c35 100644 --- a/react_on_rails/spec/react_on_rails/generators/generator_helper_spec.rb +++ b/react_on_rails/spec/react_on_rails/generators/generator_helper_spec.rb @@ -203,29 +203,26 @@ def self.read end end - describe "RSC Pro mode helpers" do - it "enables rsc-pro mode for explicit --rsc-pro flag" do - allow(self).to receive(:options).and_return({ rsc_pro: true, rsc: false, pro: false }) + describe "Pro/RSC flag helpers" do + it "treats --rsc as implying Pro" do + allow(self).to receive(:options).and_return({ rsc: true, pro: false }) - expect(use_rsc_pro_mode?).to be(true) expect(use_rsc?).to be(true) expect(use_pro?).to be(true) end - it "enables rsc-pro mode when --rsc and --pro are both set" do - allow(self).to receive(:options).and_return({ rsc_pro: false, rsc: true, pro: true }) + it "enables Pro without RSC for --pro alone" do + allow(self).to receive(:options).and_return({ rsc: false, pro: true }) - expect(use_rsc_pro_mode?).to be(true) - expect(use_rsc?).to be(true) + expect(use_rsc?).to be(false) expect(use_pro?).to be(true) end - it "does not enable rsc-pro mode for standalone --pro" do - allow(self).to receive(:options).and_return({ rsc_pro: false, rsc: false, pro: true }) + it "does not enable Pro or RSC for a plain install" do + allow(self).to receive(:options).and_return({ rsc: false, pro: false }) - expect(use_rsc_pro_mode?).to be(false) - expect(use_rsc?).to be(false) - expect(use_pro?).to be(true) + expect(use_rsc?).to be_falsey + expect(use_pro?).to be_falsey 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 index e24cffc09f..66aca79706 100644 --- 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 @@ -1724,91 +1724,6 @@ class ActiveSupport::TestCase end end - context "with --rsc-pro" do - before(:all) { run_generator_test_with_args(%w[--rsc-pro], package_json: true) } - - include_examples "rsc_common_files" - include_examples "rsc_hello_server_files" - - it "pins Pro dependencies and installs the RSC dependency" do - expected_npm_version = ReactOnRails::VersionSyntaxConverter.new.rubygem_to_npm(ReactOnRails::VERSION) - expected_rsc_npm_version = ReactOnRails::Generators::JsDependencyManager::RSC_PACKAGE_VERSION_PIN - - assert_file "package.json" do |content| - package_json = JSON.parse(content) - deps = package_json["dependencies"] || {} - expect(deps["react-on-rails-rsc"]).to eq(expected_rsc_npm_version) - expect(deps["react-on-rails-pro"]).to eq(expected_npm_version) - expect(deps["react-on-rails-pro-node-renderer"]).to eq(expected_npm_version) - end - end - - it "creates node-renderer.js" do - assert_file "renderer/node-renderer.js" do |content| - expect(content).to include("reactOnRailsProNodeRenderer") - expect(content).to include("require('react-on-rails-pro-node-renderer')") - end - end - - it "adds RSC bundle watcher to Procfile.dev" do - assert_file "Procfile.dev" do |content| - expect(content).to include("RSC_BUNDLE_ONLY=true") - expect(content).to include("rsc-bundle:") - expect(content).to include("bin/shakapacker-watch --watch") - end - end - - it "creates HelloServer instead of HelloWorld" do - assert_no_file "app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx" - assert_no_file "app/javascript/src/HelloWorld/ror_components/HelloWorld.server.jsx" - assert_no_file "app/controllers/hello_world_controller.rb" - assert_file "config/routes.rb" do |content| - expect(content).not_to include("hello_world") - end - - assert_file "app/javascript/src/HelloServer/ror_components/HelloServer.jsx" - assert_file "app/javascript/src/HelloServer/components/HelloServer.jsx" - assert_file "app/javascript/src/HelloServer/components/LikeButton.jsx" - end - - it "adds HelloServer route and RSC payload route" do - assert_file "config/routes.rb" do |content| - expect(content).to include("hello_server") - expect(content).to include("rsc_payload") - end - end - - it "creates rscWebpackConfig.js" do - assert_file "config/webpack/rscWebpackConfig.js" do |content| - expect(content).to include("const serverWebpackModule = require('./serverWebpackConfig')") - expect(content).to include("const serverWebpackConfig = serverWebpackModule.default || serverWebpackModule") - expect(content).to include("serverWebpackConfig(true)") - expect(content).to include("rsc-bundle") - expect(content).to include("react-server") - end - end - - it "serverWebpackConfig includes RSCWebpackPlugin import" do - assert_file "config/webpack/serverWebpackConfig.js" do |content| - expect(content).to include("RSCWebpackPlugin") - expect(content).to include("react-on-rails-rsc/WebpackPlugin") - end - end - - it "serverWebpackConfig has rscBundle parameter" do - assert_file "config/webpack/serverWebpackConfig.js" do |content| - expect(content).to match(/configureServer\s*=\s*\(rscBundle\s*=\s*false\)/) - expect(content).to include("if (!rscBundle)") - end - end - - it "sets DEFAULT_ROUTE to hello_server in bin/dev" do - assert_file "bin/dev" do |content| - expect(content).to include('DEFAULT_ROUTE = "hello_server"') - end - end - end - context "with --rsc --redux" do before(:all) { run_generator_test_with_args(%w[--rsc --redux], package_json: true) } @@ -2246,13 +2161,12 @@ class ActiveSupport::TestCase command = install_generator.send(:recovery_install_command) - expect(command).to eq("rails generate react_on_rails:install --redux --typescript --rspack --rsc-pro") + expect(command).to eq("rails generate react_on_rails:install --redux --typescript --rspack --rsc") expect(command).not_to include("--ignore-warnings") expect(command).not_to include("--force") expect(command).not_to include("--skip") expect(command).not_to include("--pretend") expect(command).not_to include("--pro") - expect(command).not_to match(/\s--rsc(\s|$)/) end specify "recovery_install_command includes --pro when requested without --rsc" do @@ -2263,16 +2177,6 @@ class ActiveSupport::TestCase expect(command).to eq("rails generate react_on_rails:install --pro") end - specify "recovery_install_command prefers --rsc-pro over --rsc/--pro" do - install_generator = described_class.new([], { rsc_pro: true, rsc: true, pro: true }) - - command = install_generator.send(:recovery_install_command) - - expect(command).to eq("rails generate react_on_rails:install --rsc-pro") - expect(command).not_to match(/\s--rsc(\s|$)/) - expect(command).not_to match(/\s--pro(\s|$)/) - end - specify "shakapacker install error preserves original install flags" do install_generator = described_class.new([], { redux: true, typescript: true, ignore_warnings: true }) @@ -2295,8 +2199,8 @@ class ActiveSupport::TestCase expect(output_text).to include("Then re-run: rails generate react_on_rails:install --rspack --pro") end - specify "rsc-pro installs include a dedicated verification checklist message" do - run_generator_test_with_args(%w[--rsc-pro], package_json: true) do + specify "rsc installs include the Pro verification checklist message" do + run_generator_test_with_args(%w[--rsc], package_json: true) do simulate_existing_file("bin/shakapacker", "") simulate_existing_file("bin/shakapacker-dev-server", "") simulate_existing_file("config/shakapacker.yml", "default: {}\n") @@ -2406,40 +2310,6 @@ class ActiveSupport::TestCase redux_pro_rsc_install_generator.send(:invoke_generators) end - - it "treats --rsc-pro as pro+rsc when invoking sub-generators" do - rsc_pro_install_generator = described_class.new([], { pretend: true, rsc_pro: true }) - - allow(rsc_pro_install_generator).to receive(:ensure_shakapacker_installed) - allow(rsc_pro_install_generator).to receive(:setup_react_dependencies) - - expect(rsc_pro_install_generator).to receive(:invoke) - .with("react_on_rails:base", [], hash_including(pro: true, rsc: true, pretend: true)) - expect(rsc_pro_install_generator).to receive(:invoke) - .with("react_on_rails:pro", [], hash_including(pretend: true)) - expect(rsc_pro_install_generator).to receive(:invoke) - .with("react_on_rails:rsc", [], hash_including(pretend: true)) - - rsc_pro_install_generator.send(:invoke_generators) - end - - it "forwards RSC mode to the Redux generator for --rsc-pro --redux" do - rsc_pro_redux_install_generator = described_class.new([], { pretend: true, rsc_pro: true, redux: true }) - - allow(rsc_pro_redux_install_generator).to receive(:ensure_shakapacker_installed) - allow(rsc_pro_redux_install_generator).to receive(:setup_react_dependencies) - - expect(rsc_pro_redux_install_generator).to receive(:invoke) - .with("react_on_rails:base", [], hash_including(pro: true, rsc: true, pretend: true)) - expect(rsc_pro_redux_install_generator).to receive(:invoke) - .with("react_on_rails:react_with_redux", [], hash_including(rsc: true, pretend: true)) - expect(rsc_pro_redux_install_generator).to receive(:invoke) - .with("react_on_rails:pro", [], hash_including(pretend: true)) - expect(rsc_pro_redux_install_generator).to receive(:invoke) - .with("react_on_rails:rsc", [], hash_including(pretend: true)) - - rsc_pro_redux_install_generator.send(:invoke_generators) - end end context "when detecting existing bin-files on *nix" do @@ -3104,12 +2974,12 @@ class ActiveSupport::TestCase end end - context "when using --rsc-pro flag without Pro gem installed" do - let(:install_generator) { described_class.new([], { rsc_pro: true }) } - let(:expected_pro_version) { ReactOnRails::VERSION } + context "when using --rsc flag with a prerelease ReactOnRails version" do + let(:install_generator) { described_class.new([], { rsc: true }) } let(:fake_pid) { 12_345 } before do + stub_const("ReactOnRails::VERSION", "16.4.0.rc.5") allow(Gem).to receive(:loaded_specs).and_return({}) allow(install_generator).to receive(:gem_in_lockfile?).with("react_on_rails_pro").and_return(false) allow(Bundler).to receive(:with_unbundled_env).and_yield @@ -3118,28 +2988,14 @@ class ActiveSupport::TestCase .with(fake_pid).and_return(instance_double(Process::Status, success?: false)) end - specify "missing_pro_gem? uses rsc-pro flag context and exact bundle-add version" do - expect(install_generator.send(:missing_pro_gem?)).to be true - expect(Bundler).to have_received(:with_unbundled_env) - expect(Process).to have_received(:spawn) - .with("bundle add react_on_rails_pro --version='#{expected_pro_version}' --strict", - out: anything, - err: anything) - error_text = GeneratorMessages.messages.join("\n") - expect(error_text).to include("--rsc-pro") - expect(error_text).to include("gem 'react_on_rails_pro', '#{expected_pro_version}'") - expect(error_text).not_to include("~> #{expected_pro_version}") - end - - specify "missing_pro_gem? keeps prerelease suffix when rsc-pro exact pinning is used" do - stub_const("ReactOnRails::VERSION", "16.4.0.rc.5") - + specify "missing_pro_gem? uses exact version pin and surfaces a prerelease note" do expect(install_generator.send(:missing_pro_gem?)).to be true expect(Process).to have_received(:spawn) .with("bundle add react_on_rails_pro --version='16.4.0.rc.5' --strict", out: anything, err: anything) error_text = GeneratorMessages.messages.join("\n") + expect(error_text).to include("--rsc") expect(error_text).to include("gem 'react_on_rails_pro', '16.4.0.rc.5'") expect(error_text).to include("may not be published yet") expect(error_text).to include("path:") @@ -3232,7 +3088,7 @@ class ActiveSupport::TestCase end context "when force-checking Pro gem without pro-related flags" do - let(:install_generator) { described_class.new([], { pro: false, rsc: false, rsc_pro: false }) } + let(:install_generator) { described_class.new([], { pro: false, rsc: false }) } let(:fake_pid) { 12_345 } before do @@ -3293,26 +3149,6 @@ class ActiveSupport::TestCase end end - context "when --rsc-pro flag used on a dirty worktree without pro gem" do - let(:install_generator) { described_class.new([], { rsc_pro: true }) } - - before do - allow(ReactOnRails::GitUtils).to receive(:warn_if_uncommitted_changes).and_return(true) - allow(install_generator).to receive(:cli_exists?).with("git").and_return(true) - allow(install_generator).to receive_messages(missing_node?: false, missing_package_manager?: false) - allow(Gem).to receive(:loaded_specs).and_return({}) - allow(install_generator).to receive(:gem_in_lockfile?).with("react_on_rails_pro").and_return(false) - end - - specify "installation_prerequisites_met? returns false with clear error" do - expect(install_generator.send(:installation_prerequisites_met?)).to be false - error_text = GeneratorMessages.messages.join("\n") - expect(error_text).to include("react_on_rails_pro") - expect(error_text).to include("uncommitted changes") - expect(error_text).to include("--rsc-pro") - end - end - context "when --pro flag used on a dirty worktree with pro gem installed" do let(:install_generator) { described_class.new([], { pro: true }) } From bc43fc25b84f55faa9b2904a1d1d130e12d2c8e5 Mon Sep 17 00:00:00 2001 From: ihabadham Date: Tue, 21 Apr 2026 19:45:30 +0200 Subject: [PATCH 2/5] chore(ci): exclude guavapass.com from lychee link check The guavapass.com link in PROJECTS.md consistently fails the lychee link check from GitHub Actions runners with a TLS handshake error, even though the site is reachable from other networks. This is a bot-filter at the site's TLS layer blocking the runner IP range. Adds an exclusion matching the established pattern for other PROJECTS.md links that block automated requests. Co-Authored-By: Claude Opus 4.6 (1M context) --- .lychee.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/.lychee.toml b/.lychee.toml index 730f373ce7..ed607496ec 100644 --- a/.lychee.toml +++ b/.lychee.toml @@ -80,6 +80,7 @@ exclude = [ # ============================================================================ '^https://blog\.shakacode\.com', # May block automated requests '^https?://(www\.)?foxford\.ru', # Russian site, often blocks bots + '^https://(www\.)?guavapass\.com', # TLS handshake fails from GitHub Actions (bot filter) '^https://(www\.)?airgoat\.com', # Returns 403 '^https://(www\.)?first\.io', # Returns 403 '^https://(www\.)?estately\.com', # Returns 403 From f867f9433a98718ec1a63b82e202ebccf4a3b997 Mon Sep 17 00:00:00 2001 From: ihabadham Date: Tue, 21 Apr 2026 20:18:31 +0200 Subject: [PATCH 3/5] chore: address review feedback on --rsc-pro flag removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - pro_setup.rb: rename `rsc_prerelease_note` to `prerelease_note` and gate on `prerelease_ror_version?` instead of `use_rsc?`. The exact version pin in `pro_gem_version_requirement` already fires on any prerelease regardless of flag; the explanatory note should match so a `--pro` user on a prerelease gem sees the same "may not be published yet" context a `--rsc` user gets. Dropped the intermediate local variable that clashed with the renamed method. - install_generator.rb: rename `rsc_pro_verification_message` to `rsc_verification_message` at both the call site and the definition. The method now fires on any `use_rsc?` install; the `_pro_` in the identifier was a stale reference to the removed flag. Heading text ("🔎 RSC Pro Verification") unchanged — --rsc implies Pro, so the content description is still accurate. - CHANGELOG.md: restore cross-reference to the silent-failure bugs issue with correct label. The previous label said "[PR 3104]" even though the URL pointed to /issues/; both reviewers flagged the inconsistency. Now labeled "[Issue 3104]" with the same URL. Co-Authored-By: Claude Opus 4.6 (1M context) --- CHANGELOG.md | 2 +- .../lib/generators/react_on_rails/install_generator.rb | 4 ++-- react_on_rails/lib/generators/react_on_rails/pro_setup.rb | 8 ++------ 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c49ffe40cb..86b96ba30b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,7 @@ After a release, run `/update-changelog` in Claude Code to analyze commits, writ #### Removed -- **[Pro]** **Removed the `--rsc-pro` install generator flag**: `--rsc` already implies Pro, so the separate mode was unnecessary. Behaviors previously gated on `--rsc-pro` (Pro verification checklist, prerelease install note, exact Pro gem pin on prereleases) now fire on `--rsc` installs. [PR 3105](https://github.com/shakacode/react_on_rails/pull/3105) by [ihabadham](https://github.com/ihabadham). +- **[Pro]** **Removed the `--rsc-pro` install generator flag**: `--rsc` already implies Pro, so the separate mode was unnecessary. Behaviors previously gated on `--rsc-pro` (Pro verification checklist, prerelease install note, exact Pro gem pin on prereleases) now fire on `--rsc` installs. See also [Issue 3104](https://github.com/shakacode/react_on_rails/issues/3104), which tracks unrelated silent-failure bugs in the Pro upgrade automation. [PR 3105](https://github.com/shakacode/react_on_rails/pull/3105) by [ihabadham](https://github.com/ihabadham). #### Changed 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 index 58251dc7d2..6a601b25d1 100644 --- a/react_on_rails/lib/generators/react_on_rails/install_generator.rb +++ b/react_on_rails/lib/generators/react_on_rails/install_generator.rb @@ -636,7 +636,7 @@ def add_post_install_message ci_workflow_generated: @ci_workflow_generated == true, app_root: destination_root )) - GeneratorMessages.add_info(rsc_pro_verification_message) if use_rsc? + GeneratorMessages.add_info(rsc_verification_message) if use_rsc? end def shakapacker_setup_incomplete? @@ -659,7 +659,7 @@ def recovery_install_command ["rails generate react_on_rails:install", *flags].join(" ") end - def rsc_pro_verification_message + def rsc_verification_message <<~MSG 🔎 RSC Pro Verification: diff --git a/react_on_rails/lib/generators/react_on_rails/pro_setup.rb b/react_on_rails/lib/generators/react_on_rails/pro_setup.rb index 5c61a11e59..abbffcbe9a 100644 --- a/react_on_rails/lib/generators/react_on_rails/pro_setup.rb +++ b/react_on_rails/lib/generators/react_on_rails/pro_setup.rb @@ -60,13 +60,10 @@ def missing_pro_gem?(force: false) return false if pro_gem_installed? return false if attempt_pro_gem_auto_install - context_line = pro_gem_requirement_context_line - prerelease_note = rsc_prerelease_note - GeneratorMessages.add_error(<<~MSG.strip) 🚫 Failed to auto-install #{PRO_GEM_NAME} gem. - #{context_line} + #{pro_gem_requirement_context_line} #{prerelease_note} Please add manually to your Gemfile: @@ -99,8 +96,7 @@ def pro_requirement_flag "--pro" end - def rsc_prerelease_note - return "" unless use_rsc? + def prerelease_note return "" unless prerelease_ror_version? "Note: #{PRO_GEM_NAME} #{ReactOnRails::VERSION} may not be published yet. " \ From f7c161affe301c2e991f746e46a3336f81bf6db9 Mon Sep 17 00:00:00 2001 From: ihabadham Date: Tue, 21 Apr 2026 20:44:14 +0200 Subject: [PATCH 4/5] chore: address review optional cleanups - pro_setup.rb: skip the prerelease-note line entirely when `prerelease_note` returns an empty string. The old interpolation left two consecutive blank lines in the error message on stable versions (the common case) because an empty `#{prerelease_note}` still consumed its heredoc line. Pre-compute an `optional_prerelease_line` that is either `""` or a leading newline plus the note, and append it to the preceding context line. - install_generator_spec.rb: strengthen the `--rsc` npm-dependency assertion from key presence to exact version-string pins. The deleted `--rsc-pro` context verified the exact `react-on-rails-pro` / `react-on-rails-pro-node-renderer` / `react-on-rails-rsc` npm versions; those assertions are now carried into the surviving `--rsc` context so regressions in `JsDependencyManager` or `VersionSyntaxConverter` remain covered at the integration level. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../lib/generators/react_on_rails/pro_setup.rb | 5 +++-- .../generators/install_generator_spec.rb | 11 +++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/react_on_rails/lib/generators/react_on_rails/pro_setup.rb b/react_on_rails/lib/generators/react_on_rails/pro_setup.rb index abbffcbe9a..d33e52f10e 100644 --- a/react_on_rails/lib/generators/react_on_rails/pro_setup.rb +++ b/react_on_rails/lib/generators/react_on_rails/pro_setup.rb @@ -60,11 +60,12 @@ def missing_pro_gem?(force: false) return false if pro_gem_installed? return false if attempt_pro_gem_auto_install + optional_prerelease_line = prerelease_note.empty? ? "" : "\n#{prerelease_note}" + GeneratorMessages.add_error(<<~MSG.strip) 🚫 Failed to auto-install #{PRO_GEM_NAME} gem. - #{pro_gem_requirement_context_line} - #{prerelease_note} + #{pro_gem_requirement_context_line}#{optional_prerelease_line} Please add manually to your Gemfile: gem '#{PRO_GEM_NAME}', '#{pro_gem_version_requirement}' 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 index 66aca79706..9b792be7ee 100644 --- 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 @@ -1575,13 +1575,16 @@ class ActiveSupport::TestCase end end - it "installs RSC npm dependencies" do + it "installs RSC npm dependencies with matched version pins" do + expected_npm_version = ReactOnRails::VersionSyntaxConverter.new.rubygem_to_npm(ReactOnRails::VERSION) + expected_rsc_npm_version = ReactOnRails::Generators::JsDependencyManager::RSC_PACKAGE_VERSION_PIN + assert_file "package.json" do |content| package_json = JSON.parse(content) deps = package_json["dependencies"] || {} - expect(deps).to include("react-on-rails-pro") - expect(deps).to include("react-on-rails-pro-node-renderer") - expect(deps).to include("react-on-rails-rsc") + expect(deps["react-on-rails-pro"]).to eq(expected_npm_version) + expect(deps["react-on-rails-pro-node-renderer"]).to eq(expected_npm_version) + expect(deps["react-on-rails-rsc"]).to eq(expected_rsc_npm_version) end end From ec61e4d573333a117fd25127da750e54802896a2 Mon Sep 17 00:00:00 2001 From: ihabadham Date: Tue, 21 Apr 2026 20:58:22 +0200 Subject: [PATCH 5/5] test: align boolean assertion style with repo convention MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace `be_falsey` with `be(false)` in the plain-install test. The repo uses `be(false)` / `be(true)` ~45 times across the spec tree versus only 2 uses of `be_falsey` (both introduced by this PR), so `be_falsey` was an outlier. `be(false)` is also more precise — it fails on `nil` returns that `be_falsey` would pass silently. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../spec/react_on_rails/generators/generator_helper_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/react_on_rails/spec/react_on_rails/generators/generator_helper_spec.rb b/react_on_rails/spec/react_on_rails/generators/generator_helper_spec.rb index 5e469e4c35..fafd808a2d 100644 --- a/react_on_rails/spec/react_on_rails/generators/generator_helper_spec.rb +++ b/react_on_rails/spec/react_on_rails/generators/generator_helper_spec.rb @@ -221,8 +221,8 @@ def self.read it "does not enable Pro or RSC for a plain install" do allow(self).to receive(:options).and_return({ rsc: false, pro: false }) - expect(use_rsc?).to be_falsey - expect(use_pro?).to be_falsey + expect(use_rsc?).to be(false) + expect(use_pro?).to be(false) end end