Skip to content

Fix generated demo path hints for custom Shakapacker roots#4130

Merged
justin808 merged 24 commits into
mainfrom
codex/4062-demo-path-hints-wrap
Jun 20, 2026
Merged

Fix generated demo path hints for custom Shakapacker roots#4130
justin808 merged 24 commits into
mainfrom
codex/4062-demo-path-hints-wrap

Conversation

@justin808

@justin808 justin808 commented Jun 19, 2026

Copy link
Copy Markdown
Member

Why

Fixes #4062.

Generated demo pages could wrap poorly for long source paths and could point users at default app/javascript paths even when the app uses custom Shakapacker source roots.

What changed

  • Resolves demo source, entry, stylesheet, and component hints from the app Shakapacker config when available.
  • Supports Shakapacker source_entry_path: / by generating entrypoints directly under source_path.
  • Computes generated Tailwind stylesheet import paths from the actual generated entry file locations instead of relying on hardcoded depth.
  • Copies the final React on Rails Shakapacker config before any path-dependent demo directories, entrypoints, or styles are generated, including --force installs.
  • Falls back to the existing default source paths for generated apps without custom settings.
  • Adds path wrapping support to the generated demo views, including RSC path hints.
  • Adds generator coverage for custom source root/source entry path behavior, including Redux, RSC, slash entry-root paths, Tailwind imports, and --force overwrite behavior.
  • Addresses review feedback by documenting lookup/depth/memoization contracts, removing no-op generator writes, simplifying path safety flow, and covering unsafe path edge cases.

Validation

  • git diff --check origin/main...HEAD -> pass

  • BUNDLE_GEMFILE=../Gemfile bundle exec rubocop lib/generators/react_on_rails/generator_helper.rb lib/generators/react_on_rails/react_with_redux_generator.rb lib/generators/react_on_rails/rsc_setup.rb spec/react_on_rails/generators/generator_helper_spec.rb -> 4 files inspected, no offenses

  • bundle exec rspec spec/react_on_rails/generators/generator_helper_spec.rb spec/react_on_rails/generators/install_generator_spec.rb:724 spec/react_on_rails/generators/install_generator_spec.rb:2835 spec/react_on_rails/generators/install_generator_spec.rb:2854 -> 14 examples, 0 failures

  • bundle exec rspec spec/react_on_rails/generators/generator_helper_spec.rb -> 83 examples, 0 failures

  • BUNDLE_GEMFILE=../Gemfile bundle exec rubocop spec/react_on_rails/generators/generator_helper_spec.rb lib/generators/react_on_rails/generator_helper.rb -> 2 files inspected, no offenses

  • bundle exec rspec spec/react_on_rails/generators/generator_helper_spec.rb spec/react_on_rails/generators/install_generator_spec.rb:516 spec/react_on_rails/generators/install_generator_spec.rb:457 -> 3 examples, 0 failures

  • BUNDLE_GEMFILE=../Gemfile bundle exec rubocop lib/generators/react_on_rails/generator_helper.rb spec/react_on_rails/generators/generator_helper_spec.rb spec/react_on_rails/generators/install_generator_spec.rb -> 3 files inspected, no offenses

  • bundle exec rspec spec/react_on_rails/generators/generator_helper_spec.rb spec/react_on_rails/generators/install_generator_spec.rb:415 spec/react_on_rails/generators/install_generator_spec.rb:442 spec/react_on_rails/generators/install_generator_spec.rb:455 spec/react_on_rails/generators/install_generator_spec.rb:477 spec/react_on_rails/generators/install_generator_spec.rb:502 -> 9 examples, 0 failures

  • Earlier worker-focused bundle exec rspec spec/react_on_rails/generators/install_generator_spec.rb subset -> 26 examples, 0 failures

  • Full bundle exec rspec spec/react_on_rails/generators/install_generator_spec.rb reached 194 examples, 0 failures before interruption; exit code was nonzero due the interrupt, not a test failure.

  • Current-head review follow-up validation: BUNDLE_GEMFILE=../Gemfile bundle exec rubocop lib/generators/react_on_rails/base_generator.rb lib/generators/react_on_rails/generator_helper.rb spec/react_on_rails/generators/generator_helper_spec.rb spec/react_on_rails/generators/install_generator_spec.rb -> 4 files inspected, no offenses

  • Current-head review follow-up validation: bundle exec rspec spec/react_on_rails/generators/generator_helper_spec.rb -> 84 examples, 0 failures

  • Current-head review follow-up validation: bundle exec rspec spec/react_on_rails/generators/generator_helper_spec.rb spec/react_on_rails/generators/install_generator_spec.rb:501 spec/react_on_rails/generators/install_generator_spec.rb:457 -> 4 focused install-generator examples, 0 failures; note RSpec location filtering requires running generator_helper_spec.rb separately

  • Path-safety review validation after 61f4c2684: BUNDLE_GEMFILE=../Gemfile bundle exec rubocop lib/generators/react_on_rails/base_generator.rb lib/generators/react_on_rails/generator_helper.rb spec/react_on_rails/generators/generator_helper_spec.rb spec/react_on_rails/generators/install_generator_spec.rb -> 4 files inspected, no offenses

  • Path-safety review validation after 61f4c2684: bundle exec rspec spec/react_on_rails/generators/generator_helper_spec.rb -> 85 examples, 0 failures

  • Path-safety review validation after 61f4c2684: bundle exec rspec spec/react_on_rails/generators/install_generator_spec.rb:501 spec/react_on_rails/generators/install_generator_spec.rb:457 -> 4 focused install-generator examples, 0 failures

  • Review follow-up validation after 6c3b0e9cd: BUNDLE_GEMFILE=../Gemfile bundle exec rubocop lib/generators/react_on_rails/base_generator.rb lib/generators/react_on_rails/generator_helper.rb spec/react_on_rails/generators/generator_helper_spec.rb spec/react_on_rails/generators/install_generator_spec.rb -> 4 files inspected, no offenses

  • Review follow-up validation after 6c3b0e9cd: bundle exec rspec spec/react_on_rails/generators/install_generator_spec.rb:552 -> 1 example, 0 failures

  • Review follow-up validation after 6c3b0e9cd: bundle exec rspec spec/react_on_rails/generators/generator_helper_spec.rb -> 85 examples, 0 failures

  • Review follow-up validation after 6c3b0e9cd: git diff --check -> pass

  • Guard-review validation after cfa66d774: BUNDLE_GEMFILE=../Gemfile bundle exec rubocop lib/generators/react_on_rails/base_generator.rb lib/generators/react_on_rails/generator_helper.rb spec/react_on_rails/generators/generator_helper_spec.rb spec/react_on_rails/generators/install_generator_spec.rb -> 4 files inspected, no offenses

  • Guard-review validation after cfa66d774: bundle exec rspec spec/react_on_rails/generators/generator_helper_spec.rb -> 86 examples, 0 failures

  • Guard-review validation after cfa66d774: bundle exec rspec spec/react_on_rails/generators/install_generator_spec.rb:1541 -> 1 example, 0 failures

  • Redux Tailwind review validation after 33e8f0ca3: git diff --check -> pass

  • Redux Tailwind review validation after 33e8f0ca3: BUNDLE_GEMFILE=../Gemfile bundle exec rubocop lib/generators/react_on_rails/react_with_redux_generator.rb spec/react_on_rails/generators/install_generator_spec.rb -> 2 files inspected, no offenses

  • Redux Tailwind review validation after 33e8f0ca3: bundle exec rspec spec/react_on_rails/generators/install_generator_spec.rb:530 -> 2 examples, 0 failures

  • Guard coverage review validation after a406d3eeb: git diff --check, BUNDLE_GEMFILE=../Gemfile bundle exec rubocop lib/generators/react_on_rails/react_no_redux_generator.rb spec/react_on_rails/generators/install_generator_spec.rb, bundle exec rspec spec/react_on_rails/generators/install_generator_spec.rb:1561, and bundle exec rspec spec/react_on_rails/generators/install_generator_spec.rb:1569 -> pass

  • pre-commit and pre-push hooks passed on the pushed branch; latest pre-push RuboCop inspected 10 branch Ruby files with no offenses.

  • Latest review-fix validation after b71a72792: git diff --check, BUNDLE_GEMFILE=../Gemfile bundle exec rubocop lib/generators/react_on_rails/base_generator.rb lib/generators/react_on_rails/generator_helper.rb spec/react_on_rails/generators/generator_helper_spec.rb spec/react_on_rails/generators/install_generator_spec.rb, and bundle exec rspec spec/react_on_rails/generators/install_generator_spec.rb:1561 spec/react_on_rails/generators/install_generator_spec.rb:1569 spec/react_on_rails/generators/generator_helper_spec.rb:342 -> pass; pre-commit/pre-push hooks passed.

  • Slash-entry review validation after b27832705: git diff --check, BUNDLE_GEMFILE=../Gemfile bundle exec rubocop spec/react_on_rails/generators/install_generator_spec.rb, and bundle exec rspec spec/react_on_rails/generators/install_generator_spec.rb:447 spec/react_on_rails/generators/install_generator_spec.rb:475 -> 4 examples, 0 failures; pre-commit/pre-push hooks passed.

  • Latest TypeScript custom-root review validation after 756544e33: git diff --check, BUNDLE_GEMFILE=../Gemfile bundle exec rubocop lib/generators/react_on_rails/install_generator.rb spec/react_on_rails/generators/install_generator_spec.rb, and bundle exec rspec spec/react_on_rails/generators/install_generator_spec.rb:564 -> pass; pre-commit/pre-push hooks passed.

  • TypeScript ordering review validation after 558296bd3: git diff --check, BUNDLE_GEMFILE=../Gemfile bundle exec rubocop lib/generators/react_on_rails/install_generator.rb spec/react_on_rails/generators/install_generator_spec.rb, and bundle exec rspec spec/react_on_rails/generators/install_generator_spec.rb:564 spec/react_on_rails/generators/install_generator_spec.rb:590 -> pass; pre-commit/pre-push hooks passed.

  • CSS-module conflict-handling review validation after b2a6c070e: git diff --check, BUNDLE_GEMFILE=../Gemfile bundle exec rubocop lib/generators/react_on_rails/install_generator.rb spec/react_on_rails/generators/install_generator_spec.rb, and bundle exec rspec spec/react_on_rails/generators/install_generator_spec.rb:564 spec/react_on_rails/generators/install_generator_spec.rb:590 spec/react_on_rails/generators/install_generator_spec.rb:3717 -> 4 examples, 0 failures; pre-commit/pre-push hooks passed.

  • RSC slash-entry discovery validation after aa8edbf10: git diff --check, BUNDLE_GEMFILE=../Gemfile bundle exec rubocop spec/react_on_rails/generators/install_generator_spec.rb, and bundle exec rspec spec/react_on_rails/generators/install_generator_spec.rb:662 -> 1 example, 0 failures; pre-commit/pre-push hooks passed.

Notes

Current head aa8edbf10 is pushed after all current-head review follow-up. Hosted checks are complete, failed checks are 0, and unresolved review threads are 0.


Note

Low Risk
Scope is install-time generator output and demo templates only; no runtime Rails or bundler behavior changes for existing apps.

Overview
Install generators no longer hardcode app/javascript / packs for Hello World, Redux, RSC, Tailwind, and TypeScript scaffolding. They read config/shakapacker.yml (source_path, source_entry_path, including / for entrypoints at the source root) and place demo components, server-bundle.js, stylesheets, tsconfig includes, and view file hints under those paths.

copy_packer_config runs first so path helpers memoize the final config (including --force overwrites); memoizing before that step raises. Tailwind CSS imports are computed relative to each generated entry file. RSC webpack config normalizes slash entry paths. Demo views wrap long path hints in CSS.

Generator specs cover custom roots, slash entry paths, Redux/RSC/Tailwind/TypeScript, and --force behavior.

Reviewed by Cursor Bugbot for commit b52c633. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • New Features
    • Generators now place demo/client/server components, entrypoints, and assets using the configured Shakapacker source_path/source_entry_path.
    • Tailwind and stylesheet assets are generated and import-injected relative to the configured source.
    • TypeScript type definitions and tsconfig includes now align with the configured Shakapacker source path.
  • Bug Fixes
    • Fixed generator execution ordering for reliable Shakapacker config generation.
    • Improved safety for generated destinations with edge-case/invalid path handling.
    • Enhanced path hint styling (better wrapping/readability).
  • Tests
    • Expanded coverage for custom paths, Tailwind/SSR/RSC behaviors, and execution-order regressions.

@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

The generators replace hardcoded app/javascript/src/... paths with config-backed helper methods derived from config/shakapacker.yml. A new suite of path helpers and destination-safety validators is added to GeneratorHelper. Demo view file hints now reflect actual configured source locations, TypeScript and CSS-module type definitions are placed under the configured Shakapacker source path, and generated view templates add a .path-hint CSS class for wrapping long paths.

Changes

Shakapacker config-driven generator path helpers and demo page accuracy

Layer / File(s) Summary
GeneratorHelper: constants, path helpers, safety validators
react_on_rails/lib/generators/react_on_rails/generator_helper.rb
Adds pathname require, default Shakapacker path constants, and a full suite of memoized config-backed helpers (shakapacker_source_path, shakapacker_source_entry_path, shakapacker_entrypoint_path, shakapacker_stylesheet_path, relative_stylesheet_import_path, example_component_source_directory, example_component_source_path) plus safe_generator_destination_path, absolute_path_relative_to_destination, and unsafe_generator_destination_path? validators.
base_generator: ordering guard and computed copy destinations
react_on_rails/lib/generators/react_on_rails/base_generator.rb
copy_packer_config gains a Thor::Error precondition guard enforcing execution order; create_react_directories, copy_js_bundle_files, copy_tailwind_files, and example_source_path replace hardcoded app/javascript/src/... strings with helper method calls.
react_no_redux_generator: computed component directory paths
react_on_rails/lib/generators/react_on_rails/react_no_redux_generator.rb
copy_base_files computes the HelloWorld component destination via helper and updates all copy operations to use the computed path; create_appropriate_templates passes computed source paths to view configuration instead of hardcoded strings.
react_with_redux_generator: computed component directories and stylesheet imports
react_on_rails/lib/generators/react_on_rails/react_with_redux_generator.rb
create_redux_directories computes component directory and creates Redux subdirectories within it; copy_base_files uses computed path for Tailwind imports via relative_stylesheet_import_path and removes prior gsub_file rewriting; copy_base_redux_files uses computed destinations; create_appropriate_templates passes computed source paths.
rsc_setup: computed component paths and stylesheet imports
react_on_rails/lib/generators/react_on_rails/rsc_setup.rb
create_hello_server_component derives HelloServer directory from helper; add_tailwind_import_to_rsc_client_component computes stylesheet import paths dynamically via relative_stylesheet_import_path; create_hello_server_view passes computed source paths to configuration.
demo_page_config: parameterized source paths in file hints
react_on_rails/lib/generators/react_on_rails/demo_page_config.rb
build_hello_server_view_config and hello_server_file_hints accept a source_path: keyword argument so the file-hint path in the rendered demo view reflects the configured Shakapacker source location.
Generated view templates: path-hint CSS and markup
react_on_rails/lib/generators/react_on_rails/templates/base/base/app/views/hello_world/index.html.erb.tt, templates/base/base/app/views/home/index.html.erb.tt, templates/rsc/base/app/views/hello_server/index.html.erb.tt
Adds .path-hint CSS class with overflow-wrap: anywhere and word-break rules; updates <code> elements rendering file-path hints to use class="path-hint".
install_generator: TypeScript and CSS-module paths under Shakapacker source
react_on_rails/lib/generators/react_on_rails/install_generator.rb
create_css_module_types places generated css-modules.d.ts under shakapacker_source_path/types/ instead of app/javascript/types/; create_typescript_config uses shakapacker_source_path glob in tsconfig.json include instead of hardcoded app/javascript/**/* pattern.
GeneratorHelper specs: path safety, stylesheet, entrypoint, and import helpers
react_on_rails/spec/react_on_rails/generators/generator_helper_spec.rb
Adds memoization reset helper and new specs for safe_generator_destination_path (traversal and edge cases), shakapacker_stylesheet_path with config resolution and malformed fallback, shakapacker_entrypoint_path (root entry path slash handling), and relative_stylesheet_import_path (depth and out-of-root error).
install_generator specs: pre-installed Shakapacker fixtures and helpers
react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb
Adds require "json" and introduces simulate_preinstalled_shakapacker(source_path:, source_entry_path:) fixture helper to create multi-environment YAML configs with anchors plus minimal webpack setup, and yaml_quoted_string utility for safe YAML scalar embedding.
install_generator specs: pre-installed custom paths and flag combinations
react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb
Covers custom source_path/source_entry_path output placement, --force overwrite, --redux/--tailwind/--rsc flag combinations with custom paths, slash root entry path, demo hint accuracy, Tailwind stylesheet import injection, copy_packer_config Rspack preservation, and ordering regression tests.
Shared generator example assertions: path-hint markup and CSS
react_on_rails/spec/react_on_rails/support/shared_examples/react_no_redux_generator_examples.rb, react_on_rails/spec/react_on_rails/support/shared_examples/react_with_redux_generator_examples.rb
Extends no-redux and redux shared examples to assert that generated hello_world/index.html.erb contains <code class="path-hint"> path references and the overflow-wrap: anywhere CSS rule.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • shakacode/react_on_rails#3937: Both PRs touch Tailwind v4 generator plumbing in the same codepaths (react_with_redux_generator.rb, rsc_setup.rb) for stylesheet import wiring; this PR makes those imports dynamic via relative_stylesheet_import_path.

Suggested labels

bug, enhancement, P2

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.35% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately describes the main objective: fixing demo path hints to use actual Shakapacker configuration instead of hardcoded defaults.
Linked Issues check ✅ Passed All linked issue #4062 requirements are met: CSS wrapping for path hints added, source path hints derived from Shakapacker config via helpers, and comprehensive test coverage for custom roots.
Out of Scope Changes check ✅ Passed All changes directly support issue #4062 objectives: path hint styling, Shakapacker config integration, and demo output accuracy. No unrelated modifications detected.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/4062-demo-path-hints-wrap

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@claude

claude Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Code Review — Fix generated demo path hints for custom Shakapacker roots

Overview

Solid fix. The generator correctly reads config/shakapacker.yml to determine custom source_path/source_entry_path values, with sensible defaults when the config is absent. Path traversal protection in safe_generator_destination_path is appropriately thorough. The CSS overflow-wrap fix is clean and non-invasive.


Issues

1. Hardcoded stylesheet import depth in RSC Tailwind flow

rsc_setup.rb:250 still contains a hardcoded '../../../stylesheets/application.css' path. This works today because the generator always creates {source_path}/src/HelloServer/components/, which is exactly 3 levels below {source_path}/. But the assumption is silent — a future refactor of the directory structure would break the relative import without any compiler or test catching it. Consider computing the relative depth dynamically, or at minimum add an explanatory comment about the assumed depth invariant so the next reader understands why 3 levels is always correct.

2. Section lookup order in shakapacker_path_config_value

The method checks development before default. When YAML aliases are resolved (the common case), development: inherits source_path from default: via <<: *default, so the result is the same. However, in apps where development: explicitly overrides source_path to a different root than default:, generators would use the development value — which is correct since generators run in a dev context — but this is undocumented. A brief comment stating the intent ("prefer the environment the generator runs under") would prevent future confusion.

3. No test coverage for Redux and RSC with custom source root

The new context in install_generator_spec.rb covers only the default no-redux/no-RSC flow. The same path-resolution logic applies to ReactWithReduxGenerator (emits to HelloWorldApp/) and RscSetup (emits to HelloServer/ and wires server-bundle.js). At minimum a shared example or brief spec asserting the custom path is used for the Redux flow would prevent silent regressions.

4. Repeated YAML reads per generator invocation

shakapacker_source_path and shakapacker_source_entry_path each call configured_shakapacker_relative_path, which re-reads and re-parses config/shakapacker.yml independently. Since example_component_source_directory calls shakapacker_source_path for every component name and shakapacker_entrypoint_path reads both values, the YAML file may be parsed 4-5 times per generator run. Memoizing the parsed config (or the resolved paths) would eliminate redundant I/O without any behaviour change.


Nitpicks

  • simulate_preinstalled_shakapacker creates config/webpack/webpack.config.js and the two bin/shakapacker* stubs, but neither file is exercised by the added examples. These exist to satisfy shakapacker_installed?, not the path logic under test — worth a comment so future readers understand why they're there.
  • DEFAULT_SHAKAPACKER_SOURCE_PATH and DEFAULT_SHAKAPACKER_SOURCE_ENTRY_PATH are module-level constants used only by GeneratorHelper internals. Marking them private_constant would signal they're not part of the public API.

Summary

The core logic is correct and the safety guards are solid. The main gaps are: (a) the silent depth assumption for the RSC Tailwind stylesheet import, and (b) missing generator-level test coverage for Redux and RSC flows with a custom source root. Everything else is style/maintenance.

Comment thread react_on_rails/lib/generators/react_on_rails/generator_helper.rb
Comment thread react_on_rails/lib/generators/react_on_rails/rsc_setup.rb Outdated
Comment thread react_on_rails/lib/generators/react_on_rails/generator_helper.rb

@cursor cursor Bot left a comment

Copy link
Copy Markdown

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: Slash entry path mishandled
    • Handled Shakapacker's source_entry_path '/' as the source root while preserving unsafe absolute-path fallback behavior.

Create PR

Or push these changes by commenting:

@cursor push 82ee885845
Preview (82ee885845)
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
@@ -87,7 +87,8 @@
   def shakapacker_source_entry_path
     @shakapacker_source_entry_path ||= configured_shakapacker_relative_path(
       "source_entry_path",
-      DEFAULT_SHAKAPACKER_SOURCE_ENTRY_PATH
+      DEFAULT_SHAKAPACKER_SOURCE_ENTRY_PATH,
+      allow_root: true
     )
   end
 
@@ -107,14 +108,14 @@
     "#{example_component_source_directory(component_name)}/"
   end
 
-  def configured_shakapacker_relative_path(config_key, default)
+  def configured_shakapacker_relative_path(config_key, default, allow_root: false)
     config_path = File.join(destination_root, "config/shakapacker.yml")
     return default unless File.exist?(config_path)
 
     config = parse_shakapacker_yml(config_path)
     configured_path = shakapacker_path_config_value(config, config_key)
 
-    safe_generator_destination_path(configured_path, default:)
+    safe_generator_destination_path(configured_path, default:, allow_root:)
   end
 
   def shakapacker_path_config_value(config, config_key)
@@ -128,9 +129,10 @@
     nil
   end
 
-  def safe_generator_destination_path(path, default:)
+  def safe_generator_destination_path(path, default:, allow_root: false)
     candidate = path.to_s.strip
     return default if candidate.empty?
+    return "" if allow_root && candidate == "/"
 
     pathname = Pathname.new(candidate).cleanpath
     relative_path = if pathname.absolute?

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
--- 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
@@ -204,6 +204,28 @@
     end
   end
 
+  describe "#shakapacker_entrypoint_path" do
+    let(:shakapacker_yml_path) { File.join(destination_root, "config/shakapacker.yml") }
+
+    before do
+      FileUtils.mkdir_p(File.dirname(shakapacker_yml_path))
+    end
+
+    after do
+      FileUtils.rm_rf(File.join(destination_root, "config"))
+    end
+
+    it "treats source_entry_path slash as the source_path root" do
+      File.write(shakapacker_yml_path, <<~YAML)
+        default:
+          source_path: client/app
+          source_entry_path: /
+      YAML
+
+      expect(shakapacker_entrypoint_path("server-bundle.js")).to eq("client/app/server-bundle.js")
+    end
+  end
+
   describe "#parse_shakapacker_yml_content" do
     let(:aliased_config) do
       <<~YAML

You can send follow-ups to the cloud agent here.

Comment thread react_on_rails/lib/generators/react_on_rails/generator_helper.rb
Comment thread react_on_rails/lib/generators/react_on_rails/react_with_redux_generator.rb Outdated
@claude

claude Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Code Review: Fix generated demo path hints for custom Shakapacker roots

Overall: solid PR with good security hygiene. The approach of reading config/shakapacker.yml with safe fallbacks is correct, the path-traversal guards in safe_generator_destination_path cover the important cases (absolute paths, ../ traversal, ./.. edge cases), and memoizing per generator instance is appropriate.

What works well

  • safe_generator_destination_path + unsafe_generator_destination_path? properly reject absolute paths, traversal attempts, and degenerate inputs.
  • Lookup order (development then default) correctly mirrors how generators run in the Rails dev environment; YAML aliases in the test fixture are expanded by the existing parse_shakapacker_yml which already handles them.
  • Three new spec contexts (no-redux, redux, RSC) each assert both file placement and view content — the right level of coverage.

Issues found

1. CSS inconsistency — RSC demo template missing display: inline-block; max-width: 100% (functional)

hello_server/index.html.erb.tt defines .path-hint without display: inline-block and max-width: 100% that the hello_world template includes. Because <code> is inline by default, max-width: 100% is a no-op without display: inline-block, so long custom paths may still overflow their container on the RSC demo page. (Inline comment posted on the specific lines.)

2. Hardcoded 3-level stylesheet import depth in Redux generator (latent)

react_with_redux_generator.rb line 77 hardcodes "import '../../../stylesheets/application.css';\n". The PR adds an explanatory comment for the identical pattern in rsc_setup.rb:249, but the redux generator has no equivalent. The depth is correct because example_component_source_directory always creates src/HelloWorldApp/ror_components/ (3 levels deep), but a parallel comment would guard against future regressions.

3. No-op gsub_file in Redux generator (pre-existing)

react_with_redux_generator.rb:72 replaces "../store/helloWorldStore" with itself — a no-op that still triggers Thor's write machinery. Not introduced by this PR but surfaced since the surrounding block was modified.

Minor observations

  • simulate_preinstalled_shakapacker interpolates paths into YAML without quoting. For client/app this is fine, but a path containing : would produce invalid YAML silently. Wrapping values in quotes (source_path: "#{source_path}") would future-proof the helper.
  • shakapacker_stylesheet_path silently assumes stylesheets live at {source_path}/stylesheets/. This is a valid assumption for standard Shakapacker layouts and reasonable scope for this PR, but a short comment stating the invariant would help.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

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: Stale Shakapacker paths after overwrite
    • Moved Shakapacker config generation before path-dependent base files and added a forced-overwrite Tailwind regression spec.

Create PR

Or push these changes by commenting:

@cursor push d85d629ba1
Preview (d85d629ba1)
diff --git a/react_on_rails/lib/generators/react_on_rails/base_generator.rb b/react_on_rails/lib/generators/react_on_rails/base_generator.rb
--- a/react_on_rails/lib/generators/react_on_rails/base_generator.rb
+++ b/react_on_rails/lib/generators/react_on_rails/base_generator.rb
@@ -188,6 +188,32 @@
         route "get 'hello_world', to: 'hello_world#index'"
       end
 
+      def copy_packer_config
+        base_path = "base/base/"
+        config = "config/shakapacker.yml"
+
+        if options.shakapacker_just_installed?
+          say "Replacing Shakapacker default config with React on Rails version"
+          # Shakapacker's installer just created this file from scratch (no pre-existing config).
+          # Safe to overwrite silently with RoR's version-aware template (e.g., private_output_path).
+          template("#{base_path}#{config}.tt", config, force: true)
+        else
+          say "Adding Shakapacker #{ReactOnRails::PackerUtils.shakapacker_version} config"
+          # Thor handles the conflict: prompts user interactively, or respects --force/--skip flags.
+          template("#{base_path}#{config}.tt", config)
+        end
+
+        # Configure bundler-specific settings
+        configure_rspack_in_shakapacker if using_rspack?
+
+        # Always ensure precompile_hook is configured (Shakapacker 9.0+ only)
+        configure_precompile_hook_in_shakapacker
+
+        # For SSR bundles, configure Shakapacker private_output_path (9.0+ only)
+        # This keeps Shakapacker and React on Rails server bundle paths in sync.
+        configure_private_output_path_in_shakapacker
+      end
+
       def create_react_directories
         # Skip HelloWorld directory for Redux (uses HelloWorldApp) or RSC (uses HelloServer)
         return if options.redux? || use_rsc?
@@ -278,32 +304,6 @@
                   shakapacker_stylesheet_path("application.css"))
       end
 
-      def copy_packer_config
-        base_path = "base/base/"
-        config = "config/shakapacker.yml"
-
-        if options.shakapacker_just_installed?
-          say "Replacing Shakapacker default config with React on Rails version"
-          # Shakapacker's installer just created this file from scratch (no pre-existing config).
-          # Safe to overwrite silently with RoR's version-aware template (e.g., private_output_path).
-          template("#{base_path}#{config}.tt", config, force: true)
-        else
-          say "Adding Shakapacker #{ReactOnRails::PackerUtils.shakapacker_version} config"
-          # Thor handles the conflict: prompts user interactively, or respects --force/--skip flags.
-          template("#{base_path}#{config}.tt", config)
-        end
-
-        # Configure bundler-specific settings
-        configure_rspack_in_shakapacker if using_rspack?
-
-        # Always ensure precompile_hook is configured (Shakapacker 9.0+ only)
-        configure_precompile_hook_in_shakapacker
-
-        # For SSR bundles, configure Shakapacker private_output_path (9.0+ only)
-        # This keeps Shakapacker and React on Rails server bundle paths in sync.
-        configure_private_output_path_in_shakapacker
-      end
-
       def add_base_gems_to_gemfile
         run "bundle"
       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
@@ -450,6 +450,24 @@
     end
   end
 
+  context "with --force, --tailwind, and a pre-installed custom Shakapacker source root" do
+    before(:all) do
+      run_generator_test_with_args(%w[--tailwind], package_json: true, force: true) do
+        simulate_preinstalled_shakapacker(source_path: "client/app", source_entry_path: "entrypoints")
+      end
+    end
+
+    it "generates base and demo files under the overwritten Shakapacker defaults" do
+      assert_file "app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx"
+      assert_file "app/javascript/stylesheets/application.css"
+      assert_file "app/javascript/packs/server-bundle.js"
+
+      assert_no_file "client/app/src/HelloWorld/ror_components/HelloWorld.client.jsx"
+      assert_no_file "client/app/stylesheets/application.css"
+      assert_no_file "client/app/entrypoints/server-bundle.js"
+    end
+  end
+
   context "with --redux and a pre-installed custom Shakapacker source root" do
     before(:all) do
       run_generator_test_with_args(%w[--redux], package_json: true, force: false) do

You can send follow-ups to the cloud agent here.

Comment thread react_on_rails/lib/generators/react_on_rails/base_generator.rb
@claude

claude Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Code Review

Summary

This PR correctly fixes generated demo pages to use actual Shakapacker-configured paths instead of hardcoded app/javascript defaults. The path resolution, memoization, and safety checks are well-structured, and test coverage is thorough. Below are a few findings.


Bug / No-op — remaining gsub_file identity replacement

In react_with_redux_generator.rb (around line 72), this call is still a no-op — it opens, parses, and writes the file without changing anything:

gsub_file(ror_client_file, "../containers/HelloWorldContainer",
          "../containers/HelloWorldContainer")

The PR already removed the analogous no-op for ../store/helloWorldStore. This one should either be removed too, or the replacement strings should differ if there is a real substitution intent.


Redundant nil-guard in absolute_path_relative_to_destination

def absolute_path_relative_to_destination(pathname)
  destination = Pathname.new(destination_root).cleanpath
  relative_path = pathname.relative_path_from(destination).to_s
  return nil if unsafe_generator_destination_path?(relative_path)  # inner check
  relative_path
rescue ArgumentError
  nil
end

The inner unsafe_generator_destination_path? call is unnecessary: the outer safe_generator_destination_path already calls it on the return value. This double-check is harmless but adds confusion. Returning relative_path directly (rescuing ArgumentError) with the safety guard only in the caller would be cleaner.


Tailwind depth assumption needs a stronger comment

The updated comment in rsc_setup.rb:

# Always 3 levels deep: src/HelloServer/components/ -> ../../.. -> source_path/.
stylesheet_import = "import '../../../stylesheets/application.css';"

This is correct because example_component_source_directory always places the component at {source_path}/src/{Component}/ (a fixed 2-level subpath within source path). The comment should reference that dependency explicitly so a future refactor of example_component_source_directory knows to update this import depth too.


shakapacker_source_entry_path returns "" for / — implicit contract

safe_generator_destination_path returns "" when source_entry_path is / and allow_root: true. File.join(source_path, "", filename) works correctly and the test verifies this. However, the empty-string return is an implicit convention. A short inline comment on the return "" branch would make the intent explicit and prevent future callers from misreading it as "no config found."


Minor: duplicated .path-hint CSS block

The .path-hint rule is identical in two generated templates (hello_world/index.html.erb.tt and hello_server/index.html.erb.tt). Unavoidable for standalone generated files, but worth noting if a shared partial is ever introduced.


Positives

  • Memoization pattern (@shakapacker_source_path, @shakapacker_source_entry_path) is clean and correct for generator instances.
  • Path sanitisation (cleanpath + start_with?("../") guards) appropriately prevents traversal in a generator context.
  • Lookup order (development → default) matches how Shakapacker itself resolves config.
  • Test coverage for custom roots, slash entry paths, Redux, and RSC is thorough.
  • Removing the hardcoded ../store/helloWorldStore no-op is a nice bonus cleanup.

Comment thread react_on_rails/lib/generators/react_on_rails/react_with_redux_generator.rb Outdated
Comment thread react_on_rails/lib/generators/react_on_rails/generator_helper.rb Outdated
Comment thread react_on_rails/lib/generators/react_on_rails/generator_helper.rb
Comment thread react_on_rails/lib/generators/react_on_rails/rsc_setup.rb Outdated
Comment thread react_on_rails/lib/generators/react_on_rails/generator_helper.rb Outdated
Comment thread react_on_rails/lib/generators/react_on_rails/generator_helper.rb Outdated
Comment thread react_on_rails/lib/generators/react_on_rails/generator_helper.rb
Comment thread react_on_rails/lib/generators/react_on_rails/generator_helper.rb
Comment thread react_on_rails/lib/generators/react_on_rails/react_with_redux_generator.rb Outdated
@claude

claude Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Code Review

Overall: Solid fix with good safety thinking. A few minor issues worth addressing before merge.

This PR correctly fixes #4062 — generated demo pages were hardcoding app/javascript paths even when the host app had a custom Shakapacker source_path/source_entry_path. The memoized helpers in GeneratorHelper, the earlier placement of copy_packer_config, and the --force fallback-to-defaults behavior are all well-reasoned.


What's working well

  • Path traversal protection in safe_generator_destination_path / unsafe_generator_destination_path? is solid: absolute paths are relativised against destination_root and anything that escapes via ../ is rejected.
  • Memoization order is correct: copy_packer_config (moved to line 191, the earliest public action) writes the RoR config before any path helper can cache, so --force overwrites the user's custom config before @shakapacker_source_path is memoized — the fall-back-to-defaults behaviour documented in the PR is actually guaranteed by execution order.
  • source_entry_path: / handling — returning "" from safe_generator_destination_path and letting File.join elide it is clever and correct (see inline note about documenting the intent).
  • No-op gsub_file removal in the Redux generator is the right call. There's a second identical no-op still present on line 72-73 — see inline comment.
  • Test coverage for custom roots, slash entry paths, Redux, RSC, and --force is thorough.

Issues

Medium — Missing integration test for stylesheet path with custom source root

shakapacker_stylesheet_path hard-codes source_path + "stylesheets/" + filename without a corresponding integration test. The existing custom-root contexts only assert component source files and entrypoints. A test like:

assert_file "client/app/stylesheets/application.css"
assert_no_file "app/javascript/stylesheets/application.css"

...should be added to the Tailwind + custom-root scenario (or a new context) so that a future change to shakapacker_stylesheet_path doesn't silently regress.

Minor — No unit tests for the new path-safety helpers

safe_generator_destination_path, absolute_path_relative_to_destination, and unsafe_generator_destination_path? each handle non-trivial edge cases (absolute paths inside vs. outside destination root, ".", "..", path traversal sequences, empty strings, /). These are tested indirectly through integration tests, but a small dedicated unit spec (not a full generator run) would be cheaper to run and easier to extend as more edge cases appear. See the inline note on line 163.

Minor — Redundant nil check inside absolute_path_relative_to_destination (inline note on line 155)

The method checks unsafe_generator_destination_path? internally before returning, but the caller checks the return value with the same predicate. The inner check is dead code: nil returned by a rescue is already caught by unsafe_generator_destination_path?(nil) in the caller. Suggest removing the inner check to keep the method a pure "compute relative path" helper.


Minor style notes

  • The template source paths in react_no_redux_generator.rb are now repeated verbatim multiple times (e.g. "app/javascript/src/HelloWorld/ror_components/HelloWorld.client.#{ext}"). Consider extracting a constant or a small helper for the template-side fixed source path so that if the template directory structure ever changes, there's one place to update.
  • The shakapacker_stylesheet_path convention (always source_path/stylesheets/) is correct for the generated demo structure but worth a one-line comment so future contributors don't try to read it from Shakapacker config.

Security

No concerns — path traversal to outside the destination root is blocked, generated demo files are scaffolding-only (no production request paths), and Shakapacker config values are sanitised before being written into view templates.

@claude

claude Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Overall: well-designed fix with good test coverage. Leaving inline comments for specific findings.

Comment thread react_on_rails/lib/generators/react_on_rails/generator_helper.rb
@claude

claude Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Code Review: Fix generated demo path hints for custom Shakapacker roots

Overview

This is a well-scoped, well-tested fix. The core problem (demo pages hardcoding app/javascript paths regardless of what Shakapacker is actually configured to use) is real and the approach is sound: read config/shakapacker.yml once, memoize the results, and derive all demo file destinations and path hints from those values.

Strengths

  • Path safety is solid. safe_generator_destination_path guards against null bytes (Pathname.new raises → rescued), traversal (start_with?("../")), and degenerate paths (".", ".."). The allow_root: true sentinel ("/" → "") is clean and tested.
  • ||= memoization interacts correctly with "". "" is truthy in Ruby, so a slash-root entry path caches cleanly and never re-reads the file.
  • Removal of no-op gsub_file calls in react_with_redux_generator.rb is a good cleanup.
  • create_file over File.write for css-modules.d.ts is the right upgrade — Thor's action respects --pretend/--force/--skip and creates parent directories, where raw File.write did neither.
  • Test coverage is comprehensive: 11 new generator-level integration contexts covering custom roots, slash-entry, Redux, RSC, Tailwind, TypeScript, --force overwrite, and ordering guards.

Concerns

1. use_rspack is captured before the template write — is that intentional?

use_rspack = using_rspack?            # reads existing shakapacker.yml
template("...", config, force: true)  # overwrites it
configure_rspack_in_shakapacker if use_rspack

This preserves the user's rspack choice from the pre-install config even after the template overwrites it. The test at line 1074 of install_generator_spec.rb confirms this is intentional and verified. Worth adding a one-line comment at the assignment site for future readers — e.g. "Capture before the template overwrites the config so the existing rspack choice is preserved."

2. The ordering guard raises a user-facing Thor::Error

raise Thor::Error, "copy_packer_config must run before path-dependent generator actions"

If this fires (a developer mistake in generator ordering, not a user mistake), the user sees a confusing install failure. The message is clear enough to diagnose, but adding "(this is a generator bug — please open an issue)" would tell users not to try to fix their Shakapacker config.

3. absolute_path_relative_to_destination silently returns nil

The return type is String | nil but the name implies it always returns a string. The nil case is handled correctly upstream (unsafe_generator_destination_path?(nil)true → fall back to default), but it is a hidden contract. A # @return [String, nil] nil when the path escapes the generator destination annotation or renaming it with a ? convention would make this less surprising to a future reader.

4. source_path: "." falls back silently

If someone has source_path: "." (app root) in their Shakapacker config, safe_generator_destination_path returns the default (app/javascript) with no warning. This is almost certainly correct behavior, but files would be silently written to a different location than the user configured. A say_status :warning here would help users understand why their custom path is being ignored.

5. Minor: no unit test for standard (non-slash) source_entry_path in shakapacker_entrypoint_path

The unit spec tests the slash-root case and blank filename, but not the common case like source_entry_path: "entrypoints" + "server-bundle.js""client/app/entrypoints/server-bundle.js". The install generator integration specs cover this indirectly, but a direct unit test would make shakapacker_entrypoint_path's full contract self-documenting.

Verdict

The PR is ready from a correctness standpoint. Concerns 1 and 3 are the most worth addressing before merge (one comment addition, one annotation), but none are blockers for a well-tested generator change with extensive validation documented in the PR body.

Comment thread react_on_rails/lib/generators/react_on_rails/base_generator.rb
Comment thread react_on_rails/lib/generators/react_on_rails/base_generator.rb
Comment thread react_on_rails/lib/generators/react_on_rails/generator_helper.rb
Comment thread react_on_rails/lib/generators/react_on_rails/generator_helper.rb

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

Copy link
Copy Markdown

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

ℹ️ 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 thread react_on_rails/lib/generators/react_on_rails/generator_helper.rb
@claude

claude Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Code Review: Fix generated demo path hints for custom Shakapacker roots

Overview

This PR correctly solves a real user-facing problem: generators were writing demo scaffolding to hardcoded app/javascript/packs paths regardless of the app's actual Shakapacker config. The approach — reading config/shakapacker.yml via the already-existing parse_shakapacker_yml infrastructure and memoizing behind a call-order guard — is sound. Test coverage across all flag combinations is thorough.


Strengths

  • Memoization guard in copy_packer_config using instance_variable_defined? is the right check (catches memoized nil/false too, not just truthy values). Good defensive programming.
  • Path safety: safe_generator_destination_path handles null bytes, directory traversal (../), absolute paths outside the destination root, and the Shakapacker / sentinel cleanly. The Pathname#cleanpath call before the traversal check is important and present.
  • create_file over File.write for css-modules.d.ts is a correctness fix — it now respects --pretend, --force, and --skip flags.
  • Removed no-op gsub_file calls in react_with_redux_generator.rb (replacing a string with itself). Good catch.
  • File.join(*[...].reject(&:empty?)) for the slash entry-path case is clean — avoids a double-slash without a special-case conditional.

Issues

Minor: redundant rescue Psych::SyntaxError in configured_shakapacker_relative_path

parse_shakapacker_yml (in shakapacker_precompile_hook_helper.rb) already catches ScriptError, StandardError internally and returns {}. Psych::SyntaxError is a subclass of StandardError, so the outer rescue in configured_shakapacker_relative_path can never fire. It is dead code — not a bug, but it misleads readers into thinking this path is reachable.

Observation: test fixture YAML uses merge keys (<<: *default)

simulate_preinstalled_shakapacker writes YAML with <<: *default anchors. On Ruby < 3.1 (Psych < 4.0), parse_shakapacker_yml_content's yaml_safe_load_supports_aliases? returns false, causing it to return {} for YAML that uses aliases. That would silently make all custom-path tests fall back to default paths and the assert_file "client/app/..." assertions would fail. Since CI is passing, the project must be on Ruby 3.1+, but this is a fragility worth documenting or mitigating — e.g., use flat/inline YAML (no anchors) in the simulate_preinstalled_shakapacker fixture.

Nit: yaml_quoted_string deserves an explanation comment

The trick of using JSON.generate to produce safe YAML scalars is non-obvious. A one-line comment explaining that JSON strings are valid YAML scalars (and thus give free quoting/escaping for free) would help the next reader.


Security

No concerns. Path traversal is handled correctly. YAML is parsed with YAML.safe_load, not YAML.load. No shell interpolation of config values.


Summary

The implementation is solid and the test coverage is comprehensive. The main actionable items are the redundant rescue (remove or add a belt-and-suspenders comment) and the YAML anchors fragility in the test fixtures. Everything else is clean.

Comment thread react_on_rails/lib/generators/react_on_rails/generator_helper.rb
Comment thread react_on_rails/lib/generators/react_on_rails/generator_helper.rb
Comment thread react_on_rails/lib/generators/react_on_rails/base_generator.rb
@claude

claude Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Code Review — PR #4130

Overview

This PR fixes a real and impactful bug: the install generator was hardcoding app/javascript / packs everywhere even when the app's Shakapacker config used a different source root. The fix is architecturally sound — derive paths from config, write config first, memoize after — and the test coverage is thorough. Below are a few things worth addressing before merge.


Issues

1. Dead rescue Psych::SyntaxError in configured_shakapacker_relative_path

(generator_helper.rb:138) — see inline comment.

parse_shakapacker_yml (in ShakapackerPrecompileHookHelper) already catches every exception and returns {}, so Psych::SyntaxError can never reach this clause. The malformed-YAML spec passes because parse_shakapacker_yml's own rescue handles it. The clause is dead code: remove it, or replace it with rescue StandardError and a comment explaining it's a defensive belt-and-suspenders guard.

2. using_rspack? captured before template write — comment missing

(base_generator.rb:202) — see inline comment.

Capturing before the write is the correct intent (preserve the user's pre-existing bundler choice), but it's a subtle behavioral inversion from the original code and will confuse future readers. Adding a two-line comment explaining why the capture precedes the template write is low cost and high value.

3. RSC spec asserts on template implementation text, not behaviour

(install_generator_spec.rb:665) — see inline comment.

expect(content).to include("const sourceEntryPath = config.source_entry_path === '/' ? '' :") tests the literal string in the generated file rather than what the file does at runtime. If the template is refactored to express the same guard differently, the test breaks even though nothing regressed. Either assert on observable runtime behaviour, or add a comment acknowledging it's a text-match by intent.


Minor Observations (no action required)

  • --force with a custom config silently resets to stock paths. The test at line 923 verifies this, and it's the correct and documented behaviour. It may still surprise users; a note in the generator's output when it overwrites the config could help.
  • --typescript --tailwind with a custom root isn't tested as a combination. Both are individually tested with custom roots, and the Tailwind-import path computation is the cross-cutting concern. Low risk, but worth a follow-up if combination issues surface.
  • Pre-existing double copy of HelloWorld.module.css. Both base_generator.rb#copy_js_bundle_files and react_no_redux_generator.rb#copy_base_files copy this file in the non-tailwind/non-redux/non-rsc path. The PR preserves this — Thor shows a no-op conflict on the second write, not a regression.

Strengths

  • Memoization guard (copy_packer_config raising if path IVars are already set) is excellent defensive programming and will catch ordering bugs at generator-run time rather than silently producing wrong paths.
  • Path traversal protection in safe_generator_destination_path is well-layered: cleanpath + absolute-path relativization + traversal prefix check + null-byte ArgumentError rescue.
  • create_file instead of File.write for CSS module types correctly hands conflict resolution back to Thor (--force/--skip/interactive prompt).
  • yaml_quoted_string helper using JSON.generate is a neat, robust way to produce YAML-safe scalars in test fixtures.
  • Slash entry-path sentinel ("" after sanitization, filtered by Array#reject(&:empty?) in File.join) is clean and handles the edge case without special-casing every call site.

Overall this is solid work — the three issues above are all fixable in a follow-up or as small commits to this branch.

@cursor

cursor Bot commented Jun 20, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_5b17d682-0baf-4d0d-a261-335c8ab21ded)

@justin808 justin808 merged commit 3661128 into main Jun 20, 2026
51 checks passed
@justin808 justin808 deleted the codex/4062-demo-path-hints-wrap branch June 20, 2026 03:24
@justin808

Copy link
Copy Markdown
Member Author

Manual verification: reproduced the gap and confirmed the fix ✅

Reproduced via the merged generator_helper_spec.rb using the pre-fix-on-one-file tactic. Bug (#4062): generated demo pages could point users at default app/javascript paths even when the app uses a custom Shakapacker source_path/source_entry_path, and long source paths wrapped poorly.

Reproduction

Squash-merged at 366112842, pre-fix = 366112842~1. Reverted only lib/generators/react_on_rails/generator_helper.rb, then restored.

cd react_on_rails && bundle exec rspec spec/react_on_rails/generators/generator_helper_spec.rb

Results

Scenario Behavior Result
Pre-fix generator_helper.rb safe_generator_destination_path / shakapacker_stylesheet_path don't exist (paths not resolved from Shakapacker config) 13 failed / 87 (BROKEN)
Post-fix restored demo source/entry/stylesheet hints resolved from the app's Shakapacker config; safe relativization + source_entry_path: / sentinel; malformed-config fallback 87 passed / 87 (FIXED)
# PRE-FIX (366112842~1)
✗ #safe_generator_destination_path keeps safe relative paths
    NoMethodError: undefined method 'safe_generator_destination_path'
✗ #shakapacker_stylesheet_path places generated demo stylesheets under the configured Shakapacker source path
   ... (13 path-resolution cases total)
87 examples, 13 failures

# POST-FIX (restored, git status clean)
87 examples, 0 failures

The 13 failures are exactly the new path-resolution cases (safe relativization inside the destination root, fallback for traversing/outside-root paths, the Shakapacker root-entry empty sentinel, and stylesheet placement under the configured source path).

Caveat

Generator-helper level: confirms the path-hint resolution logic (Shakapacker-config-aware source/entry/stylesheet paths + safe relativization). I did not run a full rails new with a custom source_path/source_entry_path: / to eyeball the generated demo views and entrypoints landing in the right place (the PR author reports validating the install-generator path end to end).

justin808 added a commit that referenced this pull request Jun 20, 2026
## Why

The default branch is currently failing `Rspec test for gem` on `main`
in the generator shard:

- Run:
https://github.com/shakacode/react_on_rails/actions/runs/27869267839
- Failed jobs: `rspec-package-tests (4.0, latest, generators)` and
`rspec-package-tests (3.3, minimum, generators)`
- Failed example:
`spec/react_on_rails/generators/install_generator_spec.rb:3717`

The failure was not caused by the most recent Pro streaming PR. The same
generator failure appears on the prior pushed commit and traces back to
the custom Shakapacker-root generator work in #4130. A focused ordered
repro showed the issue:

```bash
cd react_on_rails && bundle exec rspec \
  spec/react_on_rails/generators/install_generator_spec.rb:564 \
  spec/react_on_rails/generators/install_generator_spec.rb:590 \
  spec/react_on_rails/generators/install_generator_spec.rb:3717
```

Before this patch, that sequence failed because earlier examples left
`config/shakapacker.yml` in the shared generator destination. The
pretend-mode example used `expect(File).not_to receive(:read)`, which
accidentally forbade the legitimate config read used to resolve
Shakapacker paths. The behavior the test meant to protect is narrower:
pretend mode must not read the copied Redux Tailwind client entry,
because pretend mode does not create that file.

## What changed

- Adds an explicit `config/shakapacker.yml` fixture to the pretend-mode
example.
- Allows normal `File.read` calls while asserting the generated Redux
client entry is not read.
- Keeps the production generator code unchanged.

## Validation

- `git diff --check` -> passed.
- `cd react_on_rails && bundle exec rspec
spec/react_on_rails/generators/install_generator_spec.rb:564
spec/react_on_rails/generators/install_generator_spec.rb:590
spec/react_on_rails/generators/install_generator_spec.rb:3717` -> 4
examples, 0 failures.
- `cd react_on_rails && BUNDLE_GEMFILE=../Gemfile bundle exec rubocop
spec/react_on_rails/generators/install_generator_spec.rb` -> 1 file
inspected, no offenses.
- `codex review --uncommitted` -> no actionable correctness issues.
- Pre-commit hook -> trailing-newlines, Ruby autofix/RuboCop passed for
the changed spec.
- Pre-push hook -> branch Ruby RuboCop and markdown-links passed.

## Process triage

Process Gap Disposition: `checklist+replay`.

- Motivating miss: #4130 recorded focused validation for
`install_generator_spec.rb:3717`, but the default-branch failure
required replaying that example after earlier custom-root generator
examples that leave `config/shakapacker.yml` in the shared destination.
- Replay evidence: the ordered repro above failed on `main` before this
patch and passed after this patch.
- Non-goal: do not add a broad prose-only rule or a full generator-suite
requirement for every small generator PR.

Merge queue would reduce stale-base/concurrency merges, but it would not
have caught this exact issue unless the queue runs the same generator
shard before landing. The actionable process improvement is to include
ordered replay when adding generator examples that share
`dummy-for-generators`, especially when new examples leave Shakapacker
config or other shared files behind.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Test-only change to generator spec isolation; no production generator
behavior modified.
> 
> **Overview**
> Fixes flaky generator CI by tightening the **pretend-mode Redux +
Tailwind** example in `install_generator_spec.rb`, without changing
generator code.
> 
> The example now seeds its own `config/shakapacker.yml` so Shakapacker
path resolution does not depend on leftovers from earlier examples in
the shared `dummy-for-generators` destination. It stubs `File.read` with
`and_call_original` and only forbids reading the Redux Tailwind client
entry (`HelloWorldApp.client.jsx`), which matches the real behavior:
pretend mode may read config but must not read the copied client file to
inject the stylesheet import.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
ace8804. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
justin808 added a commit that referenced this pull request Jun 22, 2026
## Why

Stamps the `17.0.0.rc.6` changelog section so the release task can
publish it and auto-create the GitHub release. This is the changelog
step that comes **before** running `bundle exec rake release` (the
release task reads the version + notes from `CHANGELOG.md`).

## What

Ran a full `/update-changelog` classification sweep over the **78 merged
PRs** in `v17.0.0.rc.5..origin/main` (plus one revert commit) before
stamping. Most are docs/agent-workflow/CI/test changes (`no-entry`); the
user-visible ones were already accumulated under `[Unreleased]` during
development.

**Stamping:** `bundle exec rake "update_changelog[17.0.0.rc.6]"`
inserted the `### [17.0.0.rc.6] - 2026-06-21` header after
`[Unreleased]`, moved the accumulated entries under it, and rewrote the
compare links (`v17.0.0.rc.5...v17.0.0.rc.6`, `[unreleased]` → `main`).
Prior RC sections are preserved (RC mode does not collapse them).

**Changelog edits made in this pass:**

- **Added one missing entry** — RSC-safe generated i18n locale defaults
([#4146](#4146)): the
only user-visible PR merged after rc.5 that was not yet in the changelog
(gem runtime change in `lib/react_on_rails/locales/`, fixes #4132).
- **Co-credited
[#4107](#4107
alongside #4130 on the "generated demo paths honor custom Shakapacker
source roots" entry — #4107 is the demo-path-wrapping half of the same
Issue #4062 work, already described in that entry.
- **Added the missing PR link
([#4096](#4096 to
the `RenderFunction` types-only breaking-change entry, which previously
linked only the issue.
- **Reordered** the rc.6 category sections to the standard **Added →
Changed → Fixed**, matching rc.2/rc.4 and the changelog skill's
documented order (the `[Unreleased]` accumulation had led with Fixed).

The `17.0.0.rc.6` section contains 14 entries (3 Added, 4 Changed, 7
Fixed).

## Test plan

- `bundle exec rake "update_changelog[17.0.0.rc.6]"` (run with a UTF-8
locale to avoid the `invalid byte sequence in US-ASCII` regex bug) →
header + compare links stamped.
- `pnpm exec prettier --check CHANGELOG.md` → clean.
- Pre-commit/pre-push hooks: trailing-newlines, Lychee markdown links (8
OK / 0 errors), Prettier, branch-lint → all pass.
- Verified `[Unreleased]` is now empty, version headers are
newest-first, and compare links use the `v` prefix.

## Labels

`Labels: none` — changelog/docs-only change; relies on the required PR
gate plus the local verification above.

## After merge

Run `bundle exec rake release` (no args — it reads `17.0.0.rc.6` from
the changelog) to publish and auto-create the GitHub release.

> Note: `main` currently shows pre-existing `detect-changes` failures
unrelated to this changelog-only change.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Follow-up: wrap generated demo file paths and derive source hints from Shakapacker config

1 participant