Skip to content

Commit ee74d03

Browse files
justin808claude
andcommitted
Add validation and error messaging for hardcoded server bundle paths
Addresses review feedback to provide clear error messages when using hardcoded paths with older Shakapacker versions. Changes: - Add runtime validation in serverWebpackConfig.js template for Shakapacker < 9.0 - Display helpful warning with configuration instructions if path doesn't exist - Extract hardcoded "ssr-generated" to DEFAULT_SERVER_BUNDLE_OUTPUT_PATH constant - Improve logging: use debug level for auto-detection (not user-initiated) - Add documentation about absolute paths in normalize_to_relative_path The validation helps users quickly identify configuration mismatches and provides actionable guidance to fix them using rails react_on_rails:doctor. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent e59e878 commit ee74d03

File tree

4 files changed

+31
-17
lines changed

4 files changed

+31
-17
lines changed

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

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,31 +45,39 @@ const configureServer = () => {
4545
serverWebpackConfig.plugins.unshift(new bundler.optimize.LimitChunkCountPlugin({ maxChunks: 1 }));
4646

4747
// Custom output for the server-bundle
48-
//<% if shakapacker_version_9_or_higher? %>
48+
<% if shakapacker_version_9_or_higher? -%>
4949
// Using Shakapacker 9.0+ privateOutputPath for automatic sync with shakapacker.yml
5050
// This eliminates manual path configuration and keeps configs in sync.
51-
serverWebpackConfig.output = {
52-
filename: 'server-bundle.js',
53-
globalObject: 'this',
54-
// If using the React on Rails Pro node server renderer, uncomment the next line
55-
// libraryTarget: 'commonjs2',
56-
path: config.privateOutputPath,
57-
// No publicPath needed since server bundles are not served via web
58-
// https://webpack.js.org/configuration/output/#outputglobalobject
59-
};<% else %>
51+
<% else -%>
6052
// Using hardcoded path (Shakapacker < 9.0)
6153
// For Shakapacker 9.0+, consider using config.privateOutputPath instead
6254
// to automatically sync with shakapacker.yml private_output_path.
55+
<% end -%>
6356
serverWebpackConfig.output = {
6457
filename: 'server-bundle.js',
6558
globalObject: 'this',
6659
// If using the React on Rails Pro node server renderer, uncomment the next line
6760
// libraryTarget: 'commonjs2',
68-
path: require('path').resolve(__dirname, '../../ssr-generated'),
61+
path: <%= shakapacker_version_9_or_higher? ? 'config.privateOutputPath' : "require('path').resolve(__dirname, '../../ssr-generated')" %>,
6962
// No publicPath needed since server bundles are not served via web
7063
// https://webpack.js.org/configuration/output/#outputglobalobject
71-
};<% end %>
64+
};
65+
66+
<% unless shakapacker_version_9_or_higher? -%>
67+
// Validate hardcoded output path exists or can be created
68+
// For Shakapacker < 9.0, we use a hardcoded path. To sync with Rails config:
69+
// 1. Ensure config/initializers/react_on_rails.rb has: config.server_bundle_output_path = "ssr-generated"
70+
// 2. Run: rails react_on_rails:doctor to verify configuration
71+
const fs = require('fs');
72+
const serverBundlePath = serverWebpackConfig.output.path;
73+
if (!fs.existsSync(serverBundlePath)) {
74+
console.warn(`⚠️ Server bundle output directory does not exist: ${serverBundlePath}`);
75+
console.warn(' It will be created during build, but ensure React on Rails is configured:');
76+
console.warn(' config.server_bundle_output_path = "ssr-generated" in config/initializers/react_on_rails.rb');
77+
console.warn(' Run: rails react_on_rails:doctor to validate your configuration');
78+
}
7279

80+
<% end -%>
7381
// Don't hash the server bundle b/c would conflict with the client manifest
7482
// And no need for the MiniCssExtractPlugin
7583
serverWebpackConfig.plugins = serverWebpackConfig.plugins.filter(

lib/react_on_rails/configuration.rb

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ def self.configure
1010

1111
DEFAULT_GENERATED_ASSETS_DIR = File.join(%w[public webpack], Rails.env).freeze
1212
DEFAULT_COMPONENT_REGISTRY_TIMEOUT = 5000
13+
DEFAULT_SERVER_BUNDLE_OUTPUT_PATH = "ssr-generated"
1314

1415
def self.configuration
1516
@configuration ||= Configuration.new(
@@ -46,7 +47,7 @@ def self.configuration
4647
# Set to 0 to disable the timeout and wait indefinitely for component registration.
4748
component_registry_timeout: DEFAULT_COMPONENT_REGISTRY_TIMEOUT,
4849
generated_component_packs_loading_strategy: nil,
49-
server_bundle_output_path: "ssr-generated",
50+
server_bundle_output_path: DEFAULT_SERVER_BUNDLE_OUTPUT_PATH,
5051
enforce_private_server_bundles: false
5152
)
5253
end
@@ -263,7 +264,7 @@ def validate_enforce_private_server_bundles
263264
# rubocop:disable Metrics/CyclomaticComplexity
264265
def auto_detect_server_bundle_path_from_shakapacker
265266
# Skip if user explicitly set server_bundle_output_path to something other than default
266-
return if server_bundle_output_path != "ssr-generated"
267+
return if server_bundle_output_path != ReactOnRails::DEFAULT_SERVER_BUNDLE_OUTPUT_PATH
267268

268269
# Skip if Shakapacker is not available
269270
return unless defined?(::Shakapacker)
@@ -279,8 +280,8 @@ def auto_detect_server_bundle_path_from_shakapacker
279280
relative_path = ReactOnRails::Utils.normalize_to_relative_path(private_path)
280281
self.server_bundle_output_path = relative_path
281282

282-
Rails.logger&.info("ReactOnRails: Auto-detected server_bundle_output_path from " \
283-
"shakapacker.yml private_output_path: '#{relative_path}'")
283+
Rails.logger&.debug("ReactOnRails: Auto-detected server_bundle_output_path from " \
284+
"shakapacker.yml private_output_path: '#{relative_path}'")
284285
rescue StandardError => e
285286
# Fail gracefully - if auto-detection fails, keep the default
286287
Rails.logger&.debug("ReactOnRails: Could not auto-detect server bundle path from " \

lib/react_on_rails/doctor.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,8 @@ def analyze_server_rendering_config(content)
681681

682682
# Server bundle output path
683683
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"
684+
default_path = ReactOnRails::DEFAULT_SERVER_BUNDLE_OUTPUT_PATH
685+
rails_bundle_path = server_bundle_path_match ? server_bundle_path_match[1] : default_path
685686
checker.add_info(" server_bundle_output_path: #{rails_bundle_path}")
686687

687688
# Enforce private server bundles

lib/react_on_rails/utils.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,12 +446,16 @@ def self.package_manager_remove_command(package_name)
446446
# Converts an absolute path (String or Pathname) to a path relative to Rails.root.
447447
# If the path is already relative or doesn't contain Rails.root, returns it as-is.
448448
#
449+
# Note: Absolute paths that don't start with Rails.root are intentionally passed through
450+
# unchanged. This allows for explicit absolute paths to directories outside the Rails app.
451+
#
449452
# @param path [String, Pathname] The path to normalize
450453
# @return [String] The relative path as a string
451454
#
452455
# @example
453456
# normalize_to_relative_path("/app/ssr-generated") # => "ssr-generated"
454457
# normalize_to_relative_path("ssr-generated") # => "ssr-generated"
458+
# normalize_to_relative_path("/other/path/bundles") # => "/other/path/bundles" (unchanged)
455459
def self.normalize_to_relative_path(path)
456460
return nil if path.nil?
457461

0 commit comments

Comments
 (0)