diff --git a/lib/react_on_rails/configuration.rb b/lib/react_on_rails/configuration.rb index 6d89cd071e..e3efa2c29c 100644 --- a/lib/react_on_rails/configuration.rb +++ b/lib/react_on_rails/configuration.rb @@ -41,8 +41,6 @@ def self.configuration components_subdirectory: nil, make_generated_server_bundle_the_entrypoint: false, defer_generated_component_packs: false, - # React on Rails Pro (licensed) feature - enables immediate hydration of React components - immediate_hydration: false, # Maximum time in milliseconds to wait for client-side component registration after page load. # If exceeded, an error will be thrown for server-side rendered components not registered on the client. # Set to 0 to disable the timeout and wait indefinitely for component registration. @@ -64,7 +62,7 @@ class Configuration :server_render_method, :random_dom_id, :auto_load_bundle, :same_bundle_for_client_and_server, :rendering_props_extension, :make_generated_server_bundle_the_entrypoint, - :generated_component_packs_loading_strategy, :immediate_hydration, + :generated_component_packs_loading_strategy, :component_registry_timeout, :server_bundle_output_path, :enforce_private_server_bundles @@ -81,7 +79,7 @@ def initialize(node_modules_location: nil, server_bundle_js_file: nil, prerender same_bundle_for_client_and_server: nil, i18n_dir: nil, i18n_yml_dir: nil, i18n_output_format: nil, i18n_yml_safe_load_options: nil, random_dom_id: nil, server_render_method: nil, rendering_props_extension: nil, - components_subdirectory: nil, auto_load_bundle: nil, immediate_hydration: nil, + components_subdirectory: nil, auto_load_bundle: nil, component_registry_timeout: nil, server_bundle_output_path: nil, enforce_private_server_bundles: nil) self.node_modules_location = node_modules_location.present? ? node_modules_location : Rails.root self.generated_assets_dirs = generated_assets_dirs @@ -122,7 +120,6 @@ def initialize(node_modules_location: nil, server_bundle_js_file: nil, prerender self.auto_load_bundle = auto_load_bundle self.make_generated_server_bundle_the_entrypoint = make_generated_server_bundle_the_entrypoint self.defer_generated_component_packs = defer_generated_component_packs - self.immediate_hydration = immediate_hydration self.generated_component_packs_loading_strategy = generated_component_packs_loading_strategy self.server_bundle_output_path = server_bundle_output_path self.enforce_private_server_bundles = enforce_private_server_bundles diff --git a/lib/react_on_rails/controller.rb b/lib/react_on_rails/controller.rb index ae254b1a5a..d56c7f3deb 100644 --- a/lib/react_on_rails/controller.rb +++ b/lib/react_on_rails/controller.rb @@ -15,7 +15,7 @@ module Controller # Be sure to include view helper `redux_store_hydration_data` at the end of your layout or view # or else there will be no client side hydration of your stores. def redux_store(store_name, props: {}, immediate_hydration: nil) - immediate_hydration = ReactOnRails.configuration.immediate_hydration if immediate_hydration.nil? + immediate_hydration = ReactOnRails::ProUtils.immediate_hydration_config if immediate_hydration.nil? redux_store_data = { store_name: store_name, props: props, immediate_hydration: immediate_hydration } diff --git a/lib/react_on_rails/doctor.rb b/lib/react_on_rails/doctor.rb index 54ed06dbec..342629ac93 100644 --- a/lib/react_on_rails/doctor.rb +++ b/lib/react_on_rails/doctor.rb @@ -633,6 +633,7 @@ def check_shakapacker_configuration_details def check_react_on_rails_configuration_details check_react_on_rails_initializer + check_react_on_rails_pro_initializer check_deprecated_configuration_settings check_breaking_changes_warnings end @@ -664,6 +665,29 @@ def check_react_on_rails_initializer end end + def check_react_on_rails_pro_initializer + config_path = "config/initializers/react_on_rails_pro.rb" + + return unless File.exist?(config_path) + + begin + content = File.read(config_path) + + checker.add_info("\n📋 React on Rails Pro Configuration:") + checker.add_info("📍 Documentation: https://www.shakacode.com/react-on-rails-pro/") + + # Immediate hydration (Pro feature) + immediate_hydration_match = content.match(/config\.immediate_hydration\s*=\s*([^\s\n,]+)/) + if immediate_hydration_match + checker.add_info(" immediate_hydration: #{immediate_hydration_match[1]}") + else + checker.add_info(" immediate_hydration: false (default)") + end + rescue StandardError => e + checker.add_warning("⚠️ Unable to read react_on_rails_pro.rb: #{e.message}") + end + end + def analyze_server_rendering_config(content) checker.add_info("\n🖥️ Server Rendering:") @@ -701,7 +725,7 @@ def analyze_server_rendering_config(content) end # rubocop:enable Metrics/AbcSize - # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity + # rubocop:disable Metrics/CyclomaticComplexity def analyze_performance_config(content) checker.add_info("\n⚡ Performance & Loading:") @@ -732,19 +756,13 @@ def analyze_performance_config(content) auto_load_match = content.match(/config\.auto_load_bundle\s*=\s*([^\s\n,]+)/) checker.add_info(" auto_load_bundle: #{auto_load_match[1]}") if auto_load_match - # Immediate hydration (Pro feature) - immediate_hydration_match = content.match(/config\.immediate_hydration\s*=\s*([^\s\n,]+)/) - if immediate_hydration_match - checker.add_info(" immediate_hydration: #{immediate_hydration_match[1]} (React on Rails Pro)") - end - # Component registry timeout timeout_match = content.match(/config\.component_registry_timeout\s*=\s*([^\s\n,]+)/) return unless timeout_match checker.add_info(" component_registry_timeout: #{timeout_match[1]}ms") end - # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity + # rubocop:enable Metrics/CyclomaticComplexity # rubocop:disable Metrics/AbcSize def analyze_development_config(content) diff --git a/lib/react_on_rails/helper.rb b/lib/react_on_rails/helper.rb index 92f4e0c375..b354812c38 100644 --- a/lib/react_on_rails/helper.rb +++ b/lib/react_on_rails/helper.rb @@ -158,7 +158,7 @@ def react_component_hash(component_name, options = {}) # immediate_hydration: false -- React on Rails Pro (licensed) feature. Pass as true if you wish to # hydrate this store immediately instead of waiting for the page to load. def redux_store(store_name, props: {}, defer: false, immediate_hydration: nil) - immediate_hydration = ReactOnRails.configuration.immediate_hydration if immediate_hydration.nil? + immediate_hydration = ReactOnRails::ProUtils.immediate_hydration_config if immediate_hydration.nil? redux_store_data = { store_name: store_name, props: props, diff --git a/lib/react_on_rails/pro_utils.rb b/lib/react_on_rails/pro_utils.rb index bb222630cd..0ea4966249 100644 --- a/lib/react_on_rails/pro_utils.rb +++ b/lib/react_on_rails/pro_utils.rb @@ -10,6 +10,14 @@ def self.support_pro_features? ReactOnRails::Utils.react_on_rails_pro? end + # Returns the immediate_hydration configuration value + # @return [Boolean] immediate_hydration setting from Pro config if Pro is available, false otherwise + def self.immediate_hydration_config + return false unless support_pro_features? + + ReactOnRailsPro.configuration.immediate_hydration + end + def self.disable_pro_render_options_if_not_licensed(raw_options) return raw_options if support_pro_features? @@ -18,7 +26,8 @@ def self.disable_pro_render_options_if_not_licensed(raw_options) PRO_ONLY_OPTIONS.each do |option| # Determine if this option is enabled (either explicitly or via global config) option_enabled = if raw_options[option].nil? - ReactOnRails.configuration.send(option) + # Use the Pro config helper to get the global config value + immediate_hydration_config else raw_options[option] end diff --git a/lib/react_on_rails/react_component/render_options.rb b/lib/react_on_rails/react_component/render_options.rb index b6bc5f5fe3..b2147a16e2 100644 --- a/lib/react_on_rails/react_component/render_options.rb +++ b/lib/react_on_rails/react_component/render_options.rb @@ -97,7 +97,9 @@ def logging_on_server end def immediate_hydration - retrieve_configuration_value_for(:immediate_hydration) + options.fetch(:immediate_hydration) do + ReactOnRails::ProUtils.immediate_hydration_config + end end def to_s diff --git a/react_on_rails_pro/lib/react_on_rails_pro/configuration.rb b/react_on_rails_pro/lib/react_on_rails_pro/configuration.rb index 12d913a457..bf4d8275b6 100644 --- a/react_on_rails_pro/lib/react_on_rails_pro/configuration.rb +++ b/react_on_rails_pro/lib/react_on_rails_pro/configuration.rb @@ -32,7 +32,8 @@ def self.configuration rsc_payload_generation_url_path: Configuration::DEFAULT_RSC_PAYLOAD_GENERATION_URL_PATH, rsc_bundle_js_file: Configuration::DEFAULT_RSC_BUNDLE_JS_FILE, react_client_manifest_file: Configuration::DEFAULT_REACT_CLIENT_MANIFEST_FILE, - react_server_client_manifest_file: Configuration::DEFAULT_REACT_SERVER_CLIENT_MANIFEST_FILE + react_server_client_manifest_file: Configuration::DEFAULT_REACT_SERVER_CLIENT_MANIFEST_FILE, + immediate_hydration: Configuration::DEFAULT_IMMEDIATE_HYDRATION ) end @@ -59,6 +60,7 @@ class Configuration # rubocop:disable Metrics/ClassLength DEFAULT_RSC_BUNDLE_JS_FILE = "rsc-bundle.js" DEFAULT_REACT_CLIENT_MANIFEST_FILE = "react-client-manifest.json" DEFAULT_REACT_SERVER_CLIENT_MANIFEST_FILE = "react-server-client-manifest.json" + DEFAULT_IMMEDIATE_HYDRATION = false attr_accessor :renderer_url, :renderer_password, :tracing, :server_renderer, :renderer_use_fallback_exec_js, :prerender_caching, @@ -68,7 +70,7 @@ class Configuration # rubocop:disable Metrics/ClassLength :renderer_request_retry_limit, :throw_js_errors, :ssr_timeout, :profile_server_rendering_js_code, :raise_non_shell_server_rendering_errors, :enable_rsc_support, :rsc_payload_generation_url_path, :rsc_bundle_js_file, :react_client_manifest_file, - :react_server_client_manifest_file + :react_server_client_manifest_file, :immediate_hydration def initialize(renderer_url: nil, renderer_password: nil, server_renderer: nil, # rubocop:disable Metrics/AbcSize renderer_use_fallback_exec_js: nil, prerender_caching: nil, @@ -79,7 +81,8 @@ def initialize(renderer_url: nil, renderer_password: nil, server_renderer: nil, renderer_request_retry_limit: nil, throw_js_errors: nil, ssr_timeout: nil, profile_server_rendering_js_code: nil, raise_non_shell_server_rendering_errors: nil, enable_rsc_support: nil, rsc_payload_generation_url_path: nil, - rsc_bundle_js_file: nil, react_client_manifest_file: nil, react_server_client_manifest_file: nil) + rsc_bundle_js_file: nil, react_client_manifest_file: nil, react_server_client_manifest_file: nil, + immediate_hydration: nil) self.renderer_url = renderer_url self.renderer_password = renderer_password self.server_renderer = server_renderer @@ -105,6 +108,7 @@ def initialize(renderer_url: nil, renderer_password: nil, server_renderer: nil, self.rsc_bundle_js_file = rsc_bundle_js_file self.react_client_manifest_file = react_client_manifest_file self.react_server_client_manifest_file = react_server_client_manifest_file + self.immediate_hydration = immediate_hydration end def setup_config_values diff --git a/spec/dummy/config/initializers/react_on_rails.rb b/spec/dummy/config/initializers/react_on_rails.rb index 54c2f40d5c..83598f028a 100644 --- a/spec/dummy/config/initializers/react_on_rails.rb +++ b/spec/dummy/config/initializers/react_on_rails.rb @@ -41,6 +41,5 @@ def self.adjust_props_for_client_side_hydration(component_name, props) config.rendering_props_extension = RenderingPropsExtension config.components_subdirectory = "startup" config.auto_load_bundle = true - config.immediate_hydration = false config.generated_component_packs_loading_strategy = :defer end diff --git a/spec/dummy/spec/helpers/react_on_rails_helper_spec.rb b/spec/dummy/spec/helpers/react_on_rails_helper_spec.rb index 3b36f0e362..d6df003c36 100644 --- a/spec/dummy/spec/helpers/react_on_rails_helper_spec.rb +++ b/spec/dummy/spec/helpers/react_on_rails_helper_spec.rb @@ -38,15 +38,8 @@ def self.pro_attribution_comment stub_const("ReactOnRailsPro", pro_module) stub_const("ReactOnRailsPro::Utils", utils_module) - # Configure immediate_hydration to true for tests since they expect that behavior - ReactOnRails.configure do |config| - config.immediate_hydration = true - end - end - - after do - # Reset to default - avoid validation issues by setting directly - ReactOnRails.configuration.immediate_hydration = false + # Stub immediate_hydration to true for tests since they expect that behavior + allow(ReactOnRails::ProUtils).to receive(:immediate_hydration_config).and_return(true) end let(:hash) do diff --git a/spec/dummy/spec/system/integration_spec.rb b/spec/dummy/spec/system/integration_spec.rb index 57212d1ca0..0464dc8a03 100644 --- a/spec/dummy/spec/system/integration_spec.rb +++ b/spec/dummy/spec/system/integration_spec.rb @@ -104,12 +104,9 @@ def self.pro_attribution_comment end stub_const("ReactOnRailsPro", pro_module) stub_const("ReactOnRailsPro::Utils", utils_module) - end - around do |example| - ReactOnRails.configure { |config| config.immediate_hydration = true } - example.run - ReactOnRails.configure { |config| config.immediate_hydration = false } + # Stub immediate_hydration to true since these tests expect that behavior + allow(ReactOnRails::ProUtils).to receive(:immediate_hydration_config).and_return(true) end end