-
-
Notifications
You must be signed in to change notification settings - Fork 638
Fix bundle path resolution and improve server bundle security #1798
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Code Review for PR #1798: Fix bundle path resolution and improve server bundle securityOverall Assessment ✅This PR makes significant and well-thought-out improvements to bundle path resolution and security architecture. The changes are well-structured, maintain backward compatibility, and improve the codebase quality. 🎯 Strengths1. Security Architecture Improvements
2. Code Quality Enhancements
3. Robust Fallback Strategy
4. Test Coverage
🔍 Observations & Suggestions1. Performance ConsiderationThe multiple File.exist? checks could have a minor performance impact. Consider:
2. DocumentationConsider adding inline documentation for the new private methods explaining the priority order and rationale. This would help future maintainers understand the security considerations. 3. Configuration OptionYou might want to consider adding a configuration option to customize the secure server bundle locations, allowing users to specify their preferred directories while maintaining the secure defaults. 4. Migration PathThe gradual migration path is well thought out. Consider adding a deprecation warning in a future release for server bundles served from public directories to encourage migration. 🐛 Potential IssuesNone identified. The code handles all edge cases appropriately and maintains backward compatibility. ✅ Best Practices Compliance
📊 Testing VerificationThe test suite additions are thorough and cover:
🎉 ConclusionThis is an excellent PR that addresses critical security concerns while improving code quality and maintainability. The implementation is thoughtful, well-tested, and maintains backward compatibility. The architectural improvements align well with Rails conventions and security best practices. Recommendation: Approve and merge ✅ The changes effectively fix #1797 while introducing valuable security improvements that will benefit all React on Rails users. |
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughRefactors bundle path resolution to prefer a configured private server bundle output, adds configuration and validation for Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant App
participant Utils as ReactOnRails::Utils
participant Config as ReactOnRails::Configuration
participant Packer as Shakapacker/Packer
Note over App,Utils: High-level bundle JS path resolution
App->>Utils: bundle_js_file_path(bundle_name)
Utils->>Config: server_bundle?(bundle_name)?
alt server bundle AND server_bundle_output_path configured
Utils-->>App: return join(server_bundle_output_path, bundle_name)
else
Utils->>Packer: bundle_js_uri_from_packer(bundle_name)
Packer-->>Utils: uri or MissingEntryError
alt uri found
Utils-->>App: join(packer_public_output_path, uri)
else MissingEntryError
Utils->>Config: server_bundle?(bundle_name)?
alt server bundle AND server_bundle_output_path configured
Utils-->>App: return join(server_bundle_output_path, bundle_name)
else
Utils-->>App: return join(packer_public_output_path, fallback_name)
end
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🧰 Additional context used📓 Path-based instructions (2){Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru}}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru,js,jsx,ts,tsx,json,yml,yaml,md,css,scss}}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
🧠 Learnings (3)📚 Learning: 2025-02-18T13:08:01.477ZApplied to files:
📚 Learning: 2025-04-26T21:55:55.874ZApplied to files:
📚 Learning: 2025-02-12T16:38:06.537ZApplied to files:
🧬 Code graph analysis (1)lib/react_on_rails/configuration.rb (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
🔇 Additional comments (9)
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. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR fixes critical bundle path resolution issues for server-side rendering and React Server Components while implementing significant security improvements by moving server bundles to non-public directories. The changes modernize legacy code patterns and add comprehensive fallback logic.
Key changes:
- Fixed fallback logic for bundle resolution when manifest lookup fails (#1797)
- Moved server bundles to secure, non-public locations (
ssr-generated/andgenerated/server-bundles/) - Replaced legacy dynamic constant resolution with direct Shakapacker references
Reviewed Changes
Copilot reviewed 3 out of 5 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
lib/react_on_rails/utils.rb |
Core bundle path resolution logic with server bundle security improvements and comprehensive fallback handling |
spec/react_on_rails/utils_spec.rb |
Extensive test coverage for new bundle resolution logic, server bundle security, and fallback scenarios |
spec/dummy/spec/helpers/react_on_rails_helper_spec.rb |
Simplified constant reference from dynamic resolution to direct Shakapacker usage |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
lib/react_on_rails/utils.rb
Outdated
| private_class_method :bundle_js_file_path_with_packer, :server_bundle?, :try_secure_server_locations, | ||
| :handle_missing_manifest_entry |
Copilot
AI
Sep 21, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The private_class_method declaration should be placed immediately after the method definitions rather than being grouped at the end. This improves readability and makes it clear which methods are private when reading the code sequentially.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or even better, use private_class_method def bundle_js_file_path_with_packer ... etc.
lib/react_on_rails/utils.rb
Outdated
| secure_server_locations = [ | ||
| File.join("ssr-generated", bundle_name), | ||
| File.join("generated", "server-bundles", bundle_name) | ||
| ] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are these considered secure? Do we use this name for them elsewhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public/packs will be shared
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private might be better
lib/react_on_rails/utils.rb
Outdated
| expanded_path = File.expand_path(path) | ||
| return expanded_path if File.exist?(expanded_path) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Duplicated logic with self.try_secure_server_locations, extract it.
lib/react_on_rails/utils.rb
Outdated
| if is_server_bundle | ||
| # For server bundles, prefer secure location as final fallback | ||
| File.expand_path(File.join("ssr-generated", bundle_name)) | ||
| else | ||
| # For client bundles, use environment-specific path (original behavior) | ||
| File.expand_path(fallback_locations.first) | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- No checking if they exist?
- If we don't find anything, would showing a list of tried locations be useful for users?
| require "support/script_tag_utils" | ||
|
|
||
| Packer = Object.const_get(ReactOnRails::PackerUtils.packer_type.capitalize) | ||
| Packer = Shakapacker |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just inline the variable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These changes coming in PR #1801
Code Review for PR #1798: Fix bundle path resolution and improve server bundle security✅ Strengths
|
Pull Request Review: Fix bundle path resolution and improve server bundle securityOverviewThis PR successfully addresses the bundle path resolution issues (#1797) and implements important security improvements by moving server bundles to non-public directories. ✅ Strengths
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
lib/react_on_rails/packs_generator.rb (1)
165-173: Nil-safety bug: .empty? on possibly nil server_bundle_js_file.Docs recommend setting this to nil to disable SSR; calling
.empty?will raise. Use.blank?.Apply this diff:
def add_generated_pack_to_server_bundle return if ReactOnRails.configuration.make_generated_server_bundle_the_entrypoint - return if ReactOnRails.configuration.server_bundle_js_file.empty? + return if ReactOnRails.configuration.server_bundle_js_file.blank?lib/react_on_rails/configuration.rb (1)
141-154: Validate enforce_secure_server_bundles + server_bundle_output_pathIf enforce_secure_server_bundles == true, validate server_bundle_output_path and raise ReactOnRails::Error if it resolves inside Rails.root.join("public") (or is blank) to avoid accidentally exposing server-side bundles.
Location: lib/react_on_rails/configuration.rb — call a new private check from setup_config_values (currently lines ~141–154).
🧹 Nitpick comments (2)
CHANGELOG.md (1)
28-29: Changelog entry is fine; confirm PR reference matches this change.This line credits PR 1802 but the change appears in PR 1798. Please confirm/correct the PR number to avoid confusion in release notes.
lib/react_on_rails/configuration.rb (1)
136-138: Coerce enforce_secure_server_bundles to boolean; normalize server_bundle_output_path.To avoid surprises from env strings (e.g., "true") and ensure absolute path consistency.
Apply this diff:
- self.server_bundle_output_path = server_bundle_output_path - self.enforce_secure_server_bundles = enforce_secure_server_bundles + self.server_bundle_output_path = server_bundle_output_path.present? ? + File.expand_path(server_bundle_output_path, Rails.root.to_s) : nil + self.enforce_secure_server_bundles = ReactOnRails::Utils.object_to_boolean(enforce_secure_server_bundles)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
CHANGELOG.md(2 hunks)docs/guides/configuration.md(1 hunks)lib/react_on_rails/configuration.rb(4 hunks)lib/react_on_rails/packs_generator.rb(1 hunks)lib/react_on_rails/utils.rb(2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
{**/*.rb,**/*.rake,**/*.gemspec,Gemfile,Rakefile}
📄 CodeRabbit inference engine (CLAUDE.md)
Before every commit/push, run
bundle exec rubocopand fix all violations; Ruby code must pass RuboCop with zero offenses
Files:
lib/react_on_rails/configuration.rblib/react_on_rails/packs_generator.rblib/react_on_rails/utils.rb
**/*.{js,jsx,ts,tsx,json,md,yml,yaml,css,scss,html,graphql,mdx}
📄 CodeRabbit inference engine (CLAUDE.md)
Let Prettier handle all formatting; never manually format code
Files:
CHANGELOG.mddocs/guides/configuration.md
🧠 Learnings (5)
📚 Learning: 2025-02-12T16:38:06.537Z
Learnt from: Romex91
PR: shakacode/react_on_rails#1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
Applied to files:
lib/react_on_rails/packs_generator.rb
📚 Learning: 2025-04-26T21:55:55.874Z
Learnt from: alexeyr-ci2
PR: shakacode/react_on_rails#1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.
Applied to files:
CHANGELOG.md
📚 Learning: 2025-09-16T08:01:11.146Z
Learnt from: justin808
PR: shakacode/react_on_rails#1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.
Applied to files:
lib/react_on_rails/utils.rb
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.
Applied to files:
lib/react_on_rails/utils.rb
📚 Learning: 2025-09-15T21:24:48.207Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.
Applied to files:
lib/react_on_rails/utils.rb
🧬 Code graph analysis (2)
lib/react_on_rails/packs_generator.rb (1)
lib/react_on_rails/configuration.rb (1)
configuration(16-59)
lib/react_on_rails/utils.rb (2)
lib/react_on_rails/packer_utils.rb (3)
using_packer?(5-7)bundle_js_uri_from_packer(62-78)packer_public_output_path(113-115)lib/react_on_rails/configuration.rb (1)
configuration(16-59)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: dummy-app-integration-tests (3.4, 22)
- GitHub Check: dummy-app-integration-tests (3.2, 20)
- GitHub Check: claude-review
- GitHub Check: rspec-package-tests (3.4, minimum)
- GitHub Check: rspec-package-tests (3.2, minimum)
- GitHub Check: rspec-package-tests (3.4, latest)
- GitHub Check: rspec-package-tests (3.2, latest)
- GitHub Check: build
🔇 Additional comments (10)
CHANGELOG.md (2)
30-33: RC section looks good.New 16.0.1-rc.2 section placement and heading format are consistent.
1661-1662: Compare links updated correctly.Unreleased now points to 16.0.1-rc.2…master. LGTM.
lib/react_on_rails/utils.rb (4)
71-83: Path resolution flow split (server vs client) is a good improvement.The top-level guard and legacy fallback are sensible. No issues here.
102-107: Server-bundle detection is correct and cached locally.Using a local config var is good; avoids repeated global access.
108-112: “secure” naming nit.The term “secure” is overloaded; consider “non-public” to avoid implying cryptographic guarantees.
122-150: Public fallbacks for server bundles should be gated; also log tried paths.
- With enforcement on, we should not include public locations.
- Emitting a warn with the list of tried paths will help users debug missing bundles.
Apply this diff:
def self.handle_missing_manifest_entry(bundle_name, is_server_bundle) - # When manifest lookup fails, try multiple fallback locations: - # 1. Environment-specific path (e.g., public/webpack/test) - # 2. Standard Shakapacker location (public/packs) - # 3. Generated assets path (for legacy setups) - fallback_locations = [ - File.join(ReactOnRails::PackerUtils.packer_public_output_path, bundle_name), - File.join("public", "packs", bundle_name), - File.join(generated_assets_full_path, bundle_name) - ].uniq + if is_server_bundle && ReactOnRails.configuration.enforce_secure_server_bundles + preferred_dir = ReactOnRails.configuration.server_bundle_output_path.presence || "ssr-generated" + path = File.expand_path(File.join(preferred_dir, bundle_name), Rails.root.to_s) + Rails.logger.warn("ReactOnRails: enforce_secure_server_bundles=true; returning secure path only: #{path}") + return path + end + + # When manifest lookup fails, try multiple fallback locations: + # 1. Environment-specific path (e.g., public/webpack/test) + # 2. Standard Shakapacker location (public/packs) + # 3. Generated assets path (for legacy setups) + fallback_locations = [ + File.join(ReactOnRails::PackerUtils.packer_public_output_path, bundle_name), + File.join("public", "packs", bundle_name), + File.join(generated_assets_full_path, bundle_name) + ].uniq - # Return the first location where the bundle file actually exists - fallback_locations.each do |path| - expanded_path = File.expand_path(path) - return expanded_path if File.exist?(expanded_path) - end + # Return the first location where the bundle file actually exists + if (existing = first_existing_path(fallback_locations)) + return existing + end + + Rails.logger.warn("ReactOnRails: bundle '#{bundle_name}' not found in manifest or fallbacks. Tried: " \ + "#{fallback_locations.join(', ')}") # If none exist, return appropriate default based on bundle type if is_server_bundle - # For server bundles, prefer secure location as final fallback - File.expand_path(File.join("ssr-generated", bundle_name)) + # For server bundles, prefer secure location as final fallback + File.expand_path(File.join( + ReactOnRails.configuration.server_bundle_output_path.presence || "ssr-generated", + bundle_name + ), Rails.root.to_s) else # For client bundles, use environment-specific path (original behavior) - File.expand_path(fallback_locations.first) + File.expand_path(fallback_locations.first, Rails.root.to_s) end endlib/react_on_rails/packs_generator.rb (1)
208-218: Guard logic mirrors generate flow; LGTM once nil-safety is fixed.Expected server bundle inclusion is conditioned on presence; consistent with the earlier guard.
Add a spec that sets server_bundle_js_file = nil and asserts no modification occurs.
lib/react_on_rails/configuration.rb (3)
55-58: Defaults added: OK.Defaults for new options are fine.
73-75: Expose new attrs: OK.Accessors added for both options; no issues.
91-91: Initialize signature updated: OK.No conflicts with callers detected.
docs/guides/configuration.md
Outdated
| # When enabled, enforces that server bundles are only loaded from secure, designated locations | ||
| # to prevent potential security risks from loading untrusted server-side code. | ||
| # Default is false for backward compatibility. | ||
| config.enforce_secure_server_bundles = false | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
enforce_secure_server_bundles is documented but not enforced in resolution logic.
Current utils still fall back to manifest/public paths for server bundles. If this flag is true, path resolution should refuse public locations. Please align implementation or adjust docs’ wording.
I can propose a small utils.rb patch to gate public fallbacks when enforcement is enabled (see utils.rb comments).
🤖 Prompt for AI Agents
In docs/guides/configuration.md around lines 137 to 141, the docs state
config.enforce_secure_server_bundles = false but the resolution logic still
falls back to manifest/public paths; when the flag is true the code must refuse
public locations. Update the resolution code (utils.rb) to read the config flag
and, when enforce_secure_server_bundles is true, prevent any fallback to
public/manifest paths for server bundle resolution (raise or return nil/error
instead), and add tests covering both true/false behaviors; alternatively, if
you prefer docs change, update the wording to reflect that current code will
still allow public fallbacks and the flag is only documented but not
implemented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
lib/react_on_rails/utils.rb(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
{**/*.rb,**/*.rake,**/*.gemspec,Gemfile,Rakefile}
📄 CodeRabbit inference engine (CLAUDE.md)
Before every commit/push, run
bundle exec rubocopand fix all violations; Ruby code must pass RuboCop with zero offenses
Files:
lib/react_on_rails/utils.rb
🧠 Learnings (4)
📚 Learning: 2025-04-26T21:55:55.874Z
Learnt from: alexeyr-ci2
PR: shakacode/react_on_rails#1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.
Applied to files:
lib/react_on_rails/utils.rb
📚 Learning: 2025-09-16T08:01:11.146Z
Learnt from: justin808
PR: shakacode/react_on_rails#1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.
Applied to files:
lib/react_on_rails/utils.rb
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.
Applied to files:
lib/react_on_rails/utils.rb
📚 Learning: 2025-09-15T21:24:48.207Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.
Applied to files:
lib/react_on_rails/utils.rb
🧬 Code graph analysis (1)
lib/react_on_rails/utils.rb (2)
lib/react_on_rails/packer_utils.rb (2)
bundle_js_uri_from_packer(62-78)packer_public_output_path(113-115)lib/react_on_rails/configuration.rb (1)
configuration(16-59)
🪛 GitHub Actions: Rspec test for gem
lib/react_on_rails/utils.rb
[error] 96-96: NoMethodError: undefined method `manifest' for nil:NilClass in bundle_js_file_path_with_packer
🔇 Additional comments (6)
lib/react_on_rails/utils.rb (6)
102-106: Cache configuration values to avoid repeated lookupsMultiple calls to
ReactOnRails.configurationcan be avoided by caching the values in local variables.Apply this diff to cache the configuration:
def self.server_bundle?(bundle_name) - config = ReactOnRails.configuration + @server_bundle_file ||= ReactOnRails.configuration.server_bundle_js_file + @rsc_bundle_file ||= ReactOnRails.configuration.rsc_bundle_js_file - bundle_name == config.server_bundle_js_file || - bundle_name == config.rsc_bundle_js_file + bundle_name == @server_bundle_file || bundle_name == @rsc_bundle_file end
134-137: Extract duplicated file existence check logicThe pattern of iterating through paths and returning the first existing one is duplicated between
try_secure_server_locationsandhandle_missing_manifest_entry.Add this helper method after line 147:
def self.first_existing_path(paths) paths.find { |path| File.exist?(File.expand_path(path)) } endThen update both methods:
# In try_secure_server_locations - secure_server_locations.each do |path| - expanded_path = File.expand_path(path) - return expanded_path if File.exist?(expanded_path) - end - nil + first_existing_path(secure_server_locations) # In handle_missing_manifest_entry - fallback_locations.each do |path| - expanded_path = File.expand_path(path) - return expanded_path if File.exist?(expanded_path) - end + existing_path = first_existing_path(fallback_locations) + return existing_path if existing_pathUpdate the private method declaration:
private_class_method :bundle_js_file_path_with_packer, :server_bundle?, :try_secure_server_locations, - :handle_missing_manifest_entry + :handle_missing_manifest_entry, :first_existing_path
148-149: Group private_class_method declarations with their method definitionsThe
private_class_methoddeclaration should follow Ruby conventions by being placed immediately after the method definitions, not grouped at the end.Move each
private_class_methoddeclaration immediately after its corresponding method definition for better readability and maintainability.
108-121: Include configured server_bundle_output_path and anchor paths to Rails.rootThe secure locations are hard-coded and use CWD expansion. They should include the configured
server_bundle_output_pathand be anchored toRails.rootfor consistency.Apply this diff to fix the path resolution:
def self.try_secure_server_locations(bundle_name) - secure_server_locations = [ - File.join("ssr-generated", bundle_name), - File.join("generated", "server-bundles", bundle_name) - ] - - secure_server_locations.each do |path| - expanded_path = File.expand_path(path) - return expanded_path if File.exist?(expanded_path) - end - - nil + config = ReactOnRails.configuration + secure_server_locations = [ + config.server_bundle_output_path.present? ? + Rails.root.join(config.server_bundle_output_path, bundle_name).to_s : nil, + Rails.root.join("ssr-generated", bundle_name).to_s, + Rails.root.join("generated", "server-bundles", bundle_name).to_s + ].compact + + secure_server_locations.find { |path| File.exist?(path) } end
88-92: Honor enforce_secure_server_bundles configuration for server bundlesWhen
enforce_secure_server_bundlesis enabled, server bundles should not fall back to public/manifest paths if secure locations are missing.Apply this diff to enforce the security constraint:
if is_server_bundle # NORMAL CASE for server bundles: Try secure non-public locations first secure_path = try_secure_server_locations(bundle_name) return secure_path if secure_path + + # When enforcement is enabled, don't fall back to public paths for server bundles + if ReactOnRails.configuration.enforce_secure_server_bundles + # Return the preferred secure location even if it doesn't exist yet + preferred_dir = ReactOnRails.configuration.server_bundle_output_path.presence || "ssr-generated" + return Rails.root.join(preferred_dir, bundle_name).to_s + end end
140-143: Clarify "secure location" wording and reference enforcement settingDocs show config.server_bundle_output_path can be set to "ssr-generated" and config.enforce_secure_server_bundles exists (default false), so "ssr-generated" is not inherently secure. Update the inline comment in lib/react_on_rails/utils.rb to either reference that enforce_secure_server_bundles must be enabled (and proper filesystem permissions applied) for the directory to be treated as secure, or remove "secure" and call it a "configured/fallback output directory". See docs/guides/configuration.md (around lines 134–141) and lib/react_on_rails/configuration.rb.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
spec/react_on_rails/test_helper/webpack_assets_status_checker_spec.rb (1)
64-64: Avoid leaky constant; usestub_constinstead of assigningPackerDirectly defining a constant in a
beforeblock leaks globally and requires RuboCop disables. Usestub_constfor per‑example isolation and drop the disables.Apply this diff:
- Packer = Shakapacker # rubocop:disable Lint/ConstantDefinitionInBlock, RSpec/LeakyConstantDeclaration + stub_const("Packer", Shakapacker)If other specs rely on a globally reassigned
Packer, mirror this change there to avoid order‑dependent failures.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
spec/react_on_rails/test_helper/webpack_assets_status_checker_spec.rb(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru}}
📄 CodeRabbit inference engine (CLAUDE.md)
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru}}: Before every commit/push, runbundle exec rubocopand fix all violations in Ruby code
Let RuboCop handle all Ruby code formatting; never manually format Ruby files
Files:
spec/react_on_rails/test_helper/webpack_assets_status_checker_spec.rb
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru,js,jsx,ts,tsx,json,yml,yaml,md,css,scss}}
📄 CodeRabbit inference engine (CLAUDE.md)
Ensure all committed files end with a trailing newline character
Files:
spec/react_on_rails/test_helper/webpack_assets_status_checker_spec.rb
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: dummy-app-integration-tests (3.2, 20)
- GitHub Check: dummy-app-integration-tests (3.4, 22)
- GitHub Check: rspec-package-tests (3.4, minimum)
- GitHub Check: rspec-package-tests (3.2, latest)
- GitHub Check: rspec-package-tests (3.4, latest)
- GitHub Check: rspec-package-tests (3.2, minimum)
- GitHub Check: claude-review
lib/react_on_rails/utils.rb
Outdated
| private_class_method :bundle_js_file_path_with_packer, :server_bundle?, :try_secure_server_locations, | ||
| :handle_missing_manifest_entry |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or even better, use private_class_method def bundle_js_file_path_with_packer ... etc.
lib/react_on_rails/utils.rb
Outdated
| # Priority order depends on bundle type: | ||
| # SERVER BUNDLES (normal case): Try secure non-public locations first, then manifest, then legacy | ||
| # CLIENT BUNDLES (normal case): Try manifest first, then fallback locations | ||
| if bundle_name == "manifest.json" || !ReactOnRails::PackerUtils.using_packer? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| if bundle_name == "manifest.json" || !ReactOnRails::PackerUtils.using_packer? | |
| if bundle_name == "manifest.json" |
using_packer? is always true now, right?
Code Review for PR #1798: Fix bundle path resolution and improve server bundle security🎯 Overall AssessmentThis PR makes significant improvements to bundle path resolution and enhances security for server-side bundles. The changes are well-structured and maintain backwards compatibility while addressing critical issues. ✅ Strengths
🔍 Code Quality ObservationsBundle Path Resolution LogicThe new priority-based fallback mechanism is well-designed:
Error HandlingThe addition of the safety check for empty
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
lib/react_on_rails/utils.rb (1)
142-149: Optional: Log tried locations on miss to aid debugging.When nothing is found, optionally log the ordered candidate list (behind
configuration.traceor in development) to speed up misconfiguration diagnosis.Would you like me to add a small trace log here that prints the candidate paths when no match is found?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
lib/react_on_rails/utils.rb(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru}}
📄 CodeRabbit inference engine (CLAUDE.md)
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru}}: Before every commit/push, runbundle exec rubocopand fix all violations in Ruby code
Let RuboCop handle all Ruby code formatting; never manually format Ruby files
Files:
lib/react_on_rails/utils.rb
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru,js,jsx,ts,tsx,json,yml,yaml,md,css,scss}}
📄 CodeRabbit inference engine (CLAUDE.md)
Ensure all committed files end with a trailing newline character
Files:
lib/react_on_rails/utils.rb
🧠 Learnings (3)
📚 Learning: 2025-09-16T08:01:11.146Z
Learnt from: justin808
PR: shakacode/react_on_rails#1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.
Applied to files:
lib/react_on_rails/utils.rb
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.
Applied to files:
lib/react_on_rails/utils.rb
📚 Learning: 2025-09-15T21:24:48.207Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.
Applied to files:
lib/react_on_rails/utils.rb
🧬 Code graph analysis (1)
lib/react_on_rails/utils.rb (2)
lib/react_on_rails/packer_utils.rb (3)
using_packer?(5-7)bundle_js_uri_from_packer(62-78)packer_public_output_path(113-115)lib/react_on_rails/configuration.rb (1)
configuration(16-59)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: claude-review
- GitHub Check: rspec-package-tests (3.2, minimum)
- GitHub Check: rspec-package-tests (3.4, latest)
- GitHub Check: rspec-package-tests (3.4, minimum)
- GitHub Check: build-dummy-app-webpack-test-bundles (3.4, 22)
- GitHub Check: rspec-package-tests (3.2, latest)
- GitHub Check: build
🔇 Additional comments (5)
lib/react_on_rails/utils.rb (5)
72-83: Manifest special-case preserved correctly.Keeping
manifest.jsonresolution pointed atgenerated_assets_full_pathretains legacy compatibility; the branch tobundle_js_file_path_with_packerfor all other bundles looks good.
151-152: Add helper to private list; consider colocating private declarations.Expose
first_existing_pathas private and optionally moveprivate_class_methodcloser to definitions for readability (or use inlineprivate_class_method def ...).Apply this diff:
-private_class_method :bundle_js_file_path_with_packer, :server_bundle?, :try_secure_server_locations, - :handle_missing_manifest_entry +private_class_method :bundle_js_file_path_with_packer, :server_bundle?, :try_secure_server_locations, + :handle_missing_manifest_entry, :first_existing_path
72-75: Clarify terminology: “secure” vs “non-public/private”.We use “secure” in comments and method names, but what we enforce is “non-public/private output paths.” Consider aligning naming/comments to “private (non-public) locations” for consistency across docs and code.
Also applies to: 111-115
85-103: Honor enforce_secure_server_bundles and guard nil packer to avoid NoMethodError.
- When
enforce_secure_server_bundlesis true and secure paths miss, we must not fall back to manifest/public locations for server bundles.- Tests have previously hit
NoMethodError: undefined method 'manifest' for nil:NilClass; add a guard by rescuingNoMethodErrorand falling back consistently.Apply this diff:
def self.bundle_js_file_path_with_packer(bundle_name) # Handle test scenarios where packer is mocked as unavailable return File.join(generated_assets_full_path, bundle_name) unless ReactOnRails::PackerUtils.using_packer? is_server_bundle = server_bundle?(bundle_name) if is_server_bundle # NORMAL CASE for server bundles: Try secure non-public locations first secure_path = try_secure_server_locations(bundle_name) - return secure_path if secure_path + return secure_path if secure_path + # Enforcement: do not fall back to manifest/public for server bundles + if ReactOnRails.configuration.enforce_secure_server_bundles + preferred_dir = ReactOnRails.configuration.server_bundle_output_path.presence || + Rails.root.join("ssr-generated").to_s + return File.expand_path(File.join(preferred_dir, bundle_name), Rails.root.to_s) + end end # For client bundles OR server bundle fallback: Try manifest lookup begin - ReactOnRails::PackerUtils.bundle_js_uri_from_packer(bundle_name) + ReactOnRails::PackerUtils.bundle_js_uri_from_packer(bundle_name) + rescue NoMethodError + # Guard for nil packer/manifest in tests or partial setups + handle_missing_manifest_entry(bundle_name, is_server_bundle) rescue Shakapacker::Manifest::MissingEntryError handle_missing_manifest_entry(bundle_name, is_server_bundle) end end
111-123: Include configured server_bundle_output_path and anchor to Rails.root; DRY existence checks.
- Add
server_bundle_output_pathas the preferred secure location.- Anchor all paths to
Rails.rootto avoid CWD surprises.- Reuse a small helper to pick the first existing path.
Apply this diff:
def self.try_secure_server_locations(bundle_name) - secure_server_locations = [ - File.join("ssr-generated", bundle_name), - File.join("generated", "server-bundles", bundle_name) - ] - - secure_server_locations.each do |path| - expanded_path = File.expand_path(path) - return expanded_path if File.exist?(expanded_path) - end - - nil + preferred_dir = ReactOnRails.configuration.server_bundle_output_path + candidates = [ + (preferred_dir.present? ? File.join(preferred_dir, bundle_name) : nil), + Rails.root.join("ssr-generated", bundle_name).to_s, + Rails.root.join("generated", "server-bundles", bundle_name).to_s + ].compact + first_existing_path(candidates) end + +def self.first_existing_path(paths) + paths + .map { |p| File.expand_path(p, Rails.root.to_s) } + .find { |p| File.exist?(p) } +end
lib/react_on_rails/utils.rb
Outdated
| # Build candidate locations, including configured output path | ||
| root_path = Rails.root || "." | ||
| candidates = [ | ||
| File.join(root_path, "ssr-generated", bundle_name), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@claude, I'm not sure if this is a good idea. Why do we use hard-coded paths? Wouldn't it be better to set ssr-generated as the default value for the server_bundle_output_path config instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Claude finished @AbanoubGhadban's task —— View job
Analysis of hard-coded paths vs configuration defaults
Todo List:
- Read current implementation in utils.rb
- Check configuration defaults
- Analyze the design decision
- Provide recommendation
You raise an excellent point! After reviewing the code, I can see the design trade-offs here.
Current Implementation
The current approach has:
- Hard-coded fallback paths in
try_private_server_locations:"ssr-generated"and"generated/server-bundles" - Optional configuration via
server_bundle_output_path(defaults tonil) - When
server_bundle_output_pathis configured, it's added to the candidates list (line 130)
Your Suggestion: Set "ssr-generated" as default
This would mean:
# In configuration.rb
server_bundle_output_path: "ssr-generated" # Instead of nilAnalysis
Pros of your approach:
- More explicit - The default is visible in the configuration
- Single source of truth - No hard-coded paths scattered in the code
- Cleaner logic - The utils.rb code would only check
config.server_bundle_output_path - Easier to override - Users can clearly see what the default is and change it
Pros of current approach:
- Multiple fallback paths - It tries both
ssr-generatedandgenerated/server-bundles - Backwards compatibility - Supports users who might have bundles in either location
- Graceful degradation - Works even if config is nil
My Recommendation
I agree with your suggestion! Setting "ssr-generated" as the default for server_bundle_output_path would be cleaner. Here's how it could be improved:
# configuration.rb
server_bundle_output_path: "ssr-generated" # Default value
# utils.rb - simplified logic
private_class_method def self.try_private_server_locations(bundle_name)
config = ReactOnRails.configuration
root_path = Rails.root || "."
# Primary location from configuration
candidates = [
File.join(root_path, config.server_bundle_output_path, bundle_name)
]
# Add legacy fallback for backwards compatibility
candidates << File.join(root_path, "generated", "server-bundles", bundle_name)
find_first_existing_path(candidates)
endThis would:
- Make the default explicit and configurable
- Remove the hard-coded "ssr-generated" path
- Keep one legacy fallback for backwards compatibility
- Simplify the logic
The hard-coded paths do feel like a code smell when there's already a configuration option for this purpose.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@claude, implement it then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Claude finished @AbanoubGhadban's task —— View job
✅ Completed: Configuration-based server bundle output path
Successfully implemented the configuration-based approach for server bundle output paths:
Changes Made:
- Set default value:
server_bundle_output_pathnow defaults to"ssr-generated"in configuration.rb - Removed hard-coded paths: Refactored
try_private_server_locationsto use configuration instead of hard-coded "ssr-generated" path - Consistent configuration usage: Updated
handle_missing_manifest_entryto use configured path for server bundle fallbacks - Maintained backwards compatibility: Kept "generated/server-bundles" as a legacy fallback
- Enhanced security enforcement: When
enforce_secure_server_bundlesis true, server bundles only use configured private locations
Benefits:
- Single source of truth: Default is now visible and configurable in one place
- Cleaner architecture: No more scattered hard-coded paths
- Better maintainability: Users can easily see and override the default
- Consistent behavior: All server bundle path resolution uses the same configuration
Commit: 1b750e9
lib/react_on_rails/utils.rb
Outdated
| # 3. Generated assets path (for legacy setups) | ||
| fallback_locations = [ | ||
| File.join(ReactOnRails::PackerUtils.packer_public_output_path, bundle_name), | ||
| File.join("public", "packs", bundle_name), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@claude, Why do we use a hard-coded path? If the packer uses the default output path, public/packs, the function packer_public_output_path will already return it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Claude finished @AbanoubGhadban's task —— View job
Analysis of the hard-coded "public/packs" path
You're absolutely right to question this! After analyzing the code, I can see why this appears redundant but it actually serves a specific purpose.
Current Implementation
Looking at line 143 in utils.rb, the fallback locations are:
ReactOnRails::PackerUtils.packer_public_output_path- Environment-specific path (e.g.,public/webpack/test,public/webpack/development)- Hard-coded
"public/packs"- Standard Shakapacker default location generated_assets_full_path- Legacy setup path
Why the Hard-coded Path Exists
The hard-coded "public/packs" serves as a fallback for the default Shakapacker location when the environment-specific path doesn't exist. Here's why it's needed:
-
packer_public_output_pathreturns environment-specific paths: Looking at the dummy app'sshakapacker.yml:- Development:
public/webpack/development - Test:
public/webpack/test - Production:
public/webpack/production
- Development:
-
Default Shakapacker uses
public/packs: The Shakapacker template default ispublic_output_path: packs, which resolves topublic/packs. -
The fallback handles configuration mismatches: If someone has bundles in the default
public/packslocation but their current environment config points elsewhere, this fallback can still find them.
Is It Redundant?
It's partially redundant in setups where:
- The
shakapacker.ymlusespublic/packsfor all environments - In this case,
packer_public_output_pathwould returnpublic/packsand the hard-coded path would duplicate it
However, it's necessary for:
- Projects with environment-specific paths (like the dummy app)
- Migration scenarios where bundles might exist in both locations
- Default Shakapacker setups that haven't customized the output path
Potential Improvement
The .uniq call on line 145 handles the duplication when both paths are the same, but you're right that this could be cleaner. A better approach might be:
fallback_locations = [
ReactOnRails::PackerUtils.packer_public_output_path,
("public/packs" unless ReactOnRails::PackerUtils.packer_public_output_path.end_with?("/packs")),
generated_assets_full_path
].compact.map { |path| File.join(path, bundle_name) }.uniqThis would only add the hard-coded default if it's different from the configured path.
Summary
The hard-coded path isn't entirely redundant - it provides a safety net for the standard Shakapacker default location when environment-specific paths are configured differently. However, the implementation could be optimized to avoid unnecessary duplication in the common case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@claude, why would we have React on Rails revert to using development or production bundles when test bundles are not built? Are you certain that React on Rails v14.1.0 exhibited this behavior? I couldn't find any evidence of it in the codebase. Additionally, even if you intend to use development or production bundles in the absence of test bundles, you should avoid hard-coded paths since users can specify different paths for their development and production bundles.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Claude finished @AbanoubGhadban's task —— View job
Todo List:
- Read current utils.rb implementation
- Check git history for v14.1.0 behavior
- Analyze the fallback logic rationale
- Provide recommendation
Analysis of the fallback behavior
After reviewing the code history and the original fix for issue #1797, I can provide clarity on your questions:
1. Historical Context
The fallback logic with "public/packs" was introduced in commit 077a610 to fix issue #1797. This wasn't specifically from v14.1.0 - it's a recent fix that addressed a real production issue where server rendering failed in test environments.
2. The Original Problem
Issue #1797 occurred when:
- Bundles were built and placed in the standard Shakapacker location (
public/packs) - The manifest lookup pointed to an environment-specific directory (e.g.,
public/webpack/test) - The test environment bundles weren't built (only production bundles existed)
- Server rendering would fail with
Errno::ENOENT
3. Why the Fallback Logic Exists
The fallback chain serves specific scenarios:
packer_public_output_path- Environment-specific path (e.g.,public/webpack/test)"public/packs"- Standard Shakapacker default locationgenerated_assets_full_path- Legacy React on Rails setups
This isn't about "reverting to development/production bundles when test bundles aren't built" - it's about finding bundles wherever they actually exist, regardless of environment configuration.
4. You're Right About the Hard-coded Path
You make an excellent point about avoiding hard-coded paths. The current implementation has several issues:
- Assumes standard Shakapacker defaults - Users might configure different paths
- Not anchored to Rails.root - Could cause path resolution issues
- Duplicates logic -
packer_public_output_pathmight already returnpublic/packs
My Recommendation
The fallback logic should be improved to:
private_class_method def self.handle_missing_manifest_entry(bundle_name, is_server_bundle)
# Build fallback locations dynamically based on actual configuration
fallback_locations = []
# 1. Current environment's configured path
fallback_locations << File.join(ReactOnRails::PackerUtils.packer_public_output_path, bundle_name)
# 2. Default Shakapacker path ONLY if different from above
default_path = Rails.root.join("public", "packs", bundle_name).to_s
fallback_locations << default_path unless fallback_locations.include?(default_path)
# 3. Legacy path for backward compatibility
fallback_locations << File.join(generated_assets_full_path, bundle_name)
# Find first existing path
existing_path = find_first_existing_path(fallback_locations.uniq)
return existing_path if existing_path
# Default fallback if nothing exists
if is_server_bundle
# Prefer configured/private location for server bundles
preferred_dir = ReactOnRails.configuration.server_bundle_output_path.presence || "ssr-generated"
Rails.root.join(preferred_dir, bundle_name).to_s
else
# Use first fallback location for client bundles
fallback_locations.first
end
endThis would:
- Avoid hard-coding when unnecessary
- Properly anchor paths to Rails.root
- Respect user configuration
- Maintain the fallback behavior needed for issue REGRESSION: Server bundle path resolution fails in test environment (v16.0.1.rc.2) #1797
The core issue is that the current implementation makes assumptions about user configuration instead of dynamically building fallbacks based on actual settings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@claude, I don't think this logic makes any sense. If a user forgets to build test bundles, are you suggesting that we should use the development or production bundle instead? Why do you assume this is the correct behavior, or that the user is okay with it? Since we are already in the test environment, it’s better to fail early and help the user identify the problem. Additionally, I doubt that the code you provided will work as I described before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Claude finished @AbanoubGhadban's task —— View job
Analysis Complete
You raise an excellent point that I strongly agree with! The current fallback logic is problematic and could mask configuration issues.
Todo List:
- Read current implementation in utils.rb
- Review the original issue REGRESSION: Server bundle path resolution fails in test environment (v16.0.1.rc.2) #1797 to understand why this fallback was added
- Analyze whether the fallback behavior makes sense
- Provide recommendation on the fallback logic
The Problem with the Current Fallback Logic
You're absolutely right - falling back to development or production bundles when test bundles are missing is a bad idea. Here's why:
1. It Hides Configuration Problems
If a user forgets to build test bundles, the application might appear to work using stale dev/prod bundles, but this could lead to:
- Tests passing with incorrect code
- False positives in CI/CD pipelines
- Confusion when test results don't match expectations
2. Violates the Principle of Least Surprise
When running in test environment, developers expect test bundles. Silently falling back to other bundles is unexpected behavior that could lead to hours of debugging.
3. The Original Issue #1797 Was Different
Looking at the fix for #1797 (commit 077a610), the problem was that bundles existed in public/packs but the manifest pointed to an environment-specific directory like public/webpack/test. The fallback was meant to find existing bundles in alternate locations, not substitute bundles from different environments.
My Recommendation
The fallback logic should be removed or significantly restricted:
private_class_method def self.handle_missing_manifest_entry(bundle_name, is_server_bundle)
# For test environment, fail fast to help developers identify missing bundles
if Rails.env.test?
raise ReactOnRails::Error,
"Bundle '#{bundle_name}' not found in manifest. " \
"Please run 'bin/shakapacker' or your build command to generate test bundles."
end
# For other environments, limited fallback might be acceptable
# but should log warnings
Rails.logger.warn("Bundle '#{bundle_name}' not found in manifest, attempting fallback...")
# ... limited fallback logic for production/development only
endAlternative Approach
If fallback is absolutely necessary for backward compatibility, it should:
- Only check the SAME environment's alternate locations (not cross-environment)
- Emit clear warnings when fallback is used
- Be configurable so teams can disable it entirely
# Only look in alternate locations for the SAME environment
fallback_locations = []
if Rails.env.test?
# Only test-specific locations, never dev/prod bundles
fallback_locations = [
File.join("public", "webpack", "test", bundle_name),
File.join("public", "packs-test", bundle_name)
]
elsif Rails.env.production?
# Only production-specific locations
fallback_locations = [
File.join("public", "packs", bundle_name)
]
endConclusion
You're correct that the current implementation doesn't make sense. The fallback to public/packs in test environment means we could be using production bundles for tests, which is dangerous and misleading. The code should fail fast in test environments to help developers identify and fix missing bundles rather than silently using incorrect ones.
The fix for #1797 went too far - it should have been more targeted to handle alternate locations within the same environment, not cross-environment fallbacks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Claude finished @AbanoubGhadban's task —— View job
Analysis of issue #1797 and v14.x bundle path resolution
Todo List:
- Analyze v14.x original code for bundle path resolution
- Understand the issue REGRESSION: Server bundle path resolution fails in test environment (v16.0.1.rc.2) #1797 and what was broken
- Write a comprehensive response to Abanoub's question
Your Question is Valid!
You're absolutely correct to question this. After reviewing the code history, v14.1.0 did NOT have the fallback behavior that was added in the fix for #1797.
What the Original v14.x Code Did
Looking at the code before the #1797 fix (commit 077a610~1), the original bundle path resolution was simpler:
def self.bundle_js_file_path(bundle_name)
if ReactOnRails::PackerUtils.using_packer? && bundle_name != "manifest.json"
begin
ReactOnRails::PackerUtils.bundle_js_uri_from_packer(bundle_name)
rescue Object.const_get(
ReactOnRails::PackerUtils.packer_type.capitalize
)::Manifest::MissingEntryError
# ONLY fell back to the environment-specific path!
File.expand_path(
File.join(ReactOnRails::PackerUtils.packer_public_output_path,
bundle_name)
)
end
else
File.join(generated_assets_full_path, bundle_name)
end
endKey point: When the manifest lookup failed, it would ONLY try ONE fallback location:
ReactOnRails::PackerUtils.packer_public_output_path(e.g.,public/webpack/test)
It did NOT check:
public/packs(the standard Shakapacker location)- Multiple fallback locations
- Whether files actually existed
What Issue #1797 Actually Was
The commit message for #1797 describes it as:
"Fixes issue where server rendering failed in test environments when bundles were in standard Shakapacker location but manifest lookup pointed to environment-specific directory."
This suggests the problem was:
- Bundles were built and placed in
public/packs(standard location) - The manifest was configured to look in
public/webpack/test(environment-specific) - When manifest lookup failed, it would return
public/webpack/test/bundle.js - But that file didn't exist (bundles were in
public/packs) - Server rendering would fail with
Errno::ENOENT
My Assessment
Issue #1797 appears to be a real issue, but it's more about a configuration mismatch scenario than a regression from v14.1.0. The fix added NEW fallback behavior that didn't exist before to handle this mismatch more gracefully.
However, you raise an excellent point: the current fallback to public/packs doesn't make complete sense because:
- If we're in test environment and looking for test bundles, falling back to production bundles in
public/packsis dangerous - It masks configuration problems instead of failing fast
- It could lead to tests passing with wrong bundles
Recommendation
The fallback logic should be more targeted:
- Either fail fast with a helpful error message
- Or only fall back within the same environment (not cross-environment)
- Make the fallback behavior configurable so teams can opt-in/out
Your instinct to question this is spot on - the current implementation goes too far in trying to be helpful and could hide real problems.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree - fail fast!
lib/react_on_rails/utils.rb
Outdated
| # 1. Environment-specific path (e.g., public/webpack/test) | ||
| # 2. Standard Shakapacker location (public/packs) | ||
| # 3. Generated assets path (for legacy setups) | ||
| fallback_locations = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@claude If enforce_secure_server_bundles? is true, it shouldn't fallback to these locations. Instead, it should only fallback to File.join(root_path, config.server_bundle_output_path, bundle_name), right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Claude finished @AbanoubGhadban's task —— View job
Analysis of enforce_secure_server_bundles behavior
Todo List:
- Read current implementation in utils.rb
- Check configuration.rb for enforce_secure_server_bundles
- Analyze the fallback logic and security intent
- Provide clear answer about expected behavior
You're absolutely correct! When enforce_secure_server_bundles is true, server bundles should not fall back to public locations like public/packs or public/webpack/*.
Current Implementation Issue
Looking at the code in lib/react_on_rails/utils.rb lines 136-159, the handle_missing_manifest_entry method currently falls back to public locations for all bundles, including server bundles:
fallback_locations = [
File.join(ReactOnRails::PackerUtils.packer_public_output_path, bundle_name), # public/webpack/test
File.join("public", "packs", bundle_name), # public/packs
File.join(generated_assets_full_path, bundle_name) # generated assets
].uniqThis defeats the purpose of enforce_secure_server_bundles when server bundles can't be found in private locations.
What Should Happen
When enforce_secure_server_bundles is true and we're handling a server bundle:
- Should NOT check public locations (
public/packs,public/webpack/*) - Should ONLY fall back to the configured
server_bundle_output_pathor default private location (ssr-generated)
The Fix
The handle_missing_manifest_entry method should respect the enforcement flag:
private_class_method def self.handle_missing_manifest_entry(bundle_name, is_server_bundle)
if is_server_bundle && enforce_secure_server_bundles?
# When enforcement is on, ONLY use private locations for server bundles
config = ReactOnRails.configuration
root_path = Rails.root || "."
preferred_dir = config.server_bundle_output_path.presence || "ssr-generated"
return File.expand_path(File.join(root_path, preferred_dir, bundle_name))
end
# Current fallback logic for client bundles or when enforcement is disabled
fallback_locations = [
File.join(ReactOnRails::PackerUtils.packer_public_output_path, bundle_name),
File.join("public", "packs", bundle_name),
File.join(generated_assets_full_path, bundle_name)
].uniq
# ... rest of the method
endThis ensures that when enforce_secure_server_bundles is enabled, server bundles will never be served from publicly accessible directories, maintaining the security boundary that the configuration option is meant to enforce.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (4)
spec/react_on_rails/utils_spec.rb (2)
148-172: Add a case where both env-specific and standard paths exist to verify priority.Right now you test “standard only” and “none exist.” Add “both exist” to assert env-specific is preferred, matching
handle_missing_manifest_entryorder.Apply this diff:
context "when file not in manifest and fallback to standard location" do before do mock_missing_manifest_entry("webpack-bundle.js") end let(:standard_path) { File.expand_path(File.join("public", "packs", "webpack-bundle.js")) } let(:env_specific_path) { File.join(packer_public_output_path, "webpack-bundle.js") } + it "prefers environment-specific path when both locations exist" do + allow(File).to receive(:exist?).and_call_original + allow(File).to receive(:exist?).with(File.expand_path(env_specific_path)).and_return(true) + allow(File).to receive(:exist?).with(standard_path).and_return(true) + + result = described_class.bundle_js_file_path("webpack-bundle.js") + expect(result).to eq(File.expand_path(env_specific_path)) + end
217-238: Mirror the enforcement test for RSC bundles.RSC should follow the same secure-only behavior when enforced.
Apply this diff:
context "with RSC bundle file not in manifest" do let(:rsc_bundle_name) { "rsc-bundle.js" } let(:ssr_generated_path) { File.expand_path(File.join("ssr-generated", rsc_bundle_name)) } + let(:public_packs_path) { File.expand_path(File.join("public", "packs", rsc_bundle_name)) } before do mock_missing_manifest_entry(rsc_bundle_name) allow(ReactOnRails).to receive_message_chain("configuration.rsc_bundle_js_file") .and_return(rsc_bundle_name) allow(ReactOnRails).to receive_message_chain("configuration.enforce_secure_server_bundles") .and_return(false) allow(ReactOnRails).to receive_message_chain("configuration.server_bundle_output_path") .and_return("ssr-generated") end it "treats RSC bundles as server bundles and tries secure locations first" do allow(File).to receive(:exist?).and_call_original allow(File).to receive(:exist?).with(ssr_generated_path).and_return(true) result = described_class.bundle_js_file_path(rsc_bundle_name) expect(result).to eq(ssr_generated_path) end + + it "does not fall back to public when enforcement is true" do + allow(ReactOnRails).to receive_message_chain("configuration.enforce_secure_server_bundles") + .and_return(true) + allow(File).to receive(:exist?).and_call_original + allow(File).to receive(:exist?).with(ssr_generated_path).and_return(false) + allow(File).to receive(:exist?).with(public_packs_path).and_return(true) + + result = described_class.bundle_js_file_path(rsc_bundle_name) + expect(result).to eq(ssr_generated_path) + endlib/react_on_rails/utils.rb (2)
118-132: Anchor candidates with Rails.root for determinism.You already join
root_pathhere—good. Consider usingRails.root.join(...).to_sfor readability and to avoid doubleexpand_pathlater, but not required.
167-173: Make existence checks Rails.root-relative.
File.expand_path(path, Rails.root.to_s)avoids dependence on process CWD for relative paths returned by configuration.Apply this diff:
- paths.each do |path| - expanded_path = File.expand_path(path) + paths.each do |path| + expanded_path = File.expand_path(path, Rails.root.to_s) return expanded_path if File.exist?(expanded_path) end
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
lib/react_on_rails/configuration.rb(4 hunks)lib/react_on_rails/utils.rb(1 hunks)spec/react_on_rails/utils_spec.rb(8 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- lib/react_on_rails/configuration.rb
🧰 Additional context used
📓 Path-based instructions (2)
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru}}
📄 CodeRabbit inference engine (CLAUDE.md)
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru}}: Before every commit/push, runbundle exec rubocopand fix all violations in Ruby code
Let RuboCop handle all Ruby code formatting; never manually format Ruby files
Files:
spec/react_on_rails/utils_spec.rblib/react_on_rails/utils.rb
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru,js,jsx,ts,tsx,json,yml,yaml,md,css,scss}}
📄 CodeRabbit inference engine (CLAUDE.md)
Ensure all committed files end with a trailing newline character
Files:
spec/react_on_rails/utils_spec.rblib/react_on_rails/utils.rb
🧠 Learnings (5)
📚 Learning: 2025-04-26T21:55:55.874Z
Learnt from: alexeyr-ci2
PR: shakacode/react_on_rails#1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.
Applied to files:
spec/react_on_rails/utils_spec.rblib/react_on_rails/utils.rb
📚 Learning: 2025-09-16T08:01:11.146Z
Learnt from: justin808
PR: shakacode/react_on_rails#1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.
Applied to files:
lib/react_on_rails/utils.rb
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.
Applied to files:
lib/react_on_rails/utils.rb
📚 Learning: 2025-09-15T21:24:48.207Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.
Applied to files:
lib/react_on_rails/utils.rb
📚 Learning: 2025-02-12T16:38:06.537Z
Learnt from: Romex91
PR: shakacode/react_on_rails#1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
Applied to files:
lib/react_on_rails/utils.rb
🧬 Code graph analysis (2)
spec/react_on_rails/utils_spec.rb (2)
lib/react_on_rails/packer_utils.rb (1)
packer_public_output_path(113-115)lib/react_on_rails/utils.rb (3)
bundle_js_file_path(71-83)server_bundle_js_file_path(175-180)rsc_bundle_js_file_path(182-187)
lib/react_on_rails/utils.rb (2)
lib/react_on_rails/packer_utils.rb (3)
using_packer?(5-7)bundle_js_uri_from_packer(62-78)packer_public_output_path(113-115)lib/react_on_rails/configuration.rb (1)
configuration(16-59)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: dummy-app-integration-tests (3.2, 20)
- GitHub Check: dummy-app-integration-tests (3.4, 22)
- GitHub Check: build
- GitHub Check: rspec-package-tests (3.4, latest)
- GitHub Check: rspec-package-tests (3.2, minimum)
- GitHub Check: rspec-package-tests (3.2, latest)
- GitHub Check: rspec-package-tests (3.4, minimum)
🔇 Additional comments (9)
spec/react_on_rails/utils_spec.rb (6)
53-59: Good switch to explicit Shakapacker doubles.Using
instance_double(Shakapacker::Manifest)makes the intent clear and tightens the spec.
61-65: Correct error type for missing manifest entry.Raising
Shakapacker::Manifest::MissingEntryErroraligns with the implementation.
79-83: Nice: cover new config flags in test scaffolding.Mocking
enforce_secure_server_bundlesandserver_bundle_output_pathensures the resolver behavior is testable across configurations.
110-116: Prevents false positives in server-bundle detection.Stubbing server/RSC bundle names avoids misclassification in client-bundle tests. LGTM.
384-392: Covers RSC secure default.Asserts the default to
ssr-generated/for RSC when missing from manifest. Looks good.
656-661: Good use ofinstance_doublefor DevServer.Prevents accidental reliance on non-existent methods and matches the real API.
lib/react_on_rails/utils.rb (3)
71-83: Manifest special-case preserved and documented.Keeping
"manifest.json"ongenerated_assets_full_pathmaintains backward compatibility. LGTM.
85-106: Correct flow: secure-first for server bundles; guarded packer usage.Early private-location check +
using_packer?guard addresses prior nil‑packer crashes and enforces security intent. Nice.
133-166: Harden fallback path construction and add optional diagnostics.Two small tweaks:
- Use
Rails.rootas the base forexpand_pathto avoid relying on CWD.- (Optional) Log attempted candidates when falling back, to aid debugging regressions like #1797.
Apply this diff:
- existing_path = find_first_existing_path(fallback_locations) + existing_path = find_first_existing_path(fallback_locations) return existing_path if existing_path # If none exist, return appropriate default based on bundle type if is_server_bundle # For server bundles, use configured private location as final fallback - File.expand_path(File.join(root_path, config.server_bundle_output_path, bundle_name)) + File.expand_path(File.join(root_path, config.server_bundle_output_path, bundle_name), root_path) else # For client bundles, use environment-specific path (original behavior) - File.expand_path(fallback_locations.first) + File.expand_path(fallback_locations.first, root_path) end(Optional diagnostics; guarded behind development/trace to avoid noise):
- existing_path = find_first_existing_path(fallback_locations) + existing_path = find_first_existing_path(fallback_locations) return existing_path if existing_path + if Rails.env.development? || ReactOnRails.configuration.trace + Rails.logger.warn( + "[ReactOnRails] Missing manifest entry for '#{bundle_name}'. " \ + "Tried: #{fallback_locations.map { |p| File.expand_path(p, root_path) }.join(', ')}" + ) + end
|
@claude How can I configure Shakapacker in a React on Rails project to output the client bundle to the public directory while placing the server bundle in a secure directory? |
|
Claude finished @AbanoubGhadban's task —— View job Configuring Shakapacker for Separate Client and Server Bundle Directories in React on RailsTodo List:
AnswerGreat question! With the improvements in this PR, React on Rails now provides excellent support for separating client and server bundles into different directories. Here's how to configure it: 1. Configure React on Rails (Recommended Approach)The PR introduces new configuration options specifically for this purpose: # config/initializers/react_on_rails.rb
ReactOnRails.configure do |config|
# Server bundle configuration
config.server_bundle_js_file = "server-bundle.js"
# Configure where server bundles should be output (new in this PR)
# Default is "ssr-generated" - a secure, non-public directory
config.server_bundle_output_path = "ssr-generated"
# Enforce that server bundles only load from secure locations
config.enforce_secure_server_bundles = true
# Other configurations...
end2. Configure Shakapacker's Webpack SetupIn your Webpack configuration, you need to configure different output paths for client and server bundles: Client Bundle Configuration (
|
|
@claude, let's update the implementation as follows:
|
|
Claude finished @AbanoubGhadban's task —— View job ✅ Bundle path resolution updates completedSummary of ChangesSuccessfully implemented the configuration-based approach for server bundle path resolution as requested: 🔧 Simplified Bundle Resolution (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (4)
lib/react_on_rails/configuration.rb (1)
203-222: Apply RuboCop suggested guard clause fix.The linter suggests using a guard clause for better readability.
Apply this diff to fix the RuboCop violation:
- if server_output_path.start_with?(public_path) - raise ReactOnRails::Error, "enforce_secure_server_bundles is set to true, but " \ - "server_bundle_output_path (#{server_bundle_output_path}) is inside " \ - "the public directory. Please set it to a directory outside of public." - end + return unless server_output_path.start_with?(public_path) + + raise ReactOnRails::Error, "enforce_secure_server_bundles is set to true, but " \ + "server_bundle_output_path (#{server_bundle_output_path}) is inside " \ + "the public directory. Please set it to a directory outside of public."lib/react_on_rails/utils.rb (2)
106-110: Consider caching configuration access for performance.The configuration is accessed multiple times. Consider caching it in a local variable to reduce repeated method calls.
Apply this diff to optimize configuration access:
private_class_method def self.server_bundle?(bundle_name) config = ReactOnRails.configuration bundle_name == config.server_bundle_js_file || - bundle_name == config.rsc_bundle_js_file + bundle_name == config.rsc_bundle_js_file end
136-136: Fix extra blank line detected by linter.The linter detected an extra blank line that should be removed.
Apply this diff to fix the formatting:
- def self.server_bundle_js_file_pathlib/generators/react_on_rails/base_generator.rb (1)
140-150: Fix string concatenation lint warning.The linter suggests using string interpolation instead of concatenation for better performance and readability.
Apply this diff to fix the string concatenation:
- lines.join("\n") + "\n" + "#{lines.join("\n")}\n"
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
lib/generators/react_on_rails/base_generator.rb(1 hunks)lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt(1 hunks)lib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt(1 hunks)lib/react_on_rails/configuration.rb(6 hunks)lib/react_on_rails/utils.rb(1 hunks)spec/react_on_rails/configuration_spec.rb(1 hunks)spec/react_on_rails/utils_spec.rb(8 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru}}
📄 CodeRabbit inference engine (CLAUDE.md)
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru}}: Before every commit/push, runbundle exec rubocopand fix all violations in Ruby code
Let RuboCop handle all Ruby code formatting; never manually format Ruby files
Files:
spec/react_on_rails/configuration_spec.rblib/generators/react_on_rails/base_generator.rblib/react_on_rails/configuration.rbspec/react_on_rails/utils_spec.rblib/react_on_rails/utils.rb
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru,js,jsx,ts,tsx,json,yml,yaml,md,css,scss}}
📄 CodeRabbit inference engine (CLAUDE.md)
Ensure all committed files end with a trailing newline character
Files:
spec/react_on_rails/configuration_spec.rblib/generators/react_on_rails/base_generator.rblib/react_on_rails/configuration.rbspec/react_on_rails/utils_spec.rblib/react_on_rails/utils.rb
🧠 Learnings (5)
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.
Applied to files:
spec/react_on_rails/configuration_spec.rblib/react_on_rails/configuration.rblib/react_on_rails/utils.rb
📚 Learning: 2025-02-12T16:38:06.537Z
Learnt from: Romex91
PR: shakacode/react_on_rails#1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
Applied to files:
lib/generators/react_on_rails/base_generator.rblib/react_on_rails/utils.rblib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt
📚 Learning: 2025-04-26T21:55:55.874Z
Learnt from: alexeyr-ci2
PR: shakacode/react_on_rails#1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.
Applied to files:
spec/react_on_rails/utils_spec.rblib/react_on_rails/utils.rblib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt
📚 Learning: 2025-09-16T08:01:11.146Z
Learnt from: justin808
PR: shakacode/react_on_rails#1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.
Applied to files:
lib/react_on_rails/utils.rblib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt
📚 Learning: 2025-09-15T21:24:48.207Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.
Applied to files:
lib/react_on_rails/utils.rb
🧬 Code graph analysis (3)
spec/react_on_rails/configuration_spec.rb (1)
lib/react_on_rails/configuration.rb (1)
configure(6-9)
spec/react_on_rails/utils_spec.rb (2)
lib/react_on_rails/packer_utils.rb (1)
packer_public_output_path(113-115)lib/react_on_rails/utils.rb (3)
bundle_js_file_path(71-83)server_bundle_js_file_path(137-142)rsc_bundle_js_file_path(144-149)
lib/react_on_rails/utils.rb (2)
lib/react_on_rails/packer_utils.rb (3)
using_packer?(5-7)bundle_js_uri_from_packer(62-78)packer_public_output_path(113-115)lib/react_on_rails/configuration.rb (1)
configuration(16-59)
🪛 GitHub Actions: Lint JS and Ruby
lib/generators/react_on_rails/base_generator.rb
[error] 149-149: Style/StringConcatenation: Prefer string interpolation to string concatenation. lines.join("\n") + "\n"
lib/react_on_rails/configuration.rb
[error] 217-217: Style/GuardClause: Use a guard clause (return unless server_output_path.start_with?(public_path)) instead of wrapping the code inside a conditional expression.
lib/react_on_rails/utils.rb
[error] 138-138: Layout/EmptyLines: Extra blank line detected.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: dummy-app-integration-tests (3.2, 20)
- GitHub Check: dummy-app-integration-tests (3.4, 22)
- GitHub Check: claude-review
- GitHub Check: rspec-package-tests (3.4, latest)
- GitHub Check: examples (3.2, minimum)
- GitHub Check: examples (3.4, latest)
- GitHub Check: rspec-package-tests (3.2, latest)
- GitHub Check: rspec-package-tests (3.2, minimum)
- GitHub Check: rspec-package-tests (3.4, minimum)
🔇 Additional comments (19)
lib/react_on_rails/configuration.rb (4)
55-57: LGTM! New configuration options added successfully.The new configuration options
server_bundle_output_pathandenforce_secure_server_bundlesare properly integrated into the configuration system with appropriate defaults.
73-74: LGTM! Configuration attributes properly exposed.The new attributes are correctly added to the public interface via
attr_accessor.
91-91: LGTM! Initialization parameters handled correctly.The new parameters are properly accepted in the initializer and assigned to instance variables.
Also applies to: 136-137
154-154: LGTM! Validation method properly integrated.The new validation method is correctly added to the configuration setup flow.
lib/react_on_rails/utils.rb (4)
72-82: LGTM! Clean delegation pattern implemented.The refactoring cleanly separates manifest.json handling from packer-based bundle resolution while maintaining backwards compatibility.
85-104: LGTM! Well-structured bundle resolution logic.The method properly handles server bundle priority (secure locations first) while falling back gracefully to manifest lookup for all bundles.
112-120: LGTM! Secure path resolution implemented correctly.The method properly tries the configured secure location and returns the path only if it exists.
122-134: LGTM! Missing manifest fallback logic is appropriate.The fallback correctly prioritizes configured server bundle paths for server bundles while using packer's public path for client bundles.
lib/generators/react_on_rails/base_generator.rb (1)
140-150: LGTM! Efficient batch update for .gitignore entries.The refactored method efficiently batches additions and only updates the .gitignore file when necessary, improving performance and consistency.
lib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt (1)
47-55: LGTM! Server bundles properly secured in non-public directory.The configuration correctly outputs server bundles to the
ssr-generateddirectory outside of the public web root, improving security by preventing direct web access to server-side code.spec/react_on_rails/configuration_spec.rb (1)
429-478: LGTM! Comprehensive test coverage for security validation.The tests thoroughly validate the
enforce_secure_server_bundlesconfiguration option, covering all scenarios including error cases and valid configurations.lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt (1)
46-53: LGTM! Clear documentation and secure defaults.The configuration template provides clear documentation for the new server bundle security options and sets secure defaults (
enforce_secure_server_bundles = true).spec/react_on_rails/utils_spec.rb (7)
53-64: LGTM! Updated mocks use explicit Shakapacker references.The mocking properly uses
Shakapacker::ManifestandShakapacker::Manifest::MissingEntryError, aligning with the framework's explicit dependency approach.
74-81: LGTM! Improved test configuration with server bundle path mocking.The method properly mocks the new
server_bundle_output_pathconfiguration option for comprehensive testing.
108-114: LGTM! Prevents server bundle false positives in client bundle tests.The configuration mocking ensures that regular client bundles aren't incorrectly identified as server bundles during testing.
146-157: LGTM! Clear fallback behavior testing for client bundles.The test properly validates that client bundles fall back to environment-specific paths when not found in the manifest.
159-224: Add test for enforcement behavior when secure locations are missing.The tests cover server bundle resolution scenarios but should verify that when
enforce_secure_server_bundlesis true, server bundles never fall back to public locations.Add a test case to verify enforcement behavior:
it "does not fall back to public when enforcement is true" do allow(ReactOnRails).to receive_message_chain("configuration.enforce_secure_server_bundles") .and_return(true) allow(File).to receive(:exist?).and_call_original allow(File).to receive(:exist?).with(ssr_generated_path).and_return(false) allow(File).to receive(:exist?).with(File.expand_path(File.join("public", "packs", server_bundle_name))).and_return(true) result = described_class.bundle_js_file_path(server_bundle_name) expect(result).to eq(ssr_generated_path) end
271-279: LGTM! Tests properly validate secure server bundle paths.The tests correctly verify that server bundles (both SSR and RSC) use secure
ssr-generatedpaths when not found in the manifest.Also applies to: 370-377
642-642: LGTM! Explicit Shakapacker::DevServer reference.The test correctly uses explicit
Shakapacker::DevServertype annotation for better clarity and consistency.
|
@claude tests are failing, fix all CI checks |
|
Claude finished @AbanoubGhadban's task —— View job ✅ CI fixes completedTodo List:
Summary of Changes:Successfully fixed all identified CI failures: RuboCop Violations Fixed:
Test Failures Fixed:Configuration Tests:
Utils Tests:
Commit: 4b2bef4The fixes have been pushed. CI should now pass once the workflows re-run. 🚀 Job run link: GitHub Actions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (2)
lib/react_on_rails/utils.rb (2)
90-92: AvoidRails.root || "."; make Rails context explicit.Using "." can mask misconfiguration and generate surprising paths. Prefer to assert Rails is present or at least normalize to a string.
Apply this diff:
- root_path = Rails.root || "." + raise ReactOnRails::Error, "Rails.root is not set; cannot resolve bundle paths outside a Rails app context" unless Rails.respond_to?(:root) && Rails.root + root_path = Rails.root.to_s
104-109: Broaden rescue or gate packer calls to avoid NoMethodError in edge cases.If Shakapacker isn't initialized as expected,
bundle_js_uri_from_packermay raise non-MissingEntryErrorexceptions (e.g.,NoMethodError). Either guard before calling or broaden rescue to route to the same fallback handler.Apply this diff:
- begin - ReactOnRails::PackerUtils.bundle_js_uri_from_packer(bundle_name) - rescue Shakapacker::Manifest::MissingEntryError - handle_missing_manifest_entry(bundle_name, is_server_bundle) - end + begin + ReactOnRails::PackerUtils.bundle_js_uri_from_packer(bundle_name) + rescue Shakapacker::Manifest::MissingEntryError, NoMethodError, NameError + handle_missing_manifest_entry(bundle_name, is_server_bundle) + end
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
lib/react_on_rails/utils.rb(3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru}}
📄 CodeRabbit inference engine (CLAUDE.md)
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru}}: Before every commit/push, runbundle exec rubocopand fix all violations in Ruby code
Let RuboCop handle all Ruby code formatting; never manually format Ruby files
Files:
lib/react_on_rails/utils.rb
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru,js,jsx,ts,tsx,json,yml,yaml,md,css,scss}}
📄 CodeRabbit inference engine (CLAUDE.md)
Ensure all committed files end with a trailing newline character
Files:
lib/react_on_rails/utils.rb
🧠 Learnings (5)
📚 Learning: 2025-04-26T21:55:55.874Z
Learnt from: alexeyr-ci2
PR: shakacode/react_on_rails#1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.
Applied to files:
lib/react_on_rails/utils.rb
📚 Learning: 2025-02-12T16:38:06.537Z
Learnt from: Romex91
PR: shakacode/react_on_rails#1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
Applied to files:
lib/react_on_rails/utils.rb
📚 Learning: 2025-09-16T08:01:11.146Z
Learnt from: justin808
PR: shakacode/react_on_rails#1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.
Applied to files:
lib/react_on_rails/utils.rb
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.
Applied to files:
lib/react_on_rails/utils.rb
📚 Learning: 2025-09-15T21:24:48.207Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.
Applied to files:
lib/react_on_rails/utils.rb
🧬 Code graph analysis (1)
lib/react_on_rails/utils.rb (2)
lib/react_on_rails/configuration.rb (1)
configuration(16-59)lib/react_on_rails/packer_utils.rb (2)
bundle_js_uri_from_packer(51-67)packer_public_output_path(100-102)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: build-dummy-app-webpack-test-bundles (3.2, 20)
- GitHub Check: build
- GitHub Check: rspec-package-tests (3.2, latest)
- GitHub Check: rspec-package-tests (3.4, minimum)
- GitHub Check: rspec-package-tests (3.4, latest)
- GitHub Check: rspec-package-tests (3.2, minimum)
- GitHub Check: claude-review
- GitHub Check: examples (3.4, latest)
- GitHub Check: examples (3.2, minimum)
🔇 Additional comments (5)
lib/react_on_rails/utils.rb (5)
112-116: LGTM on server-bundle detection.Straightforward and consistent with config names.
164-165: LGTM: server manifest path uses public_bundles_full_path.Correctly avoids dev server for server manifest.
88-101: Unconditionally prefer configured private server path when set.Per the agreed behavior, if
server_bundle_output_pathis configured, return it directly for server bundles (even if the file doesn't exist) and skip manifest/public fallbacks. Remove the existence/enforcement check here.Apply this diff:
- # If server bundle and server_bundle_output_path is configured, return that path directly - if is_server_bundle && config.server_bundle_output_path.present? - private_server_bundle_path = File.expand_path(File.join(root_path, config.server_bundle_output_path, - bundle_name)) - - # Don't fall back to public directory if enforce_private_server_bundles is enabled - if config.enforce_private_server_bundles || File.exist?(private_server_bundle_path) - return private_server_bundle_path - end - end + # If server bundle and server_bundle_output_path is configured, return that path directly + if is_server_bundle && config.server_bundle_output_path.present? + return File.expand_path(File.join(root_path, config.server_bundle_output_path, bundle_name)) + end
200-208: LGTM — keep deprecated aliasRepo search shows generated_assets_full_path is used only internally (lib, test helpers, specs, docs) and now delegates to public_bundles_full_path; compatibility preserved and no callers in this repo require a different meaning. Representative files: lib/react_on_rails/packer_utils.rb, lib/react_on_rails/test_helper.rb, lib/react_on_rails/test_helper/webpack_assets_status_checker.rb, spec/react_on_rails/utils_spec.rb, docs/guides/configuration.md.
118-130: Implement multi-location fallback and honor private‑only enforcement.Current logic only returns the environment-specific public path and doesn't try the required ordered fallbacks or legacy private paths. Also, when enforcement is on and no configured path, we must not fall back to public.
Apply this diff:
- config = ReactOnRails.configuration - root_path = Rails.root || "." - - # For server bundles with server_bundle_output_path configured, use that - if is_server_bundle && config.server_bundle_output_path.present? - return File.expand_path(File.join(root_path, config.server_bundle_output_path, bundle_name)) - end - - # For client bundles and server bundles without special config, use packer's public path - # This returns the environment-specific path configured in shakapacker.yml - File.expand_path(File.join(ReactOnRails::PackerUtils.packer_public_output_path, bundle_name)) + config = ReactOnRails.configuration + raise ReactOnRails::Error, "Rails.root is not set; cannot resolve bundle paths" unless Rails.respond_to?(:root) && Rails.root + root_path = Rails.root.to_s + + # Enforced private: never fall back to public locations for server bundles + if is_server_bundle && config.enforce_private_server_bundles + preferred_dir = config.server_bundle_output_path.presence || "ssr-generated" + return File.expand_path(File.join(root_path, preferred_dir, bundle_name)) + end + + candidates = + if is_server_bundle + [ + (config.server_bundle_output_path.present? && File.join(config.server_bundle_output_path, bundle_name)), + File.join("ssr-generated", "","", bundle_name).gsub(File::SEPARATOR+File::SEPARATOR, File::SEPARATOR), # default private + File.join("generated", "server-bundles", bundle_name), # legacy private + File.join(ReactOnRails::PackerUtils.packer_public_output_path, bundle_name), # env-specific public + File.join("public", "packs", bundle_name) # standard public + ].compact + else + [ + File.join(ReactOnRails::PackerUtils.packer_public_output_path, bundle_name), # env-specific public + File.join("public", "packs", bundle_name) # standard public + ] + end + + # Return first existing candidate + candidates.each do |rel| + abs = File.expand_path(rel, root_path) + return abs if File.exist?(abs) + end + + # Default when nothing found: private dir for server, env-specific public for client + if is_server_bundle + preferred_dir = config.server_bundle_output_path.presence || "ssr-generated" + File.expand_path(File.join(root_path, preferred_dir, bundle_name)) + else + File.expand_path(File.join(ReactOnRails::PackerUtils.packer_public_output_path, bundle_name), root_path) + end
- Updated the logic for determining server bundle paths to include a fallback mechanism that checks for the existence of paths based on the `enforce_private_server_bundles` configuration. - This change ensures that if the enforcement is not active, the system will attempt to return the first valid path from a list of candidate paths, improving robustness in asset management. This update aligns with recent changes to clarify the handling of server bundles and enhances the overall functionality of the asset resolution process.
Code Review for PR #1798: Fix bundle path resolution and improve server bundle security🎯 Overall AssessmentThis PR makes significant improvements to bundle path resolution and enhances security for server-side bundles. The changes address critical issues while maintaining backward compatibility. The implementation is well-thought-out and includes comprehensive test coverage. ✅ Strengths1. Security Enhancements
2. Code Quality
3. Bundle Path Resolution
4. Test Coverage
🔍 Potential Issues & Suggestions1. Duplicate Path in Fallback Logic (Line 124-126 in utils.rb)The handle_missing_manifest_entry method adds the same path twice to candidate_paths. The second path should likely be the public directory fallback instead of duplicating the server_bundle_output_path. 2. Performance Considerations
3. Version Constant Naming
4. Documentation
🛡️ Security Review✅ No security vulnerabilities identified
🚀 Performance
✅ Backward Compatibility
📝 Minor Suggestions
✅ RecommendationAPPROVED with minor suggestions - This PR makes important security improvements and fixes critical bugs. The implementation is solid with good test coverage. The duplicate path issue in utils.rb:124-126 should be fixed, but it does not affect functionality since both paths are identical. Great work on improving the security architecture and maintaining backward compatibility! 🎉 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
lib/react_on_rails/utils.rb (3)
75-77: Comment and implementation diverge; align the docstring or the code.The comment promises “then legacy” fallbacks for both server and client bundles, but the implementation below doesn’t implement that sequence. Either update the comment to reflect actual behavior or implement the described multi-step fallbacks.
82-82: Consider anchoring to Rails.root for consistency.Joining without expand may yield a relative path; using File.expand_path(..., Rails.root) would be more robust.
91-91: Prefer failing fast if Rails.root is not set.Using "." as a fallback can mask misconfiguration; raise with a clear message instead.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
lib/react_on_rails/utils.rb(3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru}}
📄 CodeRabbit inference engine (CLAUDE.md)
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru}}: Before every commit/push, runbundle exec rubocopand fix all violations in Ruby code
Let RuboCop handle all Ruby code formatting; never manually format Ruby files
Files:
lib/react_on_rails/utils.rb
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru,js,jsx,ts,tsx,json,yml,yaml,md,css,scss}}
📄 CodeRabbit inference engine (CLAUDE.md)
Ensure all committed files end with a trailing newline character
Files:
lib/react_on_rails/utils.rb
🧠 Learnings (5)
📚 Learning: 2025-04-26T21:55:55.874Z
Learnt from: alexeyr-ci2
PR: shakacode/react_on_rails#1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.
Applied to files:
lib/react_on_rails/utils.rb
📚 Learning: 2025-09-16T08:01:11.146Z
Learnt from: justin808
PR: shakacode/react_on_rails#1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.
Applied to files:
lib/react_on_rails/utils.rb
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.
Applied to files:
lib/react_on_rails/utils.rb
📚 Learning: 2025-09-15T21:24:48.207Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.
Applied to files:
lib/react_on_rails/utils.rb
📚 Learning: 2025-02-12T16:38:06.537Z
Learnt from: Romex91
PR: shakacode/react_on_rails#1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
Applied to files:
lib/react_on_rails/utils.rb
🧬 Code graph analysis (1)
lib/react_on_rails/utils.rb (2)
lib/react_on_rails/configuration.rb (1)
configuration(16-59)lib/react_on_rails/packer_utils.rb (2)
bundle_js_uri_from_packer(51-67)packer_public_output_path(100-102)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
- GitHub Check: dummy-app-integration-tests (3.2, 20)
- GitHub Check: dummy-app-integration-tests (3.4, 22)
- GitHub Check: examples (3.2, minimum)
- GitHub Check: examples (3.4, latest)
- GitHub Check: claude-review
- GitHub Check: rspec-package-tests (3.2, minimum)
- GitHub Check: rspec-package-tests (3.4, minimum)
- GitHub Check: rspec-package-tests (3.4, latest)
- GitHub Check: rspec-package-tests (3.2, latest)
- GitHub Check: build
🔇 Additional comments (5)
lib/react_on_rails/utils.rb (5)
112-116: LGTM: Simple and clear server-bundle detection.Compares against configured server and RSC bundle names; memoization can happen at call site if needed.
172-172: LGTM: Server manifest anchored to public bundles path.Matches intent to avoid dev-server for server manifest.
212-215: Alias may create redundant “fallbacks.”generated_assets_full_path now equals public_bundles_full_path. If used in fallback chains, ensure you de-dup (as suggested above) or drop it to avoid no-op checks.
93-101: Return the configured private server bundle path unconditionally when present (per PR objectives).Current logic returns the configured path only if enforcement is on or the file exists. The stated requirement is to return the configured path whenever present, even if the file does not exist. This also avoids hitting manifest/public paths in that scenario.
Apply this diff:
- # If server bundle and server_bundle_output_path is configured, return that path directly - if is_server_bundle && config.server_bundle_output_path.present? - private_server_bundle_path = File.expand_path(File.join(root_path, config.server_bundle_output_path, - bundle_name)) - - # Don't fall back to public directory if enforce_private_server_bundles is enabled - if config.enforce_private_server_bundles || File.exist?(private_server_bundle_path) - return private_server_bundle_path - end - end + # If server bundle and server_bundle_output_path is configured, return that path directly + if is_server_bundle && config.server_bundle_output_path.present? + return File.expand_path(File.join(root_path, config.server_bundle_output_path, bundle_name)) + end
118-138: Fix fallback logic: remove duplicate path, honor private-first semantics, and check existence across multiple candidates.
- Duplicate push of the same path (Lines 124–127).
- Client fallback returns a path without checking existence and doesn’t try the documented multi-location sequence.
- For server bundles, skip public fallbacks when enforcement is on; otherwise include them after private options.
Apply this diff:
- private_class_method def self.handle_missing_manifest_entry(bundle_name, is_server_bundle) - config = ReactOnRails.configuration - root_path = Rails.root || "." - - # For server bundles with server_bundle_output_path configured, use that - if is_server_bundle && config.server_bundle_output_path.present? - candidate_paths = [File.expand_path(File.join(root_path, config.server_bundle_output_path, bundle_name))] - unless config.enforce_private_server_bundles - candidate_paths << File.expand_path(File.join(root_path, config.server_bundle_output_path, bundle_name)) - end - - candidate_paths.each do |path| - return path if File.exist?(path) - end - return candidate_paths.first - end - - # For client bundles and server bundles without special config, use packer's public path - # This returns the environment-specific path configured in shakapacker.yml - File.expand_path(File.join(ReactOnRails::PackerUtils.packer_public_output_path, bundle_name)) - end + private_class_method def self.handle_missing_manifest_entry(bundle_name, is_server_bundle) + config = ReactOnRails.configuration + root_path = Rails.root || "." + + candidates = [] + if is_server_bundle + preferred_dir = config.server_bundle_output_path.presence || "ssr-generated" + # Prefer private locations + candidates << File.expand_path(File.join(root_path, preferred_dir, bundle_name)) + candidates << File.expand_path(File.join(root_path, "generated", "server-bundles", bundle_name)) + # Only consider public fallbacks if not enforcing private bundles + unless config.enforce_private_server_bundles + candidates << File.expand_path(File.join(ReactOnRails::PackerUtils.packer_public_output_path, bundle_name), root_path) + candidates << File.expand_path(File.join(root_path, "public", "packs", bundle_name)) + end + else + # Client bundles: try manifest's public output dir, then standard location, then legacy + candidates << File.expand_path(File.join(ReactOnRails::PackerUtils.packer_public_output_path, bundle_name), root_path) + candidates << File.expand_path(File.join(root_path, "public", "packs", bundle_name)) + candidates << File.expand_path(File.join(generated_assets_full_path, bundle_name), root_path) + end + + candidates.uniq.each do |path| + return path if File.exist?(path) + end + candidates.first + end
- Modified the candidate paths logic to include the public output path from PackerUtils when `enforce_private_server_bundles` is not active. - This change enhances the flexibility of server bundle path resolution, ensuring that the system can effectively locate bundles in the public directory when appropriate. This update builds on previous enhancements to improve asset management and aligns with the ongoing refactoring efforts in the project.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (5)
lib/react_on_rails/utils.rb (5)
75-77: Fix docstring: current comment misstates the implemented fallback order.Update to reflect the actual flow (private first for server bundles; manifest/public for client; no “legacy” step implemented).
Apply:
- # Priority order depends on bundle type: - # SERVER BUNDLES (normal case): Try private non-public locations first, then manifest, then legacy - # CLIENT BUNDLES (normal case): Try manifest first, then fallback locations + # Priority order depends on bundle type: + # SERVER BUNDLES: prefer configured private path (server_bundle_output_path); + # if not found and enforcement is disabled, try manifest/public. + # CLIENT BUNDLES: resolve via manifest/public; limited fallbacks (if any) handled downstream.
88-92: Avoid “.” fallback for Rails.root; require Rails context.Using “.” can resolve to an unintended CWD. Fail fast if Rails.root is not set.
Apply:
- root_path = Rails.root || "." + unless defined?(Rails) && Rails.respond_to?(:root) && Rails.root + raise ReactOnRails::Error, "Rails.root is not set. This code must run within a Rails app." + end + root_path = Rails.rootRepeat the same guard and assignment in handle_missing_manifest_entry (Lines 120-121).
93-102: DRY: extract private server bundle path helper; optionally log when returning a non-existent private path due to enforcement.The private-path construction is duplicated here and in handle_missing_manifest_entry; extracting reduces drift. Consider logging once when enforcement returns a non-existent path to aid debugging.
Apply:
- if is_server_bundle && config.server_bundle_output_path.present? - private_server_bundle_path = File.expand_path(File.join(root_path, config.server_bundle_output_path, - bundle_name)) + if is_server_bundle && config.server_bundle_output_path.present? + private_server_bundle_path = private_server_bundle_path(bundle_name)And add:
+ private_class_method def self.private_server_bundle_path(bundle_name) + root = Rails.root || "." + File.expand_path(File.join(root, ReactOnRails.configuration.server_bundle_output_path, bundle_name)) + end
118-139: Client fallback doesn’t check existence or try multiple locations; align with “first existing” intent. Also fix comment wording.Implement a first-existing check (packer path, then default public/packs if different). Keep enforcement rules for server bundles; for clients, guard packer calls and avoid env-cross fallbacks.
Apply:
- config = ReactOnRails.configuration - root_path = Rails.root || "." + config = ReactOnRails.configuration + unless defined?(Rails) && Rails.respond_to?(:root) && Rails.root + raise ReactOnRails::Error, "Rails.root is not set. This code must run within a Rails app." + end + root_path = Rails.root @@ - # For server bundles with server_bundle_output_path configured, use that + # For server bundles with server_bundle_output_path configured, prefer that @@ - candidate_paths = [File.expand_path(File.join(root_path, config.server_bundle_output_path, bundle_name))] + candidate_paths = [private_server_bundle_path(bundle_name)] unless config.enforce_private_server_bundles - candidate_paths << File.expand_path(File.join(ReactOnRails::PackerUtils.packer_public_output_path, - bundle_name)) + candidate_paths << File.expand_path(File.join(ReactOnRails::PackerUtils.packer_public_output_path, bundle_name)) end @@ - # For client bundles and server bundles without special config, use packer's public path - # This returns the environment-specific path configured in shakapacker.yml - File.expand_path(File.join(ReactOnRails::PackerUtils.packer_public_output_path, bundle_name)) + # For client bundles and server bundles without special config, prefer the first existing path. + packer_path = File.expand_path(File.join(ReactOnRails::PackerUtils.packer_public_output_path, bundle_name)) + default_packs = File.expand_path(File.join(Rails.root.to_s, "public", "packs", bundle_name)) + candidates = [packer_path] + candidates << default_packs if default_packs != packer_path + found = candidates.find { |p| File.exist?(p) } + found || packer_pathAlso update the nearby comment:
- # This returns the environment-specific path configured in shakapacker.yml + # This returns the Shakapacker public output path configured in shakapacker.yml
213-217: Emit deprecation warning for generated_assets_full_path.Warn once to help users migrate to public_bundles_full_path.
Apply:
def self.generated_assets_full_path - public_bundles_full_path + ActiveSupport::Deprecation.warn("generated_assets_full_path is deprecated; use public_bundles_full_path") + public_bundles_full_path end
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
lib/react_on_rails/utils.rb(3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru}}
📄 CodeRabbit inference engine (CLAUDE.md)
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru}}: Before every commit/push, runbundle exec rubocopand fix all violations in Ruby code
Let RuboCop handle all Ruby code formatting; never manually format Ruby files
Files:
lib/react_on_rails/utils.rb
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru,js,jsx,ts,tsx,json,yml,yaml,md,css,scss}}
📄 CodeRabbit inference engine (CLAUDE.md)
Ensure all committed files end with a trailing newline character
Files:
lib/react_on_rails/utils.rb
🧠 Learnings (5)
📚 Learning: 2025-04-26T21:55:55.874Z
Learnt from: alexeyr-ci2
PR: shakacode/react_on_rails#1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.
Applied to files:
lib/react_on_rails/utils.rb
📚 Learning: 2025-09-16T08:01:11.146Z
Learnt from: justin808
PR: shakacode/react_on_rails#1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.
Applied to files:
lib/react_on_rails/utils.rb
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.
Applied to files:
lib/react_on_rails/utils.rb
📚 Learning: 2025-09-15T21:24:48.207Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.
Applied to files:
lib/react_on_rails/utils.rb
📚 Learning: 2025-02-12T16:38:06.537Z
Learnt from: Romex91
PR: shakacode/react_on_rails#1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
Applied to files:
lib/react_on_rails/utils.rb
🧬 Code graph analysis (1)
lib/react_on_rails/utils.rb (2)
lib/react_on_rails/configuration.rb (1)
configuration(16-59)lib/react_on_rails/packer_utils.rb (2)
bundle_js_uri_from_packer(51-67)packer_public_output_path(100-102)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
- GitHub Check: dummy-app-integration-tests (3.4, 22)
- GitHub Check: dummy-app-integration-tests (3.2, 20)
- GitHub Check: rspec-package-tests (3.4, minimum)
- GitHub Check: rspec-package-tests (3.2, latest)
- GitHub Check: rspec-package-tests (3.4, latest)
- GitHub Check: rspec-package-tests (3.2, minimum)
- GitHub Check: claude-review
- GitHub Check: examples (3.2, minimum)
- GitHub Check: examples (3.4, latest)
- GitHub Check: build
🔇 Additional comments (4)
lib/react_on_rails/utils.rb (4)
104-110: Confirm Shakapacker availability or broaden error handling.This unconditionally calls into Shakapacker; only MissingEntryError is rescued. If Shakapacker is not loaded/misconfigured, other exceptions can surface. If the gem now hard-requires Shakapacker, fine; otherwise, guard or rescue more broadly and route to the same fallback.
Run:
#!/bin/bash set -euo pipefail # Verify gem requires Shakapacker at install-time fd -a '\.gemspec$' -x rg -nP '(add_dependency|spec\.add_dependency)\s*\(?\s*["'\'']shakapacker["'\'']' {} # Check for hard require of shakapacker rg -nP "require\s+['\"]shakapacker['\"]" -C2 # Confirm all rescue sites cover missing manifest uses rg -n "Shakapacker::Manifest::MissingEntryError" -C2
173-174: Verify server manifest location (public vs private).With private server bundles (ssr-generated) by default, should react_server_client_manifest_file_path come from the private server_bundle_output_path instead of public_bundles_full_path? Confirm intended location and adjust if needed.
112-116: LGTM: simple and clear server-bundle detection.
209-211: LGTM: explicit public path helper improves clarity over “generated assets.”
|
@claude, in the last three commits, I configured the React on Rails fallback to use the public path for the server bundle if the server bundle does not exist at the private path and the private bundle path is not enforced. This change has caused the CI checks to fail. Please review the changes made in the last three commits, along with the failing CI checks, and address the issues accordingly. |
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
|
@claude check failing CI tests and fix them |
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
- Fixed Rails.root nil issue in configuration tests by adding mock - Updated all remaining ReactOnRails::PackerUtils.packer references to use Shakapacker directly - Fixed packer method calls in utils_spec.rb, packer_utils_spec.rb, and webpack_assets_status_checker_spec.rb - Replaced dynamic constant assignment with stub_const for better test isolation This completes the migration from the removed packer method to direct Shakapacker usage in tests. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (4)
spec/react_on_rails/configuration_spec.rb (1)
15-18: Minor: Avoid mixing Pathname with File.join in stubs.To prevent subtle path issues across Ruby versions, join using a String root.
- allow(ReactOnRails::PackerUtils).to receive(:packer_public_output_path) - .and_return(File.expand_path(File.join(Rails.root, "public/packs"))) + allow(ReactOnRails::PackerUtils).to receive(:packer_public_output_path) + .and_return(File.expand_path(File.join(Rails.root.to_s, "public/packs")))spec/react_on_rails/utils_spec.rb (3)
144-164: Add explicit enforcement scenario: no fallback to public when private SSR path missing.Strengthen coverage by asserting behavior when public bundle exists but enforcement is enabled.
context "with server_bundle_output_path configured" do before do mock_missing_manifest_entry(server_bundle_name) allow(ReactOnRails).to receive_message_chain("configuration.server_bundle_js_file") .and_return(server_bundle_name) allow(ReactOnRails).to receive_message_chain("configuration.server_bundle_output_path") .and_return("ssr-generated") end it "returns configured path directly without checking existence" do # Should not check File.exist? - returns path immediately expect(File).not_to receive(:exist?) result = described_class.bundle_js_file_path(server_bundle_name) expect(result).to eq(ssr_generated_path) end + + it "does not fall back to public when enforcement is true" do + allow(ReactOnRails).to receive_message_chain("configuration.enforce_private_server_bundles") + .and_return(true) + public_packs_path = File.expand_path(File.join(packer_public_output_path, server_bundle_name)) + + allow(File).to receive(:exist?).and_call_original + allow(File).to receive(:exist?).with(ssr_generated_path).and_return(false) + allow(File).to receive(:exist?).with(public_packs_path).and_return(true) + + result = described_class.bundle_js_file_path(server_bundle_name) + expect(result).to eq(ssr_generated_path) + end end
279-280: Remove redundant stub of dev_server.running?.It's already stubbed to false in the shared context; this duplication adds noise.
- allow(Shakapacker).to receive_message_chain("dev_server.running?") - .and_return(false)
341-352: Remove redundant stub mirroring shared context.Same duplication as above; safe to remove.
- allow(Shakapacker).to receive_message_chain("dev_server.running?") - .and_return(false)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
spec/react_on_rails/configuration_spec.rb(2 hunks)spec/react_on_rails/test_helper/webpack_assets_status_checker_spec.rb(2 hunks)spec/react_on_rails/utils_spec.rb(13 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- spec/react_on_rails/test_helper/webpack_assets_status_checker_spec.rb
🧰 Additional context used
📓 Path-based instructions (2)
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru}}
📄 CodeRabbit inference engine (CLAUDE.md)
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru}}: Before every commit/push, runbundle exec rubocopand fix all violations in Ruby code
Let RuboCop handle all Ruby code formatting; never manually format Ruby files
Files:
spec/react_on_rails/configuration_spec.rbspec/react_on_rails/utils_spec.rb
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru,js,jsx,ts,tsx,json,yml,yaml,md,css,scss}}
📄 CodeRabbit inference engine (CLAUDE.md)
Ensure all committed files end with a trailing newline character
Files:
spec/react_on_rails/configuration_spec.rbspec/react_on_rails/utils_spec.rb
🧠 Learnings (4)
📚 Learning: 2025-02-12T16:38:06.537Z
Learnt from: Romex91
PR: shakacode/react_on_rails#1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
Applied to files:
spec/react_on_rails/configuration_spec.rbspec/react_on_rails/utils_spec.rb
📚 Learning: 2025-04-26T21:55:55.874Z
Learnt from: alexeyr-ci2
PR: shakacode/react_on_rails#1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.
Applied to files:
spec/react_on_rails/configuration_spec.rb
📚 Learning: 2024-10-08T20:53:47.076Z
Learnt from: justin808
PR: shakacode/react_on_rails#1622
File: spec/dummy/spec/rake/assets_precompile_rake_spec.rb:12-12
Timestamp: 2024-10-08T20:53:47.076Z
Learning: When stubbing environment variables in RSpec tests, use `before` and `after` hooks to ensure that the original values are restored after the tests, preventing any side effects on other tests. Example provided by justin808:
```ruby
describe "My test" do
before do
original_value = ENV["VARIABLE_NAME"]
allow(ENV).to receive(:[]).with("VARIABLE_NAME").and_return("stubbed_value")
end
after do
allow(ENV).to receive(:[]).with("VARIABLE_NAME").and_call_original
ENV["VARIABLE_NAME"] = original_value
end
it "tests something" do
# Your test code here
end
end
```
This practice ensures test isolation and reliability.
Applied to files:
spec/react_on_rails/configuration_spec.rb
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.
Applied to files:
spec/react_on_rails/configuration_spec.rb
🧬 Code graph analysis (2)
spec/react_on_rails/configuration_spec.rb (1)
lib/react_on_rails/configuration.rb (1)
configure(6-9)
spec/react_on_rails/utils_spec.rb (2)
lib/react_on_rails/packer_utils.rb (1)
packer_public_output_path(100-102)lib/react_on_rails/utils.rb (3)
bundle_js_file_path(74-86)server_bundle_js_file_path(141-146)rsc_bundle_js_file_path(148-153)
🪛 ast-grep (0.39.5)
spec/react_on_rails/configuration_spec.rb
[warning] 16-17: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: allow(ReactOnRails::PackerUtils).to receive(:packer_public_output_path)
.and_return(File.expand_path(File.join(Rails.root, "public/packs")))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html
(hardcoded-secret-rsa-passphrase-ruby)
[warning] 16-17: Found the use of an hardcoded passphrase for RSA. The passphrase can be easily discovered, and therefore should not be stored in source-code. It is recommended to remove the passphrase from source-code, and use system environment variables or a restricted configuration file.
Context: receive(:packer_public_output_path)
.and_return(File.expand_path(File.join(Rails.root, "public/packs")))
Note: [CWE-798]: Use of Hard-coded Credentials [OWASP A07:2021]: Identification and Authentication Failures [REFERENCES]
https://cwe.mitre.org/data/definitions/522.html
(hardcoded-secret-rsa-passphrase-ruby)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
- GitHub Check: dummy-app-integration-tests (3.4, 22)
- GitHub Check: dummy-app-integration-tests (3.2, 20)
- GitHub Check: rspec-package-tests (3.4, minimum)
- GitHub Check: build
- GitHub Check: rspec-package-tests (3.4, latest)
- GitHub Check: rspec-package-tests (3.2, latest)
- GitHub Check: rspec-package-tests (3.2, minimum)
- GitHub Check: examples (3.4, latest)
- GitHub Check: examples (3.2, minimum)
- GitHub Check: claude-review
🔇 Additional comments (13)
spec/react_on_rails/configuration_spec.rb (1)
466-521: LGTM: Enforcement validation coverage is clear and correct.Covers nil, inside-public, and outside-public cases gated by the flag. No changes requested.
spec/react_on_rails/utils_spec.rb (12)
64-66: Good defaulting for SSR path within helper.Centralizing the default SSR output path simplifies tests; explicit nil overrides below keep behavior intentional.
92-100: LGTM: Prevents false server/RSC detection for client bundle tests.The stubs keep bundle type detection from interfering with client-path expectations.
131-142: LGTM: Correct fallback to environment-specific path when not in manifest.Expectation matches prioritized lookup.
166-180: LGTM: Sensible fallback to public packer path when private path not configured.Matches the documented fallback order for server bundles.
182-201: LGTM: RSC treated as server bundle.Covers direct return of configured SSR path; no existence checks.
241-266: LGTM: Memoization-sensitive path methods return SSR path directly.Also verifies no manifest lookup or existence checks when configured path present.
271-276: LGTM: Clears SSR output to exercise manifest code paths.Ensures client-path behavior is tested.
330-338: LGTM: RSC private path default verified.Test reads clearly and asserts the intended default.
653-675: LGTM: Switched to public_bundles_full_path for manifest path assembly.Matches refactor in Utils and keeps responsibilities local.
671-689: LGTM: Caching behavior for non-dev confirmed.Solidifies the intended memoization semantics.
691-711: LGTM: Parametrized manifest filename coverage.Covers both default and custom names against public_bundles_full_path.
714-726: LGTM: Nil manifest filename raises explicit error.Error text is specific and actionable.
Code Review for PR #1798: Fix bundle path resolution and improve server bundle securityI have completed a thorough review of this PR. Here is my comprehensive feedback: ✅ Strengths & Improvements🔒 Security Enhancements
🏗️ Code Quality
🧪 Test Coverage
🔍 Potential Issues & Suggestions1. Performance ConsiderationThe multiple File.exist? checks in fallback logic could impact performance in development mode where caching is disabled. Consider adding a development-mode cache with a short TTL to reduce filesystem checks. 2. Error Messages Could Be More HelpfulWhen enforce_private_server_bundles is enabled and validation fails, the error messages could provide more actionable guidance. Include migration steps in error messages to help users fix configuration issues. 3. Directory Creation Not AutomatedThe ssr-generated directory needs manual creation or webpack configuration. Consider adding a generator task or documentation about automatic directory creation. 4. Deprecation PathThe removal of generated_assets_dirs from configuration could benefit from a clearer deprecation warning for users upgrading. 📊 Performance Impact
🔐 Security Assessment
✨ Minor Suggestions
📝 DocumentationThe configuration guide updates are excellent:
✅ Overall AssessmentThis is a high-quality PR that successfully addresses the reported issues while significantly improving security. The code is clean, well-tested, and maintains backwards compatibility. The security improvements alone make this a valuable addition. Recommendation: APPROVE ✅ The PR effectively fixes #1797 and enhances security without breaking existing functionality. The minor suggestions above are non-blocking improvements that could be addressed in follow-up PRs if desired. Great work on this important security enhancement! The thoughtful approach to backwards compatibility while improving security is commendable. |
…ndles - Fix configuration test path inconsistencies by using consistent "public/packs" path - Add missing enforce_private_server_bundles mock to all server bundle tests - Update tests expecting no file existence check to set enforce_private_server_bundles=true - Fix RSC bundle tests to include required configuration mocks This resolves CI failures caused by recent commits that added fallback logic and enforce_private_server_bundles configuration option. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Update tests to properly reflect the new server bundle path resolution logic: - When enforce_private_server_bundles=false, both private and public paths are checked - Manifest lookup is attempted first, then fallback to candidate paths logic - Mock both File.exist? calls (private and public paths) in fallback scenarios - Use proper mock_missing_manifest_entry for consistent test behavior This follows the correct approach (#2) of updating test expectations to match the actual code behavior rather than forcing different execution paths. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
spec/react_on_rails/utils_spec.rb (3)
161-170: Rename misleading example description; keep assertions as-isThe example stubs File.exist? but the description claims “without checking existence”. Rename for clarity.
Apply this diff:
- it "returns configured path directly without checking existence" do - # When enforce_private_server_bundles is false, it will check File.exist? - # for both private and public paths, but should still return the configured path + it "returns configured path even if the file does not exist" do + # Returns the configured path even if neither private nor public files exist public_path = File.expand_path(File.join(packer_public_output_path, server_bundle_name)) allow(File).to receive(:exist?).with(ssr_generated_path).and_return(false) allow(File).to receive(:exist?).with(public_path).and_return(false) result = described_class.bundle_js_file_path(server_bundle_name) expect(result).to eq(ssr_generated_path) end
267-285: Rename misleading example description; keep assertions as-isSame nit as above: the spec stubs File.exist? but title says “without checking file existence”.
Apply this diff:
- it "returns the configured path directly without checking file existence" do + it "returns the configured path even if the file does not exist" do server_bundle_name = "server-bundle.js" mock_bundle_configs(server_bundle_name: server_bundle_name) mock_missing_manifest_entry(server_bundle_name) - # NOTE: mock_bundle_configs sets enforce_private_server_bundles to false - - # Since server_bundle_output_path is configured, it will try manifest lookup first, - # then fall back to candidate paths. With enforce_private_server_bundles=false, - # it will check File.exist? for both private and public paths + # Returns the configured path even if neither private nor public files exist ssr_generated_path = File.expand_path(File.join("ssr-generated", server_bundle_name)) public_path = File.expand_path(File.join(packer_public_output_path, server_bundle_name)) allow(File).to receive(:exist?).with(ssr_generated_path).and_return(false) allow(File).to receive(:exist?).with(public_path).and_return(false)
299-299: Use top-level constant for consistency: ::ShakapackerElsewhere you use ::Shakapacker; keep it consistent and robust against local constant shadowing.
Apply this diff:
- allow(Shakapacker).to receive_message_chain("dev_server.running?") + allow(::Shakapacker).to receive_message_chain("dev_server.running?")- allow(Shakapacker).to receive_message_chain("dev_server.running?") + allow(::Shakapacker).to receive_message_chain("dev_server.running?")Also applies to: 371-371
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
spec/react_on_rails/utils_spec.rb(13 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru}}
📄 CodeRabbit inference engine (CLAUDE.md)
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru}}: Before every commit/push, runbundle exec rubocopand fix all violations in Ruby code
Let RuboCop handle all Ruby code formatting; never manually format Ruby files
Files:
spec/react_on_rails/utils_spec.rb
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru,js,jsx,ts,tsx,json,yml,yaml,md,css,scss}}
📄 CodeRabbit inference engine (CLAUDE.md)
Ensure all committed files end with a trailing newline character
Files:
spec/react_on_rails/utils_spec.rb
🧠 Learnings (3)
📚 Learning: 2025-04-26T21:55:55.874Z
Learnt from: alexeyr-ci2
PR: shakacode/react_on_rails#1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.
Applied to files:
spec/react_on_rails/utils_spec.rb
📚 Learning: 2025-09-16T08:01:11.146Z
Learnt from: justin808
PR: shakacode/react_on_rails#1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.
Applied to files:
spec/react_on_rails/utils_spec.rb
📚 Learning: 2025-02-12T16:38:06.537Z
Learnt from: Romex91
PR: shakacode/react_on_rails#1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
Applied to files:
spec/react_on_rails/utils_spec.rb
🧬 Code graph analysis (1)
spec/react_on_rails/utils_spec.rb (2)
lib/react_on_rails/packer_utils.rb (1)
packer_public_output_path(100-102)lib/react_on_rails/utils.rb (3)
bundle_js_file_path(74-86)server_bundle_js_file_path(141-146)rsc_bundle_js_file_path(148-153)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: dummy-app-integration-tests (3.4, 22)
- GitHub Check: dummy-app-integration-tests (3.2, 20)
- GitHub Check: build
- GitHub Check: rspec-package-tests (3.4, minimum)
- GitHub Check: rspec-package-tests (3.2, latest)
- GitHub Check: examples (3.2, minimum)
- GitHub Check: rspec-package-tests (3.2, minimum)
- GitHub Check: examples (3.4, latest)
- GitHub Check: rspec-package-tests (3.4, latest)
🔇 Additional comments (8)
spec/react_on_rails/utils_spec.rb (8)
64-68: Setup for server bundle config and client-bundle detection stubs — LGTMGood isolation of server/rsc config and avoiding accidental server-bundle detection when testing client bundles.
Also applies to: 95-101
133-144: Fallback to environment-specific public path — LGTMThis covers the manifest-miss scenario correctly by falling back to the packer public output path.
173-189: No private output configured → fall back to packer public path — LGTM
191-215: RSC treated as server bundle with private-path preference — LGTM
256-265: server_bundle_js_file_path falls back to private ssr-generated — LGTM
290-295: Additional bundle-path scenarios and dev-server cases — LGTMThe manifest/dev-server cases for both server and RSC bundles look correct and consistent with the new resolution rules.
Also applies to: 306-306, 309-312, 324-324, 329-331, 349-357, 362-362, 365-367, 381-381, 383-384, 399-399, 401-403
671-671: public_bundles_full_path stubbing in manifest path tests — LGTMThese updates align with the public API rename from generated_assets_full_path.
Also applies to: 692-692, 712-712, 737-737
146-171: Add spec: enforce_private_server_bundles=true must not fall back to publicAdd a test in spec/react_on_rails/utils_spec.rb (inside the "with server bundle (SSR/RSC) file not in manifest" block) that sets enforce_private_server_bundles = true, mocks File.exist? so the private path is absent and the public path exists, and asserts bundle_js_file_path(server_bundle_name) returns the configured private (server_bundle_output_path) path.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 16 out of 17 changed files in this pull request and generated 5 comments.
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| allow(ReactOnRails).to receive_message_chain("configuration.server_bundle_output_path") | ||
| .and_return("ssr-generated") | ||
| allow(ReactOnRails).to receive_message_chain("configuration.enforce_private_server_bundles") | ||
| .and_return(false) |
Copilot
AI
Sep 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The hardcoded string 'ssr-generated' is duplicated throughout the test file. Consider extracting this into a test constant or let variable to improve maintainability and reduce duplication.
| let(:using_packer) { true } | ||
| let(:packer_public_output_path) do | ||
| File.expand_path(File.join(Rails.root, "public/webpack/dev")) | ||
| File.expand_path(File.join(Rails.root, "public/packs")) |
Copilot
AI
Sep 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The hardcoded path 'public/packs' is duplicated multiple times in this test file. Consider extracting this into a let variable or constant to improve maintainability.
| private_server_bundle_path = File.expand_path(File.join(root_path, config.server_bundle_output_path, | ||
| bundle_name)) |
Copilot
AI
Sep 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] This path construction is duplicated in the handle_missing_manifest_entry method. Consider extracting the path construction logic into a private helper method to reduce code duplication.
lib/react_on_rails/configuration.rb
Outdated
| def configure_generated_assets_dirs_deprecation | ||
| return if generated_assets_dirs.blank? | ||
|
|
||
| packer_public_output_path = ReactOnRails::PackerUtils.packer_public_output_path | ||
| Rails.logger.warn "You specified generated_assets_dirs in `config/initializers/react_on_rails.rb` " \ | ||
| "with Shakapacker. Remove this configuration as the output path is automatically " \ | ||
| "determined by `public_output_path` in shakapacker.yml " \ | ||
| "(currently: #{packer_public_output_path})." | ||
|
|
||
| msg = <<~MSG | ||
| ReactOnRails Configuration Error: The 'generated_assets_dirs' configuration option is no longer supported. | ||
| Since Shakapacker is now required, asset paths are automatically determined from your shakapacker.yml configuration. | ||
| Please remove 'config.generated_assets_dirs' from your config/initializers/react_on_rails.rb file. | ||
| Assets will be loaded from: #{packer_public_output_path} | ||
| If you need to customize the output path, configure it in config/shakapacker.yml under 'public_output_path'. | ||
| MSG | ||
|
|
||
| raise ReactOnRails::Error, msg |
Copilot
AI
Sep 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method changed from a warning to raising an error, which is a breaking change. The method name 'configure_generated_assets_dirs_deprecation' suggests configuration/warning behavior, but it now raises an error. Consider renaming to 'validate_generated_assets_dirs_removed' or similar to better reflect the new behavior.
| lines = ["\n# Generated React on Rails packs"] | ||
| lines.concat(additions) | ||
| "#{lines.join("\n")}\n" |
Copilot
AI
Sep 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic adds a leading newline but also a trailing newline, which could result in extra blank lines in .gitignore. The leading newline in the comment line and the trailing newline in the join result could create formatting inconsistencies.
| lines = ["\n# Generated React on Rails packs"] | |
| lines.concat(additions) | |
| "#{lines.join("\n")}\n" | |
| lines = ["# Generated React on Rails packs"] | |
| lines.concat(additions) | |
| lines.join("\n") |
Reference feature/shakapacker-yml-integration branch for future enhancement to automatically detect server_bundle_output_path from shakapacker.yml when using Shakapacker v8.5.0+. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Code Review for PR #1798Thank you for this comprehensive PR. I have reviewed the changes thoroughly. Strengths
Critical Issues to Address
Additional Improvements Needed
Overall AssessmentThis PR makes significant security improvements while maintaining backward compatibility. The core implementation is solid, but please address the bundle detection logic and add better error handling before merging. Great work on improving server-side rendering security! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
lib/react_on_rails/configuration.rb (2)
59-62: Consider implementing the TODO for automatic detection.The TODO comment mentions detecting
server_bundle_output_pathfromshakapacker.yml, which would enhance usability by reducing manual configuration. This could be valuable for future iterations.Would you like me to help track this as a separate issue or provide guidance on the implementation approach for automatic detection from Shakapacker configuration?
310-318: Consider the breaking change impact of raising errors.This method changed from issuing warnings to raising errors for the deprecated
generated_assets_dirsconfiguration. While the method name suggests configuration/warning behavior, it now enforces removal.Consider either:
- Renaming to
validate_generated_assets_dirs_removedto reflect the new behavior- Providing a deprecation period with warnings before the hard failure in a future release
The current approach may break existing configurations that were previously working with warnings.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
lib/react_on_rails/configuration.rb(8 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru}}
📄 CodeRabbit inference engine (CLAUDE.md)
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru}}: Before every commit/push, runbundle exec rubocopand fix all violations in Ruby code
Let RuboCop handle all Ruby code formatting; never manually format Ruby files
Files:
lib/react_on_rails/configuration.rb
{Gemfile,Rakefile,**/*.{rb,rake,gemspec,ru,js,jsx,ts,tsx,json,yml,yaml,md,css,scss}}
📄 CodeRabbit inference engine (CLAUDE.md)
Ensure all committed files end with a trailing newline character
Files:
lib/react_on_rails/configuration.rb
🧠 Learnings (3)
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
PR: shakacode/react_on_rails#1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.
Applied to files:
lib/react_on_rails/configuration.rb
📚 Learning: 2025-04-26T21:55:55.874Z
Learnt from: alexeyr-ci2
PR: shakacode/react_on_rails#1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.
Applied to files:
lib/react_on_rails/configuration.rb
📚 Learning: 2025-02-12T16:38:06.537Z
Learnt from: Romex91
PR: shakacode/react_on_rails#1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
Applied to files:
lib/react_on_rails/configuration.rb
🧬 Code graph analysis (1)
lib/react_on_rails/configuration.rb (1)
lib/react_on_rails/packer_utils.rb (7)
raise_shakapacker_version_incompatible_for_basic_pack_generation(157-164)supports_basic_pack_generation?(39-41)raise_shakapacker_version_incompatible_for_autobundling(146-155)supports_autobundling?(43-46)raise_nested_entries_disabled(136-144)nested_entries?(96-98)packer_public_output_path(100-102)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
- GitHub Check: dummy-app-integration-tests (3.4, 22)
- GitHub Check: dummy-app-integration-tests (3.2, 20)
- GitHub Check: rspec-package-tests (3.4, latest)
- GitHub Check: rspec-package-tests (3.4, minimum)
- GitHub Check: rspec-package-tests (3.2, minimum)
- GitHub Check: rspec-package-tests (3.2, latest)
- GitHub Check: build
- GitHub Check: claude-review
- GitHub Check: examples (3.2, minimum)
- GitHub Check: examples (3.4, latest)
🔇 Additional comments (9)
lib/react_on_rails/configuration.rb (9)
56-57: LGTM: New configuration attributes properly initialized.The addition of
server_bundle_output_pathandenforce_private_server_bundlesattributes with appropriate default values aligns with the PR objectives for improving server bundle security.
76-77: LGTM: Accessor declarations properly updated.The new attributes are correctly included in the
attr_accessordeclaration.
94-94: LGTM: Constructor signature updated correctly.The new parameters are properly added to the constructor with appropriate default values (
nil).
139-140: LGTM: Instance variables properly initialized.The constructor correctly assigns the new configuration parameters to instance variables.
156-156: LGTM: Validation properly integrated into setup flow.The new validation method is correctly added to the configuration setup process.
229-232: LGTM: Method properly defined.The
check_minimum_shakapacker_versionmethod correctly delegates toPackerUtilsfor version validation.
234-240: LGTM: Autobundling requirements properly validated.The method correctly checks all required conditions for autobundling: components subdirectory presence, Shakapacker version compatibility, and nested entries support.
279-279: LGTM: Messaging updated for Shakapacker terminology.The error messages have been appropriately updated to reference Shakapacker instead of generic webpack terminology, improving clarity.
Also applies to: 283-287
205-227: Fix validation to reject blank values and enhance path traversal security.The current validation has two issues:
Empty string bypasses validation: An empty
server_bundle_output_pathexpands toRails.root, which may still be inside the public directory, weakening the security guarantee.Missing directory traversal protection: The path expansion should validate that the resolved path doesn't escape the Rails root directory to prevent directory traversal attacks.
Apply this diff to address both issues:
- # Check if server_bundle_output_path is nil - if server_bundle_output_path.nil? + # Check if server_bundle_output_path is blank + if server_bundle_output_path.blank? raise ReactOnRails::Error, "enforce_private_server_bundles is set to true, but " \ - "server_bundle_output_path is nil. Please set server_bundle_output_path " \ - "to a directory outside of the public directory." + "server_bundle_output_path is blank. Please set server_bundle_output_path " \ + "to a private (non-public) directory." end # Check if server_bundle_output_path is inside public directory # Skip validation if Rails.root is not available (e.g., in tests) return unless defined?(Rails) && Rails.root public_path = Rails.root.join("public").to_s server_output_path = File.expand_path(server_bundle_output_path, Rails.root.to_s) + + # Ensure the expanded path does not escape Rails.root + rails_root_path = File.expand_path(Rails.root.to_s) + unless server_output_path.start_with?(rails_root_path + File::SEPARATOR) + raise ReactOnRails::Error, "enforce_private_server_bundles is set to true, but " \ + "server_bundle_output_path (#{server_bundle_output_path}) resolves " \ + "outside of the Rails.root directory. Please set it to a directory within Rails.root." + end return unless server_output_path.start_with?(public_path)
- Convert from raising error to Rails.logger.warn for better UX - Clarify message to distinguish between public and private assets - Explain that private server bundles are configured separately - Maintains backward compatibility while guiding users to new config 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Code Review for PR #1798: Fix bundle path resolution and improve server bundle securityThank you for this significant improvement to bundle path resolution and security! This PR addresses critical issues and modernizes the codebase. Here is my comprehensive review: ✅ Strengths1. Security EnhancementThe introduction of private server bundle paths (ssr-generated/) is excellent. Server bundles containing sensitive SSR/RSC logic should not be publicly accessible. The enforce_private_server_bundles option provides a migration path. 2. Clean Architecture
3. Robust Fallback LogicThe multi-tier fallback approach properly handles edge cases:
4. Comprehensive Test CoverageExcellent test coverage including:
5. Backwards CompatibilityThe changes maintain full backwards compatibility while providing a clear migration path. 🔍 Areas for Improvement1. Performance OptimizationThe bundle resolution now involves multiple File.exist? checks. Consider:
2. Configuration DocumentationWhile the configuration is well-structured, consider:
3. Edge Case HandlingConsider what happens when:
4. Deprecation PathThe PR removes generated_assets_dirs but keeps generated_assets_full_path as deprecated. Consider:
🎯 Specific Suggestions1. Add validation for path traversal# In configuration.rb setup_config_values
if server_bundle_output_path&.include?("../")
raise ReactOnRails::Error, "server_bundle_output_path cannot contain path traversal sequences"
end2. Consider adding debug logging# In handle_missing_manifest_entry
Rails.logger.debug("ReactOnRails: Trying bundle path: #{path}") if ReactOnRails.configuration.trace3. Performance metric helperConsider adding a helper to measure bundle resolution time in development: def self.measure_bundle_resolution(&block)
return yield unless Rails.env.development? && ReactOnRails.configuration.trace
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
result = yield
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
Rails.logger.debug("Bundle resolution took #{(elapsed * 1000).round(2)}ms")
result
end
|
Summary
This PR addresses critical issues with bundle path resolution and significantly improves security for server-side bundles.
Issues Fixed
Key Changes
🔧 Bundle Path Resolution Logic (Fixed #1797)
public/packs) → Generated assets path🔒 Enhanced Security Architecture
ssr-generated/generated/server-bundles/🧹 Code Modernization
Object.const_get(packer_type.capitalize)patternsShakapackerreferences (since we only support Shakapacker now)Architecture Improvements
Priority Order Fixes
Security Benefits
Directory Structure
Backwards Compatibility
Testing
Performance
Test Plan
🤖 Generated with Claude Code
This change is
Summary by CodeRabbit