react_on_rails 16.2.0.beta.3 → 16.2.0.beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -0
  3. data/CLAUDE.md +59 -0
  4. data/CONTRIBUTING.md +48 -0
  5. data/Gemfile.development_dependencies +1 -0
  6. data/Gemfile.lock +25 -2
  7. data/SWITCHING_CI_CONFIGS.md +55 -6
  8. data/Steepfile +51 -0
  9. data/bin/ci-rerun-failures +34 -11
  10. data/bin/ci-run-failed-specs +25 -1
  11. data/bin/ci-switch-config +254 -32
  12. data/bin/lefthook/check-trailing-newlines +2 -12
  13. data/bin/lefthook/eslint-lint +0 -10
  14. data/bin/lefthook/prettier-format +0 -10
  15. data/bin/lefthook/ruby-autofix +1 -5
  16. data/lib/react_on_rails/configuration.rb +56 -12
  17. data/lib/react_on_rails/controller.rb +3 -3
  18. data/lib/react_on_rails/doctor.rb +4 -2
  19. data/lib/react_on_rails/helper.rb +3 -3
  20. data/lib/react_on_rails/pro_helper.rb +2 -44
  21. data/lib/react_on_rails/react_component/render_options.rb +7 -7
  22. data/lib/react_on_rails/utils.rb +40 -0
  23. data/lib/react_on_rails/version.rb +1 -1
  24. data/react_on_rails_pro/CHANGELOG.md +135 -29
  25. data/react_on_rails_pro/Gemfile.development_dependencies +1 -0
  26. data/react_on_rails_pro/Gemfile.lock +6 -3
  27. data/react_on_rails_pro/README.md +559 -38
  28. data/react_on_rails_pro/docs/installation.md +40 -22
  29. data/react_on_rails_pro/docs/node-renderer/basics.md +26 -19
  30. data/react_on_rails_pro/docs/node-renderer/js-configuration.md +24 -22
  31. data/react_on_rails_pro/docs/node-renderer/troubleshooting.md +2 -0
  32. data/react_on_rails_pro/lib/react_on_rails_pro/version.rb +1 -1
  33. data/react_on_rails_pro/package.json +1 -1
  34. data/react_on_rails_pro/packages/node-renderer/src/master/restartWorkers.ts +39 -17
  35. data/react_on_rails_pro/packages/node-renderer/src/master.ts +15 -4
  36. data/react_on_rails_pro/packages/node-renderer/src/shared/configBuilder.ts +44 -5
  37. data/react_on_rails_pro/packages/node-renderer/src/shared/utils.ts +4 -2
  38. data/react_on_rails_pro/packages/node-renderer/src/worker/handleGracefulShutdown.ts +49 -0
  39. data/react_on_rails_pro/packages/node-renderer/src/worker/vm.ts +3 -3
  40. data/react_on_rails_pro/packages/node-renderer/src/worker.ts +5 -2
  41. data/react_on_rails_pro/packages/node-renderer/tests/helper.ts +8 -8
  42. data/react_on_rails_pro/packages/node-renderer/tests/testingNodeRendererConfigs.js +1 -1
  43. data/react_on_rails_pro/packages/node-renderer/tests/worker.test.ts +19 -19
  44. data/react_on_rails_pro/rakelib/rbs.rake +47 -0
  45. data/react_on_rails_pro/sig/react_on_rails_pro/cache.rbs +13 -0
  46. data/react_on_rails_pro/sig/react_on_rails_pro/configuration.rbs +100 -0
  47. data/react_on_rails_pro/sig/react_on_rails_pro/error.rbs +4 -0
  48. data/react_on_rails_pro/sig/react_on_rails_pro/utils.rbs +7 -0
  49. data/react_on_rails_pro/sig/react_on_rails_pro.rbs +5 -0
  50. data/react_on_rails_pro/spec/dummy/Gemfile.lock +6 -3
  51. data/react_on_rails_pro/spec/dummy/client/node-renderer.js +1 -1
  52. data/react_on_rails_pro/spec/dummy/spec/system/integration_spec.rb +16 -17
  53. data/sig/react_on_rails/controller.rbs +1 -1
  54. data/sig/react_on_rails/error.rbs +4 -0
  55. data/sig/react_on_rails/helper.rbs +2 -2
  56. data/sig/react_on_rails/json_parse_error.rbs +10 -0
  57. data/sig/react_on_rails/prerender_error.rbs +21 -0
  58. data/sig/react_on_rails/smart_error.rbs +28 -0
  59. data/sig/react_on_rails.rbs +3 -24
  60. metadata +14 -3
  61. data/lib/react_on_rails/pro_utils.rb +0 -37
@@ -11,7 +11,7 @@ def change_text_expect_dom_selector(dom_selector, expect_no_change: false)
11
11
  find("input").set new_text
12
12
  within("h3") do
13
13
  if expect_no_change
14
- expect(subject).to have_no_content new_text
14
+ expect(subject).not_to have_content new_text
15
15
  else
16
16
  expect(subject).to have_content new_text
17
17
  end
@@ -110,7 +110,7 @@ describe "Turbolinks across pages", :js do
110
110
  it "changes name in message according to input" do
111
111
  visit "/client_side_hello_world"
112
112
  change_text_expect_dom_selector("#HelloWorld-react-component-0")
113
- click_link "Hello World Component Server Rendered, with extra options" # rubocop:disable Capybara/ClickLinkOrButtonStyle
113
+ click_link "Hello World Component Server Rendered, with extra options"
114
114
  change_text_expect_dom_selector("#my-hello-world-id")
115
115
  end
116
116
  end
@@ -174,19 +174,19 @@ describe "React Router", :js do
174
174
 
175
175
  before do
176
176
  visit "/"
177
- click_link "React Router" # rubocop:disable Capybara/ClickLinkOrButtonStyle
177
+ click_link "React Router"
178
178
  end
179
179
 
180
180
  context "when rendering /react_router" do
181
181
  it { is_expected.to have_text("Woohoo, we can use react-router here!") }
182
182
 
183
183
  it "clicking links correctly renders other pages" do
184
- click_link "Router First Page" # rubocop:disable Capybara/ClickLinkOrButtonStyle
184
+ click_link "Router First Page"
185
185
  expect(page).to have_current_path("/react_router/first_page")
186
186
  first_page_header_text = page.find(:css, "h2#first-page").text
187
187
  expect(first_page_header_text).to eq("React Router First Page")
188
188
 
189
- click_link "Router Second Page" # rubocop:disable Capybara/ClickLinkOrButtonStyle
189
+ click_link "Router Second Page"
190
190
  expect(page).to have_current_path("/react_router/second_page")
191
191
  second_page_header_text = page.find(:css, "h2#second-page").text
192
192
  expect(second_page_header_text).to eq("React Router Second Page")
@@ -239,12 +239,12 @@ describe "async render function returns component", :js do
239
239
  end
240
240
  end
241
241
 
242
- describe "Manual client hydration", :js do
242
+ describe "Manual client hydration", :js, type: :system do
243
243
  before { visit "/xhr_refresh" }
244
244
 
245
245
  it "HelloWorldRehydratable onChange should trigger" do
246
246
  within("form") do
247
- click_button "refresh" # rubocop:disable Capybara/ClickLinkOrButtonStyle
247
+ click_button "refresh"
248
248
  end
249
249
  within("#HelloWorldRehydratable-react-component-1") do
250
250
  find("input").set "Should update"
@@ -396,20 +396,20 @@ shared_examples "streamed component tests" do |path, selector|
396
396
 
397
397
  it "hydrates the component" do
398
398
  visit path
399
- expect(page.html).to include("client-bundle.js")
399
+ expect(page.html).to match(/client-bundle[^\"]*.js/)
400
400
  change_text_expect_dom_selector(selector)
401
401
  end
402
402
 
403
403
  it "renders the page completely on server and displays content on client even without JavaScript" do
404
404
  # Don't add client-bundle.js to the page to ensure that the app is not hydrated
405
405
  visit "#{path}?skip_js_packs=true"
406
- expect(page.html).not_to include("client-bundle.js")
406
+ expect(page.html).not_to match(/client-bundle[^\"]*.js/)
407
407
  # Ensure that the component state is not updated
408
408
  change_text_expect_dom_selector(selector, expect_no_change: true)
409
409
 
410
- expect(page).to have_no_text "Loading branch1"
411
- expect(page).to have_no_text "Loading branch2"
412
- expect(page).to have_no_text(/Loading branch1 at level \d+/)
410
+ expect(page).not_to have_text "Loading branch1"
411
+ expect(page).not_to have_text "Loading branch2"
412
+ expect(page).not_to have_text(/Loading branch1 at level \d+/)
413
413
  expect(page).to have_text(/branch1 \(level \d+\)/, count: 5)
414
414
  end
415
415
 
@@ -417,18 +417,17 @@ shared_examples "streamed component tests" do |path, selector|
417
417
  # visit waits for the page to load, so we ensure that the page is loaded before checking the hydration status
418
418
  visit "#{path}?skip_js_packs=true"
419
419
  expect(page).to have_text "HydrationStatus: Streaming server render"
420
- expect(page).to have_no_text "HydrationStatus: Hydrated"
421
- expect(page).to have_no_text "HydrationStatus: Page loaded"
420
+ expect(page).not_to have_text "HydrationStatus: Hydrated"
421
+ expect(page).not_to have_text "HydrationStatus: Page loaded"
422
422
  end
423
423
  end
424
424
 
425
- describe "Pages/stream_async_components_for_testing", :js,
426
- skip: "Flaky test replaced by Playwright E2E tests in e2e-tests/streaming.spec.ts" do
425
+ describe "Pages/stream_async_components_for_testing", :js do
427
426
  it_behaves_like "streamed component tests", "/stream_async_components_for_testing",
428
427
  "#AsyncComponentsTreeForTesting-react-component-0"
429
428
  end
430
429
 
431
- describe "React Router Sixth Page", :js, skip: "Work in progress in another branch: justin808/fix-body-dup-retry" do
430
+ describe "React Router Sixth Page", :js do
432
431
  it_behaves_like "streamed component tests", "/server_router/streaming-server-component",
433
432
  "#ServerComponentRouter-react-component-0"
434
433
  end
@@ -5,7 +5,7 @@ module ReactOnRails
5
5
 
6
6
  def redux_store: (
7
7
  String store_name,
8
- ?props: untyped,
8
+ ?props: Hash[Symbol, untyped] | String,
9
9
  ?immediate_hydration: bool
10
10
  ) -> void
11
11
 
@@ -0,0 +1,4 @@
1
+ module ReactOnRails
2
+ class Error < StandardError
3
+ end
4
+ end
@@ -21,7 +21,7 @@ module ReactOnRails
21
21
  # Returns html_safe string (ActiveSupport::SafeBuffer)
22
22
  def redux_store: (
23
23
  String store_name,
24
- ?Hash[Symbol, untyped] props
24
+ ?Hash[Symbol, untyped] | String props
25
25
  ) -> safe_buffer
26
26
 
27
27
  # Returns html_safe string (ActiveSupport::SafeBuffer)
@@ -33,7 +33,7 @@ module ReactOnRails
33
33
  ?Hash[Symbol, untyped] options
34
34
  ) -> safe_buffer
35
35
 
36
- def sanitized_props_string: (untyped props) -> String
36
+ def sanitized_props_string: (Hash[Symbol, untyped] | String props) -> String
37
37
 
38
38
  def rails_context: (
39
39
  ?server_side: bool
@@ -0,0 +1,10 @@
1
+ module ReactOnRails
2
+ class JsonParseError < ::ReactOnRails::Error
3
+ attr_reader json: String
4
+
5
+ def initialize: (parse_error: StandardError, json: String) -> void
6
+ def to_honeybadger_context: () -> Hash[Symbol, untyped]
7
+ def raven_context: () -> Hash[Symbol, untyped]
8
+ def to_error_context: () -> Hash[Symbol, untyped]
9
+ end
10
+ end
@@ -0,0 +1,21 @@
1
+ module ReactOnRails
2
+ class PrerenderError < ::ReactOnRails::Error
3
+ MAX_ERROR_SNIPPET_TO_LOG: Integer
4
+
5
+ attr_reader component_name: String?
6
+ attr_reader err: StandardError?
7
+ attr_reader props: String?
8
+ attr_reader js_code: String?
9
+ attr_reader console_messages: String?
10
+
11
+ def initialize: (?component_name: String?, ?err: StandardError?, ?props: String?, ?js_code: String?, ?console_messages: String?) -> void
12
+ def to_honeybadger_context: () -> Hash[Symbol, untyped]
13
+ def raven_context: () -> Hash[Symbol, untyped]
14
+ def to_error_context: () -> Hash[Symbol, untyped]
15
+
16
+ private
17
+
18
+ def calc_message: (String? component_name, String? console_messages, StandardError? err, String? js_code, String? props) -> [String?, String]
19
+ def build_troubleshooting_suggestions: (String? component_name, StandardError? err, String? console_messages) -> String
20
+ end
21
+ end
@@ -0,0 +1,28 @@
1
+ module ReactOnRails
2
+ class SmartError < Error
3
+ attr_reader error_type: Symbol
4
+ attr_reader component_name: String?
5
+ attr_reader props: String?
6
+ attr_reader js_code: String?
7
+ attr_reader additional_context: Hash[Symbol, untyped]
8
+
9
+ def initialize: (error_type: Symbol, ?component_name: String?, ?props: String?, ?js_code: String?, **untyped additional_context) -> void
10
+ def solution: () -> String
11
+
12
+ private
13
+
14
+ def build_error_message: () -> String
15
+ def error_type_title: () -> String
16
+ def error_description: () -> String
17
+ def component_not_registered_solution: () -> String
18
+ def missing_auto_loaded_bundle_solution: () -> String
19
+ def hydration_mismatch_solution: () -> String
20
+ def server_rendering_error_solution: () -> String
21
+ def redux_store_not_found_solution: () -> String
22
+ def configuration_error_solution: () -> String
23
+ def default_solution: () -> String
24
+ def additional_info: () -> String
25
+ def troubleshooting_section: () -> String
26
+ def find_similar_components: (String name) -> Array[String]
27
+ end
28
+ end
@@ -9,30 +9,9 @@ module ReactOnRails
9
9
  def self.configure: () { (Configuration) -> void } -> void
10
10
  def self.configuration: () -> Configuration
11
11
 
12
+ # Error classes are defined in separate RBS files:
13
+ # - sig/react_on_rails/prerender_error.rbs
14
+ # - sig/react_on_rails/json_parse_error.rbs
12
15
  class Error < StandardError
13
16
  end
14
-
15
- class PrerenderError < Error
16
- attr_reader component_name: String?
17
- attr_reader js_code: String?
18
- attr_reader err: Hash[Symbol, untyped]?
19
- attr_reader props: (Hash[Symbol, untyped] | String)?
20
- attr_reader console_messages: Array[String]?
21
-
22
- def initialize: (
23
- ?component_name: String?,
24
- ?js_code: String?,
25
- ?err: Hash[Symbol, untyped]?,
26
- ?props: (Hash[Symbol, untyped] | String)?,
27
- ?console_messages: Array[String]?
28
- ) -> void
29
-
30
- def to_honeybadger_context: () -> Hash[Symbol, untyped]
31
- def raven_context: () -> Hash[Symbol, untyped]
32
- def to_error_context: () -> Hash[Symbol, untyped]
33
- end
34
-
35
- class JsonParseError < Error
36
- def initialize: (Hash[Symbol, untyped] err) -> void
37
- end
38
17
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: react_on_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 16.2.0.beta.3
4
+ version: 16.2.0.beta.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Gordon
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-11-11 00:00:00.000000000 Z
11
+ date: 2025-11-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -137,6 +137,7 @@ files:
137
137
  - Rakefile
138
138
  - SUMMARY.md
139
139
  - SWITCHING_CI_CONFIGS.md
140
+ - Steepfile
140
141
  - TODO.md
141
142
  - WARP.md
142
143
  - app/helpers/react_on_rails_helper.rb
@@ -243,7 +244,6 @@ files:
243
244
  - lib/react_on_rails/packs_generator.rb
244
245
  - lib/react_on_rails/prerender_error.rb
245
246
  - lib/react_on_rails/pro_helper.rb
246
- - lib/react_on_rails/pro_utils.rb
247
247
  - lib/react_on_rails/react_component/render_options.rb
248
248
  - lib/react_on_rails/server_rendering_js_code.rb
249
249
  - lib/react_on_rails/server_rendering_pool.rb
@@ -378,6 +378,7 @@ files:
378
378
  - react_on_rails_pro/packages/node-renderer/src/worker.ts
379
379
  - react_on_rails_pro/packages/node-renderer/src/worker/authHandler.ts
380
380
  - react_on_rails_pro/packages/node-renderer/src/worker/checkProtocolVersionHandler.ts
381
+ - react_on_rails_pro/packages/node-renderer/src/worker/handleGracefulShutdown.ts
381
382
  - react_on_rails_pro/packages/node-renderer/src/worker/handleRenderRequest.ts
382
383
  - react_on_rails_pro/packages/node-renderer/src/worker/types.ts
383
384
  - react_on_rails_pro/packages/node-renderer/src/worker/vm.ts
@@ -430,6 +431,7 @@ files:
430
431
  - react_on_rails_pro/rakelib/dummy_apps.rake
431
432
  - react_on_rails_pro/rakelib/lint.rake
432
433
  - react_on_rails_pro/rakelib/public_key_management.rake
434
+ - react_on_rails_pro/rakelib/rbs.rake
433
435
  - react_on_rails_pro/rakelib/run_rspec.rake
434
436
  - react_on_rails_pro/rakelib/task_helpers.rb
435
437
  - react_on_rails_pro/rakelib/yard.rake
@@ -439,6 +441,11 @@ files:
439
441
  - react_on_rails_pro/script/preinstall.js
440
442
  - react_on_rails_pro/script/setup
441
443
  - react_on_rails_pro/script/test
444
+ - react_on_rails_pro/sig/react_on_rails_pro.rbs
445
+ - react_on_rails_pro/sig/react_on_rails_pro/cache.rbs
446
+ - react_on_rails_pro/sig/react_on_rails_pro/configuration.rbs
447
+ - react_on_rails_pro/sig/react_on_rails_pro/error.rbs
448
+ - react_on_rails_pro/sig/react_on_rails_pro/utils.rbs
442
449
  - react_on_rails_pro/spec/.rubocop.yml
443
450
  - react_on_rails_pro/spec/dummy/.browserslistrc
444
451
  - react_on_rails_pro/spec/dummy/.gitignore
@@ -966,10 +973,14 @@ files:
966
973
  - sig/react_on_rails.rbs
967
974
  - sig/react_on_rails/configuration.rbs
968
975
  - sig/react_on_rails/controller.rbs
976
+ - sig/react_on_rails/error.rbs
969
977
  - sig/react_on_rails/git_utils.rbs
970
978
  - sig/react_on_rails/helper.rbs
979
+ - sig/react_on_rails/json_parse_error.rbs
971
980
  - sig/react_on_rails/packer_utils.rbs
981
+ - sig/react_on_rails/prerender_error.rbs
972
982
  - sig/react_on_rails/server_rendering_pool.rbs
983
+ - sig/react_on_rails/smart_error.rbs
973
984
  - sig/react_on_rails/test_helper.rbs
974
985
  - sig/react_on_rails/utils.rbs
975
986
  - sig/react_on_rails/version_checker.rbs
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ReactOnRails
4
- module ProUtils
5
- PRO_ONLY_OPTIONS = %i[immediate_hydration].freeze
6
-
7
- # Checks if React on Rails Pro features are available
8
- # @return [Boolean] true if Pro is installed and licensed, false otherwise
9
- def self.support_pro_features?
10
- ReactOnRails::Utils.react_on_rails_pro?
11
- end
12
-
13
- def self.disable_pro_render_options_if_not_licensed(raw_options)
14
- if support_pro_features?
15
- return {
16
- raw_options: raw_options,
17
- explicitly_disabled_pro_options: []
18
- }
19
- end
20
-
21
- raw_options_after_disable = raw_options.dup
22
-
23
- explicitly_disabled_pro_options = PRO_ONLY_OPTIONS.select do |option|
24
- # Use global configuration if it's not overridden in the options
25
- next ReactOnRails.configuration.send(option) if raw_options[option].nil?
26
-
27
- raw_options[option]
28
- end
29
- explicitly_disabled_pro_options.each { |option| raw_options_after_disable[option] = false }
30
-
31
- {
32
- raw_options: raw_options_after_disable,
33
- explicitly_disabled_pro_options: explicitly_disabled_pro_options
34
- }
35
- end
36
- end
37
- end