Skip to content

Commit 8a591dc

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 37f5894 commit 8a591dc

File tree

3 files changed

+90
-4
lines changed

3 files changed

+90
-4
lines changed

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,21 @@ ReactOnRails.configure do |config|
1212
# Set to "" if you're not using server rendering
1313
config.server_bundle_js_file = "server-bundle.js"
1414

15+
# ⚠️ IMPORTANT: This must match output.path in config/webpack/serverWebpackConfig.js
16+
#
17+
# Both are currently set to 'ssr-generated' (relative to Rails.root)
18+
# Keeping these in sync ensures React on Rails can find the server bundle at runtime.
19+
#
20+
# Configure where server bundles are output. Defaults to "ssr-generated".
21+
# This path is relative to Rails.root and should point to a private directory
22+
# (outside of public/) for security.
23+
config.server_bundle_output_path = "ssr-generated"
24+
25+
# Enforce that server bundles are only loaded from private (non-public) directories.
26+
# When true, server bundles will only be loaded from the configured server_bundle_output_path.
27+
# This is recommended for production to prevent server-side code from being exposed.
28+
config.enforce_private_server_bundles = true
29+
1530
################################################################################
1631
# Test Configuration (Optional)
1732
################################################################################

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
@@ -667,6 +667,7 @@ def check_react_on_rails_initializer
667667
end
668668
end
669669

670+
# rubocop:disable Metrics/CyclomaticComplexity
670671
def analyze_server_rendering_config(content)
671672
checker.add_info("\n🖥️ Server Rendering:")
672673

@@ -678,6 +679,18 @@ def analyze_server_rendering_config(content)
678679
checker.add_info(" server_bundle_js_file: server-bundle.js (default)")
679680
end
680681

682+
# Server bundle output path
683+
server_bundle_path_match = content.match(/config\.server_bundle_output_path\s*=\s*["']([^"']+)["']/)
684+
rails_bundle_path = server_bundle_path_match ? server_bundle_path_match[1] : "ssr-generated"
685+
checker.add_info(" server_bundle_output_path: #{rails_bundle_path}")
686+
687+
# Enforce private server bundles
688+
enforce_private_match = content.match(/config\.enforce_private_server_bundles\s*=\s*([^\s\n,]+)/)
689+
checker.add_info(" enforce_private_server_bundles: #{enforce_private_match[1]}") if enforce_private_match
690+
691+
# Validate webpack config matches Rails config
692+
validate_server_bundle_path_sync(rails_bundle_path)
693+
681694
# RSC bundle file (Pro feature)
682695
rsc_bundle_match = content.match(/config\.rsc_bundle_js_file\s*=\s*["']([^"']+)["']/)
683696
if rsc_bundle_match
@@ -702,7 +715,7 @@ def analyze_server_rendering_config(content)
702715

703716
checker.add_info(" raise_on_prerender_error: #{raise_on_error_match[1]}")
704717
end
705-
# rubocop:enable Metrics/AbcSize
718+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
706719

707720
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
708721
def analyze_performance_config(content)
@@ -1391,6 +1404,58 @@ def log_debug(message)
13911404

13921405
Rails.logger.debug(message)
13931406
end
1407+
1408+
# Validates that webpack serverWebpackConfig.js output.path matches
1409+
# React on Rails config.server_bundle_output_path
1410+
def validate_server_bundle_path_sync(rails_bundle_path)
1411+
webpack_config_path = "config/webpack/serverWebpackConfig.js"
1412+
1413+
unless File.exist?(webpack_config_path)
1414+
checker.add_info("\n ℹ️ Webpack server config not found - skipping path validation")
1415+
return
1416+
end
1417+
1418+
begin
1419+
webpack_content = File.read(webpack_config_path)
1420+
1421+
# Extract the path from webpack config
1422+
# Look for: path: require('path').resolve(__dirname, '../../ssr-generated')
1423+
path_regex = %r{path:\s*require\(['"]path['"]\)\.resolve\(__dirname,\s*['"]\.\./\.\./([^'"]+)['"]\)}
1424+
path_match = webpack_content.match(path_regex)
1425+
1426+
unless path_match
1427+
checker.add_info("\n ℹ️ Could not parse webpack server bundle path - skipping validation")
1428+
return
1429+
end
1430+
1431+
webpack_bundle_path = path_match[1]
1432+
1433+
# Compare the paths
1434+
if webpack_bundle_path == rails_bundle_path
1435+
checker.add_success("\n ✅ Webpack and Rails configs are in sync (both use '#{rails_bundle_path}')")
1436+
else
1437+
checker.add_warning(<<~MSG.strip)
1438+
\n ⚠️ Configuration mismatch detected!
1439+
1440+
React on Rails config (config/initializers/react_on_rails.rb):
1441+
server_bundle_output_path = "#{rails_bundle_path}"
1442+
1443+
Webpack config (#{webpack_config_path}):
1444+
output.path = "#{webpack_bundle_path}" (relative to Rails.root)
1445+
1446+
These must match for server rendering to work correctly.
1447+
1448+
To fix:
1449+
1. Update server_bundle_output_path in config/initializers/react_on_rails.rb, OR
1450+
2. Update output.path in #{webpack_config_path}
1451+
1452+
Make sure both point to the same directory relative to Rails.root.
1453+
MSG
1454+
end
1455+
rescue StandardError => e
1456+
checker.add_info("\n ℹ️ Could not validate webpack config: #{e.message}")
1457+
end
1458+
end
13941459
end
13951460
# rubocop:enable Metrics/ClassLength
13961461
end

0 commit comments

Comments
 (0)