From ec29344be1756aeb0c713c976bd7d298ac45f210 Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Fri, 7 Nov 2025 18:52:18 -1000 Subject: [PATCH 1/4] Add RBS type signatures for React on Rails gem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds comprehensive RBS (Ruby Signature) type definitions for the React on Rails gem to improve type safety and IDE support. Changes: - Add RBS gem to development dependencies - Create sig/ directory with type signatures for core modules: - ReactOnRails module and main classes - Configuration class with all attributes and methods - Helper module with view helper methods - ServerRenderingPool module - Utils, PackerUtils, TestHelper modules - Controller, GitUtils, VersionChecker modules - Add rake tasks for RBS validation (rbs:validate, rbs:check, rbs:list) - Include README documentation for RBS usage Benefits: - Better IDE autocomplete and IntelliSense support - Static type checking with tools like Steep - Improved API documentation - Early detection of type-related bugs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- Gemfile.development_dependencies | 1 + Gemfile.lock | 3 + rakelib/rbs.rake | 32 +++++++ sig/README.md | 52 +++++++++++ sig/react_on_rails.rbs | 36 ++++++++ sig/react_on_rails/configuration.rbs | 93 ++++++++++++++++++++ sig/react_on_rails/controller.rbs | 11 +++ sig/react_on_rails/git_utils.rbs | 8 ++ sig/react_on_rails/helper.rbs | 57 ++++++++++++ sig/react_on_rails/packer_utils.rbs | 15 ++++ sig/react_on_rails/server_rendering_pool.rbs | 12 +++ sig/react_on_rails/test_helper.rbs | 11 +++ sig/react_on_rails/utils.rbs | 22 +++++ sig/react_on_rails/version_checker.rbs | 12 +++ 14 files changed, 365 insertions(+) create mode 100644 rakelib/rbs.rake create mode 100644 sig/README.md create mode 100644 sig/react_on_rails.rbs create mode 100644 sig/react_on_rails/configuration.rbs create mode 100644 sig/react_on_rails/controller.rbs create mode 100644 sig/react_on_rails/git_utils.rbs create mode 100644 sig/react_on_rails/helper.rbs create mode 100644 sig/react_on_rails/packer_utils.rbs create mode 100644 sig/react_on_rails/server_rendering_pool.rbs create mode 100644 sig/react_on_rails/test_helper.rbs create mode 100644 sig/react_on_rails/utils.rbs create mode 100644 sig/react_on_rails/version_checker.rbs diff --git a/Gemfile.development_dependencies b/Gemfile.development_dependencies index a16a1c084a..8d0e4ca98c 100644 --- a/Gemfile.development_dependencies +++ b/Gemfile.development_dependencies @@ -33,6 +33,7 @@ group :development, :test do gem "pry-doc" gem "pry-rails" gem "pry-rescue" + gem "rbs", require: false gem "rubocop", "1.61.0", require: false gem "rubocop-performance", "~>1.20.0", require: false gem "rubocop-rspec", "~>2.26", require: false diff --git a/Gemfile.lock b/Gemfile.lock index 7f475c71dd..4656156b8e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -267,6 +267,8 @@ GEM rb-fsevent (0.11.2) rb-inotify (0.10.1) ffi (~> 1.0) + rbs (3.9.5) + logger rdoc (6.15.1) erb psych (>= 4.0.0) @@ -437,6 +439,7 @@ DEPENDENCIES pry-rescue puma (~> 6.0) rails (~> 7.1) + rbs react_on_rails! rspec-rails rspec-retry diff --git a/rakelib/rbs.rake b/rakelib/rbs.rake new file mode 100644 index 0000000000..779870ee6d --- /dev/null +++ b/rakelib/rbs.rake @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +namespace :rbs do + desc "Validate RBS type signatures" + task :validate do + require "rbs" + require "rbs/cli" + + puts "Validating RBS type signatures..." + + # Run RBS validate + result = system("bundle exec rbs -I sig validate") + + if result + puts "✓ RBS validation passed" + else + puts "✗ RBS validation failed" + exit 1 + end + end + + desc "Check RBS type signatures (alias for validate)" + task check: :validate + + desc "List all RBS files" + task :list do + sig_files = Dir.glob("sig/**/*.rbs") + puts "RBS type signature files:" + sig_files.each { |f| puts " #{f}" } + puts "\nTotal: #{sig_files.count} files" + end +end diff --git a/sig/README.md b/sig/README.md new file mode 100644 index 0000000000..8621ab7117 --- /dev/null +++ b/sig/README.md @@ -0,0 +1,52 @@ +# RBS Type Signatures + +This directory contains RBS (Ruby Signature) type definitions for the React on Rails gem. + +## What is RBS? + +RBS is Ruby's official type signature language. It provides type information for Ruby code, enabling: + +- Better IDE support and autocomplete +- Static type checking with tools like Steep +- Improved documentation +- Early detection of type-related bugs + +## Structure + +The signatures are organized to mirror the `lib/` directory structure: + +- `react_on_rails.rbs` - Main module and core classes +- `react_on_rails/configuration.rbs` - Configuration class types +- `react_on_rails/helper.rbs` - View helper method signatures +- `react_on_rails/server_rendering_pool.rbs` - Server rendering types +- `react_on_rails/utils.rbs` - Utility method signatures +- And more... + +## Validation + +To validate the RBS signatures: + +```bash +bundle exec rake rbs:validate +``` + +Or directly: + +```bash +bundle exec rbs -I sig validate +``` + +To list all RBS files: + +```bash +bundle exec rake rbs:list +``` + +## Development + +When adding new public methods or classes to the gem, please also add corresponding RBS signatures. + +For more information about RBS: + +- [RBS Documentation](https://github.com/ruby/rbs) +- [RBS Syntax Guide](https://github.com/ruby/rbs/blob/master/docs/syntax.md) diff --git a/sig/react_on_rails.rbs b/sig/react_on_rails.rbs new file mode 100644 index 0000000000..c7b61f4f46 --- /dev/null +++ b/sig/react_on_rails.rbs @@ -0,0 +1,36 @@ +# Type signatures for ReactOnRails gem + +module ReactOnRails + VERSION: String + + DEFAULT_GENERATED_ASSETS_DIR: String + DEFAULT_COMPONENT_REGISTRY_TIMEOUT: Integer + + def self.configure: () { (Configuration) -> void } -> void + def self.configuration: () -> Configuration + + class Error < StandardError + end + + class PrerenderError < Error + attr_accessor js_code: String? + attr_accessor err: Hash[Symbol, untyped] + attr_accessor js_message: String + + def initialize: ( + ?component_name: String?, + ?js_code: String?, + ?err: Hash[Symbol, untyped], + ?props: (Hash[Symbol, untyped] | String)?, + ?js_message: String + ) -> void + + def console_messages: () -> Array[String] + def base_message: () -> String + def build_message: () -> String + end + + class JsonParseError < Error + def initialize: (Hash[Symbol, untyped] err) -> void + end +end diff --git a/sig/react_on_rails/configuration.rbs b/sig/react_on_rails/configuration.rbs new file mode 100644 index 0000000000..7fd6a95051 --- /dev/null +++ b/sig/react_on_rails/configuration.rbs @@ -0,0 +1,93 @@ +module ReactOnRails + class Configuration + attr_accessor node_modules_location: String? + attr_accessor server_bundle_js_file: String + attr_accessor prerender: bool + attr_accessor replay_console: bool + attr_accessor trace: bool + attr_accessor development_mode: bool + attr_accessor logging_on_server: bool + attr_accessor server_renderer_pool_size: Integer + attr_accessor server_renderer_timeout: Integer + attr_accessor skip_display_none: bool? + attr_accessor raise_on_prerender_error: bool + attr_accessor generated_assets_dirs: Array[String]? + attr_accessor generated_assets_dir: String + attr_accessor components_subdirectory: String? + attr_accessor webpack_generated_files: Array[String] + attr_accessor rendering_extension: String? + attr_accessor build_test_command: String + attr_accessor build_production_command: String + attr_accessor i18n_dir: String? + attr_accessor i18n_yml_dir: String? + attr_accessor i18n_output_format: Symbol? + attr_accessor i18n_yml_safe_load_options: Hash[Symbol, untyped]? + attr_accessor defer_generated_component_packs: bool + attr_accessor server_render_method: String? + attr_accessor random_dom_id: bool + attr_accessor auto_load_bundle: bool + attr_accessor same_bundle_for_client_and_server: bool + attr_accessor rendering_props_extension: String? + attr_accessor make_generated_server_bundle_the_entrypoint: bool + attr_accessor generated_component_packs_loading_strategy: Symbol? + attr_accessor immediate_hydration: bool + attr_accessor component_registry_timeout: Integer + attr_accessor server_bundle_output_path: String? + attr_accessor enforce_private_server_bundles: bool + + def initialize: ( + ?node_modules_location: String?, + ?server_bundle_js_file: String?, + ?prerender: bool?, + ?replay_console: bool?, + ?make_generated_server_bundle_the_entrypoint: bool?, + ?trace: bool?, + ?development_mode: bool?, + ?defer_generated_component_packs: bool?, + ?logging_on_server: bool?, + ?server_renderer_pool_size: Integer?, + ?server_renderer_timeout: Integer?, + ?raise_on_prerender_error: bool, + ?skip_display_none: bool?, + ?generated_assets_dirs: Array[String]?, + ?generated_assets_dir: String?, + ?webpack_generated_files: Array[String]?, + ?rendering_extension: String?, + ?build_test_command: String?, + ?build_production_command: String?, + ?generated_component_packs_loading_strategy: Symbol?, + ?same_bundle_for_client_and_server: bool?, + ?i18n_dir: String?, + ?i18n_yml_dir: String?, + ?i18n_output_format: Symbol?, + ?i18n_yml_safe_load_options: Hash[Symbol, untyped]?, + ?random_dom_id: bool?, + ?server_render_method: String?, + ?rendering_props_extension: String?, + ?components_subdirectory: String?, + ?auto_load_bundle: bool?, + ?immediate_hydration: bool?, + ?component_registry_timeout: Integer?, + ?server_bundle_output_path: String?, + ?enforce_private_server_bundles: bool? + ) -> void + + def setup_config_values: () -> void + + private + + def check_component_registry_timeout: () -> void + def validate_generated_component_packs_loading_strategy: () -> void + def validate_enforce_private_server_bundles: () -> void + def check_minimum_shakapacker_version: () -> void + def check_autobundling_requirements: () -> void + def adjust_precompile_task: () -> void + def error_if_using_packer_and_generated_assets_dir_not_match_public_output_path: () -> void + def check_server_render_method_is_only_execjs: () -> void + def configure_generated_assets_dirs_deprecation: () -> void + def ensure_webpack_generated_files_exists: () -> void + def configure_skip_display_none_deprecation: () -> void + def raise_missing_components_subdirectory: () -> void + def compile_command_conflict_message: () -> String + end +end diff --git a/sig/react_on_rails/controller.rbs b/sig/react_on_rails/controller.rbs new file mode 100644 index 0000000000..e89b8d3278 --- /dev/null +++ b/sig/react_on_rails/controller.rbs @@ -0,0 +1,11 @@ +module ReactOnRails + module Controller + def redux_store: ( + String store_name, + ?props: untyped, + ?immediate_hydration: bool? + ) -> void + + def redux_store_hydration_data: () -> String + end +end diff --git a/sig/react_on_rails/git_utils.rbs b/sig/react_on_rails/git_utils.rbs new file mode 100644 index 0000000000..d1cd8e8d24 --- /dev/null +++ b/sig/react_on_rails/git_utils.rbs @@ -0,0 +1,8 @@ +module ReactOnRails + module GitUtils + def self.uncommitted_changes?: (String path) -> bool + def self.git_installed?: () -> bool + def self.current_branch: () -> String? + def self.rspec_fixture_branches: () -> Array[String] + end +end diff --git a/sig/react_on_rails/helper.rbs b/sig/react_on_rails/helper.rbs new file mode 100644 index 0000000000..199618275e --- /dev/null +++ b/sig/react_on_rails/helper.rbs @@ -0,0 +1,57 @@ +module ReactOnRails + module Helper + COMPONENT_HTML_KEY: String + + # Main helper methods + def react_component: ( + String component_name, + ?Hash[Symbol, untyped] options + ) -> String + + def react_component_hash: ( + String component_name, + ?Hash[Symbol, untyped] options + ) -> Hash[String, String] + + def redux_store: ( + String store_name, + ?Hash[Symbol, untyped] props + ) -> String + + def redux_store_hydration_data: () -> String + + def server_render_js: ( + String js_expression, + ?Hash[Symbol, untyped] options + ) -> String + + def sanitized_props_string: (untyped props) -> String + + def rails_context: ( + ?server_side: bool + ) -> Hash[Symbol, untyped] + + private + + def internal_react_component: ( + String component_name, + Hash[Symbol, untyped] options + ) -> Hash[Symbol, untyped] + + def build_react_component_result_for_server_rendered_string: ( + server_rendered_html: String, + component_specification_tag: String, + console_script: String, + render_options: Hash[Symbol, untyped] + ) -> String + + def build_react_component_result_for_server_rendered_hash: ( + server_rendered_html: Hash[String, untyped], + component_specification_tag: String, + console_script: String, + render_options: Hash[Symbol, untyped] + ) -> Hash[String, String] + + def prepend_render_rails_context: (String result) -> String + end +end diff --git a/sig/react_on_rails/packer_utils.rbs b/sig/react_on_rails/packer_utils.rbs new file mode 100644 index 0000000000..e1144d0fd4 --- /dev/null +++ b/sig/react_on_rails/packer_utils.rbs @@ -0,0 +1,15 @@ +module ReactOnRails + module PackerUtils + def self.supports_autobundling?: () -> bool + def self.supports_basic_pack_generation?: () -> bool + def self.supports_async_loading?: () -> bool + def self.nested_entries?: () -> bool + def self.packer_public_output_path: () -> String + def self.precompile?: () -> bool + def self.shakapacker_precompile_hook_configured?: () -> bool + def self.shakapacker_precompile_hook_value: () -> String + def self.raise_shakapacker_version_incompatible_for_autobundling: () -> void + def self.raise_shakapacker_version_incompatible_for_basic_pack_generation: () -> void + def self.raise_nested_entries_disabled: () -> void + end +end diff --git a/sig/react_on_rails/server_rendering_pool.rbs b/sig/react_on_rails/server_rendering_pool.rbs new file mode 100644 index 0000000000..dc55b7c8cb --- /dev/null +++ b/sig/react_on_rails/server_rendering_pool.rbs @@ -0,0 +1,12 @@ +module ReactOnRails + module ServerRenderingPool + def self.pool: () -> untyped + + def self.reset_pool_if_server_bundle_was_modified: () -> void + def self.reset_pool: () -> void + def self.server_render_js_with_console_logging: ( + String js_code, + Hash[Symbol, untyped] render_options + ) -> Hash[String, untyped] + end +end diff --git a/sig/react_on_rails/test_helper.rbs b/sig/react_on_rails/test_helper.rbs new file mode 100644 index 0000000000..3c2dce2118 --- /dev/null +++ b/sig/react_on_rails/test_helper.rbs @@ -0,0 +1,11 @@ +module ReactOnRails + module TestHelper + def self.configure_rspec_to_compile_assets: (untyped config) -> void + def self.ensure_assets_compiled: (?force_compile: bool) -> void + def self.webpack_assets_compiled?: () -> bool + + module EnsureAssetsCompiled + def self.has_been_run?: () -> bool + end + end +end diff --git a/sig/react_on_rails/utils.rbs b/sig/react_on_rails/utils.rbs new file mode 100644 index 0000000000..39202e2c64 --- /dev/null +++ b/sig/react_on_rails/utils.rbs @@ -0,0 +1,22 @@ +module ReactOnRails + module Utils + TRUNCATION_FILLER: String + + def self.truthy_presence: (untyped obj) -> untyped + def self.wrap_message: (String msg, ?Symbol color) -> String + def self.object_to_boolean: (untyped value) -> bool + def self.server_rendering_is_enabled?: () -> bool + def self.invoke_and_exit_if_failed: (String cmd, String failure_message) -> Array[String] + def self.server_bundle_path_is_http?: () -> bool + def self.bundle_js_file_path: (String bundle_name) -> String + def self.react_on_rails_pro?: () -> bool + def self.server_bundle?: (String bundle_name) -> bool + def self.running_on_windows?: () -> bool + def self.rails_version_less_than?: (String version) -> bool + def self.rails_version_less_than_4_1_1?: () -> bool + + module Required + def required: (String name) -> untyped + end + end +end diff --git a/sig/react_on_rails/version_checker.rbs b/sig/react_on_rails/version_checker.rbs new file mode 100644 index 0000000000..f16b5dd7b1 --- /dev/null +++ b/sig/react_on_rails/version_checker.rbs @@ -0,0 +1,12 @@ +module ReactOnRails + module VersionChecker + def self.log_if_gem_and_node_package_versions_differ: () -> void + + private + + def self.node_package_version: () -> String? + def self.gem_version: () -> String + def self.node_package_version_different_from_gem?: () -> bool + def self.pr_1683_warning: () -> String + end +end From 2bd6a40610bd556f143d50232955cff4f56108c0 Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Fri, 7 Nov 2025 19:26:22 -1000 Subject: [PATCH 2/4] Fix RBS type signatures based on feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add optional block parameters to react_component, react_component_hash, and internal_react_component helper methods to support yielding in view code - Change Controller#redux_store immediate_hydration parameter from bool? to bool to disallow explicit nil (nil is handled via parameter omission) - Fix PrerenderError to use attr_reader instead of attr_accessor - Mark PrerenderError attributes as optional types (String?, Hash?, Array?) - Add missing attr_readers (component_name, props, console_messages) - Add missing public methods (to_honeybadger_context, raven_context, to_error_context) All changes improve type accuracy and match actual Ruby implementation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- sig/react_on_rails.rbs | 18 ++++++++++-------- sig/react_on_rails/controller.rbs | 2 +- sig/react_on_rails/helper.rbs | 6 +++--- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/sig/react_on_rails.rbs b/sig/react_on_rails.rbs index c7b61f4f46..116f0a2e5b 100644 --- a/sig/react_on_rails.rbs +++ b/sig/react_on_rails.rbs @@ -13,21 +13,23 @@ module ReactOnRails end class PrerenderError < Error - attr_accessor js_code: String? - attr_accessor err: Hash[Symbol, untyped] - attr_accessor js_message: String + attr_reader component_name: String? + attr_reader js_code: String? + attr_reader err: Hash[Symbol, untyped]? + attr_reader props: (Hash[Symbol, untyped] | String)? + attr_reader console_messages: Array[String]? def initialize: ( ?component_name: String?, ?js_code: String?, - ?err: Hash[Symbol, untyped], + ?err: Hash[Symbol, untyped]?, ?props: (Hash[Symbol, untyped] | String)?, - ?js_message: String + ?console_messages: Array[String]? ) -> void - def console_messages: () -> Array[String] - def base_message: () -> String - def build_message: () -> String + def to_honeybadger_context: () -> Hash[Symbol, untyped] + def raven_context: () -> Hash[Symbol, untyped] + def to_error_context: () -> Hash[Symbol, untyped] end class JsonParseError < Error diff --git a/sig/react_on_rails/controller.rbs b/sig/react_on_rails/controller.rbs index e89b8d3278..a32683c08e 100644 --- a/sig/react_on_rails/controller.rbs +++ b/sig/react_on_rails/controller.rbs @@ -3,7 +3,7 @@ module ReactOnRails def redux_store: ( String store_name, ?props: untyped, - ?immediate_hydration: bool? + ?immediate_hydration: bool ) -> void def redux_store_hydration_data: () -> String diff --git a/sig/react_on_rails/helper.rbs b/sig/react_on_rails/helper.rbs index 199618275e..2948deff23 100644 --- a/sig/react_on_rails/helper.rbs +++ b/sig/react_on_rails/helper.rbs @@ -6,12 +6,12 @@ module ReactOnRails def react_component: ( String component_name, ?Hash[Symbol, untyped] options - ) -> String + ) ?{ () -> untyped } -> String def react_component_hash: ( String component_name, ?Hash[Symbol, untyped] options - ) -> Hash[String, String] + ) ?{ () -> untyped } -> Hash[String, String] def redux_store: ( String store_name, @@ -36,7 +36,7 @@ module ReactOnRails def internal_react_component: ( String component_name, Hash[Symbol, untyped] options - ) -> Hash[Symbol, untyped] + ) ?{ () -> untyped } -> Hash[Symbol, untyped] def build_react_component_result_for_server_rendered_string: ( server_rendered_html: String, From 8d83c3882f8cd482500e462a15627219c360e6ba Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Fri, 7 Nov 2025 19:28:10 -1000 Subject: [PATCH 3/4] Improve RBS signatures based on code review feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix Utils module method signatures: - Remove question mark from rails_version_less_than (matches actual implementation) - Add missing public utility methods: - react_on_rails_pro_version - rsc_support_enabled? - full_text_errors_enabled? - smart_trim - find_most_recent_mtime - prepend_to_file_if_text_not_present - detect_package_manager - default_troubleshooting_section - server_bundle_js_file_path - source_path - public_bundles_full_path - generated_assets_full_path - gem_available? - Add missing Configuration private methods: - rsc_bundle_js_file - react_client_manifest_file - react_server_client_manifest_file These methods are used in ensure_webpack_generated_files_exists and other parts of the codebase, so including them improves type coverage. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- sig/react_on_rails/configuration.rbs | 3 +++ sig/react_on_rails/utils.rbs | 16 ++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/sig/react_on_rails/configuration.rbs b/sig/react_on_rails/configuration.rbs index 7fd6a95051..aaccc02216 100644 --- a/sig/react_on_rails/configuration.rbs +++ b/sig/react_on_rails/configuration.rbs @@ -89,5 +89,8 @@ module ReactOnRails def configure_skip_display_none_deprecation: () -> void def raise_missing_components_subdirectory: () -> void def compile_command_conflict_message: () -> String + def rsc_bundle_js_file: () -> String? + def react_client_manifest_file: () -> String? + def react_server_client_manifest_file: () -> String? end end diff --git a/sig/react_on_rails/utils.rbs b/sig/react_on_rails/utils.rbs index 39202e2c64..8c576aa4f3 100644 --- a/sig/react_on_rails/utils.rbs +++ b/sig/react_on_rails/utils.rbs @@ -10,10 +10,22 @@ module ReactOnRails def self.server_bundle_path_is_http?: () -> bool def self.bundle_js_file_path: (String bundle_name) -> String def self.react_on_rails_pro?: () -> bool - def self.server_bundle?: (String bundle_name) -> bool + def self.react_on_rails_pro_version: () -> String? + def self.rsc_support_enabled?: () -> bool + def self.full_text_errors_enabled?: () -> bool + def self.smart_trim: (String str, ?Integer max_length) -> String + def self.find_most_recent_mtime: (Array[String] files) -> Time? + def self.prepend_to_file_if_text_not_present: (file: String, text_to_prepend: String, regex: Regexp) -> void + def self.detect_package_manager: () -> String + def self.default_troubleshooting_section: () -> String + def self.server_bundle_js_file_path: () -> String def self.running_on_windows?: () -> bool - def self.rails_version_less_than?: (String version) -> bool + def self.rails_version_less_than: (String version) -> bool def self.rails_version_less_than_4_1_1?: () -> bool + def self.source_path: () -> String + def self.public_bundles_full_path: () -> String + def self.generated_assets_full_path: () -> String + def self.gem_available?: (String name) -> bool module Required def required: (String name) -> untyped From 16c9604772718df66797db550913d6b7dcc0c3e4 Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sat, 8 Nov 2025 12:20:01 -1000 Subject: [PATCH 4/4] Fix RBS type accuracy and improve error handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Type Accuracy Fixes: - Change Configuration#initialize raise_on_prerender_error from bool to bool? to allow nil values (matches actual implementation) - Replace String return types with safe_buffer type alias for methods that return html_safe strings (ActiveSupport::SafeBuffer): - Helper: react_component, react_component_hash, redux_store, redux_store_hydration_data, server_render_js - Controller: redux_store_hydration_data - Private helpers: build_react_component_result_for_server_rendered_string, build_react_component_result_for_server_rendered_hash, prepend_render_rails_context Infrastructure Improvements: - Improve rake task error handling with explicit case statement for nil (handles cases where RBS command is not found) - Add rubocop disable directive for Metrics/BlockLength in rbs.rake The safe_buffer type alias (= String) documents that these methods return html_safe strings while maintaining RBS compatibility without requiring ActiveSupport type definitions. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- rakelib/rbs.rake | 10 ++++++++-- sig/react_on_rails/configuration.rbs | 2 +- sig/react_on_rails/controller.rbs | 6 +++++- sig/react_on_rails/helper.rbs | 24 ++++++++++++++++-------- 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/rakelib/rbs.rake b/rakelib/rbs.rake index 779870ee6d..bd4a754a8b 100644 --- a/rakelib/rbs.rake +++ b/rakelib/rbs.rake @@ -1,5 +1,6 @@ # frozen_string_literal: true +# rubocop:disable Metrics/BlockLength namespace :rbs do desc "Validate RBS type signatures" task :validate do @@ -11,11 +12,15 @@ namespace :rbs do # Run RBS validate result = system("bundle exec rbs -I sig validate") - if result + case result + when true puts "✓ RBS validation passed" - else + when false puts "✗ RBS validation failed" exit 1 + when nil + puts "✗ RBS command not found or could not be executed" + exit 1 end end @@ -30,3 +35,4 @@ namespace :rbs do puts "\nTotal: #{sig_files.count} files" end end +# rubocop:enable Metrics/BlockLength diff --git a/sig/react_on_rails/configuration.rbs b/sig/react_on_rails/configuration.rbs index aaccc02216..961270141f 100644 --- a/sig/react_on_rails/configuration.rbs +++ b/sig/react_on_rails/configuration.rbs @@ -47,7 +47,7 @@ module ReactOnRails ?logging_on_server: bool?, ?server_renderer_pool_size: Integer?, ?server_renderer_timeout: Integer?, - ?raise_on_prerender_error: bool, + ?raise_on_prerender_error: bool?, ?skip_display_none: bool?, ?generated_assets_dirs: Array[String]?, ?generated_assets_dir: String?, diff --git a/sig/react_on_rails/controller.rbs b/sig/react_on_rails/controller.rbs index a32683c08e..a238284df6 100644 --- a/sig/react_on_rails/controller.rbs +++ b/sig/react_on_rails/controller.rbs @@ -1,11 +1,15 @@ module ReactOnRails module Controller + # Type alias for ActiveSupport::SafeBuffer (html_safe strings) + type safe_buffer = String + def redux_store: ( String store_name, ?props: untyped, ?immediate_hydration: bool ) -> void - def redux_store_hydration_data: () -> String + # Returns html_safe string (ActiveSupport::SafeBuffer) + def redux_store_hydration_data: () -> safe_buffer end end diff --git a/sig/react_on_rails/helper.rbs b/sig/react_on_rails/helper.rbs index 2948deff23..dcc356b968 100644 --- a/sig/react_on_rails/helper.rbs +++ b/sig/react_on_rails/helper.rbs @@ -1,29 +1,37 @@ module ReactOnRails module Helper + # Type alias for ActiveSupport::SafeBuffer (html_safe strings) + type safe_buffer = String + COMPONENT_HTML_KEY: String # Main helper methods + # Returns html_safe string (ActiveSupport::SafeBuffer) def react_component: ( String component_name, ?Hash[Symbol, untyped] options - ) ?{ () -> untyped } -> String + ) ?{ () -> untyped } -> safe_buffer + # Returns hash with html_safe strings (ActiveSupport::SafeBuffer) def react_component_hash: ( String component_name, ?Hash[Symbol, untyped] options - ) ?{ () -> untyped } -> Hash[String, String] + ) ?{ () -> untyped } -> Hash[String, safe_buffer] + # Returns html_safe string (ActiveSupport::SafeBuffer) def redux_store: ( String store_name, ?Hash[Symbol, untyped] props - ) -> String + ) -> safe_buffer - def redux_store_hydration_data: () -> String + # Returns html_safe string (ActiveSupport::SafeBuffer) + def redux_store_hydration_data: () -> safe_buffer + # Returns html_safe string (ActiveSupport::SafeBuffer) def server_render_js: ( String js_expression, ?Hash[Symbol, untyped] options - ) -> String + ) -> safe_buffer def sanitized_props_string: (untyped props) -> String @@ -43,15 +51,15 @@ module ReactOnRails component_specification_tag: String, console_script: String, render_options: Hash[Symbol, untyped] - ) -> String + ) -> safe_buffer def build_react_component_result_for_server_rendered_hash: ( server_rendered_html: Hash[String, untyped], component_specification_tag: String, console_script: String, render_options: Hash[Symbol, untyped] - ) -> Hash[String, String] + ) -> Hash[String, safe_buffer] - def prepend_render_rails_context: (String result) -> String + def prepend_render_rails_context: (String result) -> safe_buffer end end