Why
react_on_rails:install --rsc processes both the Pro and RSC npm dependency groups twice.
The repeated package-manager operations are normally idempotent and complete successfully, but they add avoidable install time and output noise. The RSC pass also queues the temporary package-pin explanation twice, so the final message is duplicated.
Reproduction
cd /tmp
rm -rf ror-duplicate-generator-deps
npx --yes create-react-on-rails-app@17.0.0-rc.9 ror-duplicate-generator-deps --rsc --package-manager npm --no-agent-files | tee create-app.log
grep -n "Installing React on Rails Pro dependencies" create-app.log
grep -n "Installing React Server Components dependencies" create-app.log
grep -n "React Server Components package pin:" create-app.log
Each grep returns two matches.
The same behavior occurs through the direct Ruby generator:
REACT_ON_RAILS_PACKAGE_MANAGER=npm bin/rails generate react_on_rails:install --rsc --no-agent-files
Exact call stack
Parent dependency pass
InstallGenerator#invoke_generators
→ setup_react_dependencies
→ JsDependencyManager#setup_js_dependencies
→ add_js_dependencies
→ add_pro_dependencies # use_pro? is true
→ add_rsc_dependencies # use_rsc? is true
Invoked standalone-generator passes
InstallGenerator#invoke_generators
→ invoke react_on_rails:pro, invoked_by_install: true
→ ProGenerator#run_generator
→ add_pro_npm_dependencies
→ add_pro_dependencies
InstallGenerator#invoke_generators
→ invoke react_on_rails:rsc, invoked_by_install: true
→ RscGenerator#run_generator
→ add_rsc_npm_dependencies
→ add_rsc_dependencies
History and intent
PR #2284 introduced the full-install flags, standalone upgrade generators, central Pro/RSC dependency additions, and the parent-to-child invocations together.
Each dependency path has an independent purpose:
- the install generator centrally builds the selected full dependency stack;
- standalone Pro/RSC generators must add their packages when upgrading existing applications.
The duplication happens only when those paths are composed. invoked_by_install already suppresses child prerequisite checks, standalone messages, and some migration behavior, but does not suppress the child dependency step. PR #2284 describes reruns as idempotent; it does not describe double package-manager work as intended, and no test asserts two dependency additions.
Expected behavior
- Full
install --rsc adds the Pro dependency group once and the RSC dependency group once.
- Standalone
react_on_rails:pro still adds Pro dependencies once.
- Standalone
react_on_rails:rsc still adds RSC dependencies once.
- The RSC pin explanation prints once.
Suggested boundary
During parent-driven installation, keep the central dependency pass as owner and skip the standalone dependency methods when invoked_by_install is true:
add_pro_npm_dependencies unless options[:invoked_by_install]
add_rsc_npm_dependencies unless options[:invoked_by_install]
Equivalent ownership is acceptable if it preserves standalone-generator behavior and performs each dependency operation exactly once.
Acceptance criteria
- Generator coverage asserts one Pro and one RSC dependency addition for
install --rsc.
- Standalone Pro and RSC generator coverage continues to assert one dependency addition each.
- Published-style create-app output contains one Pro install heading, one RSC install heading, and one RSC pin explanation.
Why
react_on_rails:install --rscprocesses both the Pro and RSC npm dependency groups twice.The repeated package-manager operations are normally idempotent and complete successfully, but they add avoidable install time and output noise. The RSC pass also queues the temporary package-pin explanation twice, so the final message is duplicated.
Reproduction
Each grep returns two matches.
The same behavior occurs through the direct Ruby generator:
Exact call stack
Parent dependency pass
Invoked standalone-generator passes
History and intent
PR #2284 introduced the full-install flags, standalone upgrade generators, central Pro/RSC dependency additions, and the parent-to-child invocations together.
Each dependency path has an independent purpose:
The duplication happens only when those paths are composed.
invoked_by_installalready suppresses child prerequisite checks, standalone messages, and some migration behavior, but does not suppress the child dependency step. PR #2284 describes reruns as idempotent; it does not describe double package-manager work as intended, and no test asserts two dependency additions.Expected behavior
install --rscadds the Pro dependency group once and the RSC dependency group once.react_on_rails:prostill adds Pro dependencies once.react_on_rails:rscstill adds RSC dependencies once.Suggested boundary
During parent-driven installation, keep the central dependency pass as owner and skip the standalone dependency methods when
invoked_by_installis true:Equivalent ownership is acceptable if it preserves standalone-generator behavior and performs each dependency operation exactly once.
Acceptance criteria
install --rsc.