Skip to content

Commit 82094f3

Browse files
justin808claude
andcommitted
Improve server bundle path configuration documentation and validation
Adds clear documentation and automated validation to prevent configuration mismatches between webpack and React on Rails configurations. **Documentation Improvements:** - Add prominent warnings in both webpack config and Rails initializer templates - Clarify that both server_bundle_output_path settings must match - Explain the security rationale (private directories prevent code exposure) - Update generator templates with cross-reference comments **Doctor Validation:** - Add automated check that compares webpack serverWebpackConfig.js output.path with React on Rails server_bundle_output_path configuration - Display success message when configs are in sync - Provide detailed warning with fix instructions when mismatch detected - Gracefully handles missing config files or parsing errors **Why This Matters:** Webpack (build-time) and React on Rails (runtime) need to agree on where server bundles are located. Misconfiguration causes SSR failures that can be hard to debug. This change makes the requirement explicit and adds automated validation via the doctor command. **Technical Notes:** - Validation runs as part of `rails react_on_rails:doctor` analysis - Parses webpack config to extract output.path value - Compares paths relative to Rails.root - No breaking changes - pure documentation and tooling improvement Related to closed PR #1808 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 13a1aca commit 82094f3

File tree

3 files changed

+82
-5
lines changed

3 files changed

+82
-5
lines changed

lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,14 @@ ReactOnRails.configure do |config|
4343
#
4444
config.server_bundle_js_file = "server-bundle.js"
4545

46+
# ⚠️ IMPORTANT: This must match output.path in config/webpack/serverWebpackConfig.js
47+
#
48+
# Both are currently set to 'ssr-generated' (relative to Rails.root)
49+
# Keeping these in sync ensures React on Rails can find the server bundle at runtime.
50+
#
4651
# Configure where server bundles are output. Defaults to "ssr-generated".
47-
# This should match your webpack configuration for server bundles.
52+
# This path is relative to Rails.root and should point to a private directory
53+
# (outside of public/) for security.
4854
config.server_bundle_output_path = "ssr-generated"
4955

5056
# Enforce that server bundles are only loaded from private (non-public) directories.

lib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,15 @@ const configureServer = () => {
4444
};
4545
serverWebpackConfig.plugins.unshift(new bundler.optimize.LimitChunkCountPlugin({ maxChunks: 1 }));
4646

47-
// Custom output for the server-bundle that matches the config in
48-
// config/initializers/react_on_rails.rb
49-
// Server bundles are output to a private directory (not public) for security
47+
// Custom output for the server-bundle
48+
// ⚠️ IMPORTANT: This output.path must match server_bundle_output_path in
49+
// config/initializers/react_on_rails.rb
50+
//
51+
// Both are currently set to 'ssr-generated' (relative to Rails.root)
52+
// Keeping these in sync ensures React on Rails can find the server bundle at runtime.
53+
//
54+
// Server bundles are output to a private directory (not public) for security.
55+
// This prevents server-side code from being exposed via the web server.
5056
serverWebpackConfig.output = {
5157
filename: 'server-bundle.js',
5258
globalObject: 'this',

lib/react_on_rails/doctor.rb

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,7 @@ def check_react_on_rails_initializer
664664
end
665665
end
666666

667+
# rubocop:disable Metrics/CyclomaticComplexity
667668
def analyze_server_rendering_config(content)
668669
checker.add_info("\n🖥️ Server Rendering:")
669670

@@ -675,6 +676,18 @@ def analyze_server_rendering_config(content)
675676
checker.add_info(" server_bundle_js_file: server-bundle.js (default)")
676677
end
677678

679+
# Server bundle output path
680+
server_bundle_path_match = content.match(/config\.server_bundle_output_path\s*=\s*["']([^"']+)["']/)
681+
rails_bundle_path = server_bundle_path_match ? server_bundle_path_match[1] : "ssr-generated"
682+
checker.add_info(" server_bundle_output_path: #{rails_bundle_path}")
683+
684+
# Enforce private server bundles
685+
enforce_private_match = content.match(/config\.enforce_private_server_bundles\s*=\s*([^\s\n,]+)/)
686+
checker.add_info(" enforce_private_server_bundles: #{enforce_private_match[1]}") if enforce_private_match
687+
688+
# Validate webpack config matches Rails config
689+
validate_server_bundle_path_sync(rails_bundle_path)
690+
678691
# RSC bundle file (Pro feature)
679692
rsc_bundle_match = content.match(/config\.rsc_bundle_js_file\s*=\s*["']([^"']+)["']/)
680693
if rsc_bundle_match
@@ -699,7 +712,7 @@ def analyze_server_rendering_config(content)
699712

700713
checker.add_info(" raise_on_prerender_error: #{raise_on_error_match[1]}")
701714
end
702-
# rubocop:enable Metrics/AbcSize
715+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
703716

704717
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
705718
def analyze_performance_config(content)
@@ -1144,6 +1157,58 @@ def safe_display_config_value(label, config, method_name)
11441157
checker.add_info(" #{label}: <error reading value: #{e.message}>")
11451158
end
11461159
end
1160+
1161+
# Validates that webpack serverWebpackConfig.js output.path matches
1162+
# React on Rails config.server_bundle_output_path
1163+
def validate_server_bundle_path_sync(rails_bundle_path)
1164+
webpack_config_path = "config/webpack/serverWebpackConfig.js"
1165+
1166+
unless File.exist?(webpack_config_path)
1167+
checker.add_info("\n ℹ️ Webpack server config not found - skipping path validation")
1168+
return
1169+
end
1170+
1171+
begin
1172+
webpack_content = File.read(webpack_config_path)
1173+
1174+
# Extract the path from webpack config
1175+
# Look for: path: require('path').resolve(__dirname, '../../ssr-generated')
1176+
path_regex = %r{path:\s*require\(['"]path['"]\)\.resolve\(__dirname,\s*['"]\.\./\.\./([^'"]+)['"]\)}
1177+
path_match = webpack_content.match(path_regex)
1178+
1179+
unless path_match
1180+
checker.add_info("\n ℹ️ Could not parse webpack server bundle path - skipping validation")
1181+
return
1182+
end
1183+
1184+
webpack_bundle_path = path_match[1]
1185+
1186+
# Compare the paths
1187+
if webpack_bundle_path == rails_bundle_path
1188+
checker.add_success("\n ✅ Webpack and Rails configs are in sync (both use '#{rails_bundle_path}')")
1189+
else
1190+
checker.add_warning(<<~MSG.strip)
1191+
\n ⚠️ Configuration mismatch detected!
1192+
1193+
React on Rails config (config/initializers/react_on_rails.rb):
1194+
server_bundle_output_path = "#{rails_bundle_path}"
1195+
1196+
Webpack config (#{webpack_config_path}):
1197+
output.path = "#{webpack_bundle_path}" (relative to Rails.root)
1198+
1199+
These must match for server rendering to work correctly.
1200+
1201+
To fix:
1202+
1. Update server_bundle_output_path in config/initializers/react_on_rails.rb, OR
1203+
2. Update output.path in #{webpack_config_path}
1204+
1205+
Make sure both point to the same directory relative to Rails.root.
1206+
MSG
1207+
end
1208+
rescue StandardError => e
1209+
checker.add_info("\n ℹ️ Could not validate webpack config: #{e.message}")
1210+
end
1211+
end
11471212
end
11481213
# rubocop:enable Metrics/ClassLength
11491214
end

0 commit comments

Comments
 (0)