react_on_rails 16.2.0.beta.4 → 16.2.0.beta.10

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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +36 -8
  3. data/CONTRIBUTING.md +1 -1
  4. data/Gemfile.development_dependencies +0 -1
  5. data/Gemfile.lock +1 -9
  6. data/bin/ci-rerun-failures +39 -16
  7. data/bin/ci-run-failed-specs +1 -1
  8. data/bin/ci-switch-config +8 -2
  9. data/bin/lefthook/ruby-autofix +2 -1
  10. data/knip.ts +35 -9
  11. data/lib/generators/react_on_rails/base_generator.rb +3 -118
  12. data/lib/generators/react_on_rails/install_generator.rb +5 -180
  13. data/lib/generators/react_on_rails/js_dependency_manager.rb +332 -0
  14. data/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt +32 -52
  15. data/lib/generators/react_on_rails/templates/base/base/config/shakapacker.yml +14 -3
  16. data/lib/react_on_rails/dev/server_manager.rb +11 -4
  17. data/lib/react_on_rails/doctor.rb +245 -0
  18. data/lib/react_on_rails/engine.rb +2 -5
  19. data/lib/react_on_rails/helper.rb +9 -0
  20. data/lib/react_on_rails/version.rb +1 -1
  21. data/react_on_rails_pro/CHANGELOG.md +7 -0
  22. data/react_on_rails_pro/CONTRIBUTING.md +2 -13
  23. data/react_on_rails_pro/Gemfile.lock +21 -3
  24. data/react_on_rails_pro/docs/code-splitting-loadable-components.md +1 -1
  25. data/react_on_rails_pro/docs/contributors-info/releasing.md +2 -2
  26. data/react_on_rails_pro/docs/installation.md +106 -104
  27. data/react_on_rails_pro/docs/node-renderer/basics.md +3 -3
  28. data/react_on_rails_pro/docs/node-renderer/error-reporting-and-tracing.md +8 -8
  29. data/react_on_rails_pro/docs/node-renderer/js-configuration.md +1 -1
  30. data/react_on_rails_pro/docs/updating.md +209 -15
  31. data/react_on_rails_pro/lib/react_on_rails_pro/concerns/stream.rb +58 -4
  32. data/react_on_rails_pro/lib/react_on_rails_pro/configuration.rb +17 -3
  33. data/react_on_rails_pro/lib/react_on_rails_pro/license_public_key.rb +9 -9
  34. data/react_on_rails_pro/lib/react_on_rails_pro/request.rb +41 -25
  35. data/react_on_rails_pro/lib/react_on_rails_pro/stream_request.rb +27 -7
  36. data/react_on_rails_pro/lib/react_on_rails_pro/utils.rb +3 -3
  37. data/react_on_rails_pro/lib/react_on_rails_pro/version.rb +1 -1
  38. data/react_on_rails_pro/package-scripts.yml +1 -1
  39. data/react_on_rails_pro/package.json +5 -8
  40. data/react_on_rails_pro/packages/node-renderer/src/integrations/api.ts +1 -1
  41. data/react_on_rails_pro/rakelib/public_key_management.rake +6 -5
  42. data/react_on_rails_pro/react_on_rails_pro.gemspec +1 -0
  43. data/react_on_rails_pro/spec/dummy/Gemfile.lock +20 -3
  44. data/react_on_rails_pro/spec/dummy/app/controllers/pages_controller.rb +3 -3
  45. data/react_on_rails_pro/spec/dummy/bin/dev +4 -8
  46. data/react_on_rails_pro/spec/dummy/bin/shakapacker-precompile-hook +19 -0
  47. data/react_on_rails_pro/spec/dummy/client/node-renderer.js +3 -3
  48. data/react_on_rails_pro/spec/dummy/config/environments/production.rb +1 -1
  49. data/react_on_rails_pro/spec/dummy/config/initializers/react_on_rails.rb +28 -12
  50. data/react_on_rails_pro/spec/dummy/config/shakapacker.yml +5 -0
  51. data/react_on_rails_pro/spec/dummy/config.ru +1 -1
  52. data/react_on_rails_pro/spec/dummy/package.json +2 -2
  53. data/react_on_rails_pro/spec/dummy/spec/helpers/react_on_rails_pro_helper_spec.rb +40 -11
  54. data/react_on_rails_pro/spec/dummy/spec/rails_helper.rb +1 -1
  55. data/react_on_rails_pro/spec/dummy/spec/requests/renderer_console_logging_spec.rb +5 -5
  56. data/react_on_rails_pro/spec/dummy/spec/system/integration_spec.rb +20 -14
  57. data/react_on_rails_pro/spec/dummy/spec/system/renderer_integration_spec.rb +3 -3
  58. data/react_on_rails_pro/spec/dummy/yarn.lock +4 -4
  59. data/react_on_rails_pro/spec/execjs-compatible-dummy/config/environments/production.rb +1 -1
  60. data/react_on_rails_pro/spec/execjs-compatible-dummy/config/initializers/react_on_rails.rb +16 -43
  61. data/react_on_rails_pro/spec/react_on_rails_pro/assets_precompile_spec.rb +15 -18
  62. data/react_on_rails_pro/spec/react_on_rails_pro/cache_spec.rb +1 -1
  63. data/react_on_rails_pro/spec/react_on_rails_pro/configuration_spec.rb +5 -3
  64. data/react_on_rails_pro/spec/react_on_rails_pro/license_validator_spec.rb +27 -12
  65. data/react_on_rails_pro/spec/react_on_rails_pro/request_spec.rb +0 -27
  66. data/react_on_rails_pro/spec/react_on_rails_pro/spec_helper.rb +1 -1
  67. data/react_on_rails_pro/spec/react_on_rails_pro/stream_decorator_spec.rb +89 -0
  68. data/react_on_rails_pro/spec/react_on_rails_pro/stream_spec.rb +144 -0
  69. data/react_on_rails_pro/spec/react_on_rails_pro/support/caching.rb +1 -1
  70. data/react_on_rails_pro/spec/react_on_rails_pro/support/mock_block_helper.rb +4 -2
  71. data/sig/react_on_rails/generators/js_dependency_manager.rbs +123 -0
  72. metadata +5 -3
  73. data/react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/TestingStreamableComponent.jsx +0 -15
@@ -75,26 +75,30 @@ RSpec.describe ReactOnRailsPro::LicenseValidator do
75
75
  ENV["REACT_ON_RAILS_PRO_LICENSE"] = expired_token
76
76
  end
77
77
 
78
- context "in development/test environment" do
78
+ context "when in development/test environment" do
79
79
  before do
80
80
  allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new("development"))
81
81
  end
82
82
 
83
83
  it "raises error immediately" do
84
- expect { described_class.validated_license_data! }.to raise_error(ReactOnRailsPro::Error, /License has expired/)
84
+ expect do
85
+ described_class.validated_license_data!
86
+ end.to raise_error(ReactOnRailsPro::Error, /License has expired/)
85
87
  end
86
88
 
87
89
  it "includes FREE license information in error message" do
88
- expect { described_class.validated_license_data! }.to raise_error(ReactOnRailsPro::Error, /FREE evaluation license/)
90
+ expect do
91
+ described_class.validated_license_data!
92
+ end.to raise_error(ReactOnRailsPro::Error, /FREE evaluation license/)
89
93
  end
90
94
  end
91
95
 
92
- context "in production environment" do
96
+ context "when in production environment" do
93
97
  before do
94
98
  allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new("production"))
95
99
  end
96
100
 
97
- context "within grace period (expired < 1 month ago)" do
101
+ context "with grace period (expired < 1 month ago)" do
98
102
  let(:expired_within_grace) do
99
103
  {
100
104
  sub: "test@example.com",
@@ -113,7 +117,8 @@ RSpec.describe ReactOnRailsPro::LicenseValidator do
113
117
  end
114
118
 
115
119
  it "logs warning with grace period remaining" do
116
- expect(mock_logger).to receive(:error).with(/WARNING:.*License has expired.*Grace period:.*day\(s\) remaining/)
120
+ expect(mock_logger).to receive(:error)
121
+ .with(/WARNING:.*License has expired.*Grace period:.*day\(s\) remaining/)
117
122
  described_class.validated_license_data!
118
123
  end
119
124
 
@@ -123,7 +128,7 @@ RSpec.describe ReactOnRailsPro::LicenseValidator do
123
128
  end
124
129
  end
125
130
 
126
- context "outside grace period (expired > 1 month ago)" do
131
+ context "when outside grace period (expired > 1 month ago)" do
127
132
  let(:expired_outside_grace) do
128
133
  {
129
134
  sub: "test@example.com",
@@ -138,11 +143,15 @@ RSpec.describe ReactOnRailsPro::LicenseValidator do
138
143
  end
139
144
 
140
145
  it "raises error" do
141
- expect { described_class.validated_license_data! }.to raise_error(ReactOnRailsPro::Error, /License has expired/)
146
+ expect do
147
+ described_class.validated_license_data!
148
+ end.to raise_error(ReactOnRailsPro::Error, /License has expired/)
142
149
  end
143
150
 
144
151
  it "includes FREE license information in error message" do
145
- expect { described_class.validated_license_data! }.to raise_error(ReactOnRailsPro::Error, /FREE evaluation license/)
152
+ expect do
153
+ described_class.validated_license_data!
154
+ end.to raise_error(ReactOnRailsPro::Error, /FREE evaluation license/)
146
155
  end
147
156
  end
148
157
  end
@@ -168,7 +177,9 @@ RSpec.describe ReactOnRailsPro::LicenseValidator do
168
177
  end
169
178
 
170
179
  it "includes FREE license information in error message" do
171
- expect { described_class.validated_license_data! }.to raise_error(ReactOnRailsPro::Error, /FREE evaluation license/)
180
+ expect do
181
+ described_class.validated_license_data!
182
+ end.to raise_error(ReactOnRailsPro::Error, /FREE evaluation license/)
172
183
  end
173
184
  end
174
185
 
@@ -180,11 +191,15 @@ RSpec.describe ReactOnRailsPro::LicenseValidator do
180
191
  end
181
192
 
182
193
  it "raises error" do
183
- expect { described_class.validated_license_data! }.to raise_error(ReactOnRailsPro::Error, /Invalid license signature/)
194
+ expect do
195
+ described_class.validated_license_data!
196
+ end.to raise_error(ReactOnRailsPro::Error, /Invalid license signature/)
184
197
  end
185
198
 
186
199
  it "includes FREE license information in error message" do
187
- expect { described_class.validated_license_data! }.to raise_error(ReactOnRailsPro::Error, /FREE evaluation license/)
200
+ expect do
201
+ described_class.validated_license_data!
202
+ end.to raise_error(ReactOnRailsPro::Error, /FREE evaluation license/)
188
203
  end
189
204
  end
190
205
 
@@ -194,32 +194,5 @@ describe ReactOnRailsPro::Request do
194
194
  expect(mocked_block).not_to have_received(:call)
195
195
  end
196
196
  end
197
-
198
- it "does not use HTTPx retries plugin for streaming requests to prevent body duplication" do
199
- # This test verifies the fix for https://github.com/shakacode/react_on_rails/issues/1895
200
- # When streaming requests encounter connection errors mid-transmission, HTTPx retries
201
- # would cause body duplication because partial chunks are already sent to the client.
202
- # The StreamRequest class handles retries properly by starting fresh requests.
203
-
204
- # Reset connections to ensure we're using a fresh connection
205
- described_class.reset_connection
206
-
207
- # Trigger a streaming request
208
- mock_streaming_response(render_full_url, 200) do |yielder|
209
- yielder.call("Test chunk\n")
210
- end
211
-
212
- stream = described_class.render_code_as_stream("/render", "console.log('test');", is_rsc_payload: false)
213
- chunks = []
214
- stream.each_chunk { |chunk| chunks << chunk }
215
-
216
- # Verify that the streaming request completed successfully
217
- expect(chunks).to eq(["Test chunk"])
218
-
219
- # Verify that the connection_without_retries was created
220
- # by checking that a connection was created with retries disabled
221
- connection_without_retries = described_class.send(:connection_without_retries)
222
- expect(connection_without_retries).to be_a(HTTPX::Session)
223
- end
224
197
  end
225
198
  end
@@ -15,7 +15,7 @@ require "rails"
15
15
  require "rails/test_help"
16
16
  Rails.backtrace_cleaner.remove_silencers!
17
17
 
18
- require_relative "./simplecov_helper"
18
+ require_relative "simplecov_helper"
19
19
  # prevent Test::Unit's AutoRunner from executing during RSpec's rake task
20
20
  Test::Unit.run = true if defined?(Test::Unit) && Test::Unit.respond_to?(:run=)
21
21
 
@@ -62,4 +62,93 @@ RSpec.describe ReactOnRailsPro::StreamDecorator do
62
62
  expect(chunks.last).to end_with("-end")
63
63
  end
64
64
  end
65
+
66
+ describe "#rescue" do
67
+ it "catches the error happens inside the component" do
68
+ allow(mock_component).to receive(:each_chunk).and_raise(StandardError.new("Fake Error"))
69
+ mocked_block = mock_block
70
+
71
+ stream_decorator.rescue(&mocked_block.block)
72
+ chunks = []
73
+ expect { stream_decorator.each_chunk { |chunk| chunks << chunk } }.not_to raise_error
74
+
75
+ expect(mocked_block).to have_received(:call) do |error|
76
+ expect(error).to be_a(StandardError)
77
+ expect(error.message).to eq("Fake Error")
78
+ end
79
+ expect(chunks).to eq([])
80
+ end
81
+
82
+ it "catches the error happens inside subsequent component calls" do
83
+ allow(mock_component).to receive(:each_chunk).and_yield("Chunk1").and_raise(ArgumentError.new("Fake Error"))
84
+ mocked_block = mock_block
85
+
86
+ stream_decorator.rescue(&mocked_block.block)
87
+ chunks = []
88
+ expect { stream_decorator.each_chunk { |chunk| chunks << chunk } }.not_to raise_error
89
+
90
+ expect(mocked_block).to have_received(:call) do |error|
91
+ expect(chunks).to eq(["Chunk1"])
92
+ expect(error).to be_a(ArgumentError)
93
+ expect(error.message).to eq("Fake Error")
94
+ end
95
+ expect(chunks).to eq(["Chunk1"])
96
+ end
97
+
98
+ it "can yield values to the stream" do
99
+ allow(mock_component).to receive(:each_chunk).and_yield("Chunk1").and_raise(ArgumentError.new("Fake Error"))
100
+ mocked_block = mock_block
101
+
102
+ stream_decorator.rescue(&mocked_block.block)
103
+ chunks = []
104
+ expect { stream_decorator.each_chunk { |chunk| chunks << chunk } }.not_to raise_error
105
+
106
+ expect(mocked_block).to have_received(:call) do |error, &inner_block|
107
+ expect(chunks).to eq(["Chunk1"])
108
+ expect(error).to be_a(ArgumentError)
109
+ expect(error.message).to eq("Fake Error")
110
+
111
+ inner_block.call "Chunk from rescue block"
112
+ inner_block.call "Chunk2 from rescue block"
113
+ end
114
+ expect(chunks).to eq(["Chunk1", "Chunk from rescue block", "Chunk2 from rescue block"])
115
+ end
116
+
117
+ it "can convert the error into another error" do
118
+ allow(mock_component).to receive(:each_chunk).and_raise(StandardError.new("Fake Error"))
119
+ mocked_block = mock_block do |error|
120
+ expect(error).to be_a(StandardError)
121
+ expect(error.message).to eq("Fake Error")
122
+ raise ArgumentError, "Another Error"
123
+ end
124
+
125
+ stream_decorator.rescue(&mocked_block.block)
126
+ chunks = []
127
+ expect { stream_decorator.each_chunk { |chunk| chunks << chunk } }.to raise_error(ArgumentError, "Another Error")
128
+ expect(chunks).to eq([])
129
+ end
130
+
131
+ it "chains multiple rescue blocks" do
132
+ allow(mock_component).to receive(:each_chunk).and_yield("Chunk1").and_raise(StandardError.new("Fake Error"))
133
+ fist_rescue_block = mock_block do |error, &block|
134
+ expect(error).to be_a(StandardError)
135
+ expect(error.message).to eq("Fake Error")
136
+ block.call "Chunk from first rescue block"
137
+ raise ArgumentError, "Another Error"
138
+ end
139
+
140
+ second_rescue_block = mock_block do |error, &block|
141
+ expect(error).to be_a(ArgumentError)
142
+ expect(error.message).to eq("Another Error")
143
+ block.call "Chunk from second rescue block"
144
+ end
145
+
146
+ stream_decorator.rescue(&fist_rescue_block.block)
147
+ stream_decorator.rescue(&second_rescue_block.block)
148
+ chunks = []
149
+ expect { stream_decorator.each_chunk { |chunk| chunks << chunk } }.not_to raise_error
150
+
151
+ expect(chunks).to eq(["Chunk1", "Chunk from first rescue block", "Chunk from second rescue block"])
152
+ end
153
+ end
65
154
  end
@@ -1,7 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "async"
4
+ require "async/queue"
3
5
  require_relative "spec_helper"
4
6
 
7
+ class StreamController
8
+ include ReactOnRailsPro::Stream
9
+
10
+ attr_reader :response
11
+
12
+ def initialize(component_queues:, initial_response: "TEMPLATE")
13
+ @component_queues = component_queues
14
+ @initial_response = initial_response
15
+ end
16
+
17
+ def render_to_string(**_opts)
18
+ @rorp_rendering_fibers = @component_queues.map do |queue|
19
+ Fiber.new do
20
+ loop do
21
+ chunk = queue.dequeue
22
+ break if chunk.nil?
23
+
24
+ Fiber.yield chunk
25
+ end
26
+ end
27
+ end
28
+
29
+ @initial_response
30
+ end
31
+ end
32
+
5
33
  RSpec.describe "Streaming API" do
6
34
  let(:origin) { "http://api.example.com" }
7
35
  let(:path) { "/stream" }
@@ -342,4 +370,120 @@ RSpec.describe "Streaming API" do
342
370
  expect(mocked_block).to have_received(:call).with("First chunk")
343
371
  end
344
372
  end
373
+
374
+ describe "Component streaming concurrency" do
375
+ def run_stream(controller, template: "ignored")
376
+ Sync do |parent|
377
+ parent.async { controller.stream_view_containing_react_components(template: template) }
378
+ yield(parent)
379
+ end
380
+ end
381
+
382
+ def setup_stream_test(component_count: 2)
383
+ component_queues = Array.new(component_count) { Async::Queue.new }
384
+ controller = StreamController.new(component_queues: component_queues)
385
+
386
+ mocked_response = instance_double(ActionController::Live::Response)
387
+ mocked_stream = instance_double(ActionController::Live::Buffer)
388
+ allow(mocked_response).to receive(:stream).and_return(mocked_stream)
389
+ allow(mocked_stream).to receive(:write)
390
+ allow(mocked_stream).to receive(:close)
391
+ allow(controller).to receive(:response).and_return(mocked_response)
392
+
393
+ [component_queues, controller, mocked_stream]
394
+ end
395
+
396
+ it "streams components concurrently" do
397
+ queues, controller, stream = setup_stream_test
398
+
399
+ run_stream(controller) do |_parent|
400
+ queues[1].enqueue("B1")
401
+ sleep 0.05
402
+ expect(stream).to have_received(:write).with("B1")
403
+
404
+ queues[0].enqueue("A1")
405
+ sleep 0.05
406
+ expect(stream).to have_received(:write).with("A1")
407
+
408
+ queues[1].enqueue("B2")
409
+ queues[1].close
410
+ sleep 0.05
411
+
412
+ queues[0].enqueue("A2")
413
+ queues[0].close
414
+ sleep 0.1
415
+ end
416
+ end
417
+
418
+ it "maintains per-component ordering" do
419
+ queues, controller, stream = setup_stream_test
420
+
421
+ run_stream(controller) do |_parent|
422
+ queues[0].enqueue("X1")
423
+ queues[0].enqueue("X2")
424
+ queues[0].enqueue("X3")
425
+ queues[0].close
426
+
427
+ queues[1].enqueue("Y1")
428
+ queues[1].enqueue("Y2")
429
+ queues[1].close
430
+
431
+ sleep 0.2
432
+ end
433
+
434
+ # Verify all chunks were written
435
+ expect(stream).to have_received(:write).with("X1")
436
+ expect(stream).to have_received(:write).with("X2")
437
+ expect(stream).to have_received(:write).with("X3")
438
+ expect(stream).to have_received(:write).with("Y1")
439
+ expect(stream).to have_received(:write).with("Y2")
440
+ end
441
+
442
+ it "handles empty component list" do
443
+ _queues, controller, stream = setup_stream_test(component_count: 0)
444
+
445
+ run_stream(controller) do |_parent|
446
+ sleep 0.1
447
+ end
448
+
449
+ expect(stream).to have_received(:write).with("TEMPLATE")
450
+ expect(stream).to have_received(:close)
451
+ end
452
+
453
+ it "handles single component" do
454
+ queues, controller, stream = setup_stream_test(component_count: 1)
455
+
456
+ run_stream(controller) do |_parent|
457
+ queues[0].enqueue("Single1")
458
+ queues[0].enqueue("Single2")
459
+ queues[0].close
460
+
461
+ sleep 0.1
462
+ end
463
+
464
+ expect(stream).to have_received(:write).with("Single1")
465
+ expect(stream).to have_received(:write).with("Single2")
466
+ end
467
+
468
+ it "applies backpressure with slow writer" do
469
+ queues, controller, stream = setup_stream_test(component_count: 1)
470
+
471
+ write_timestamps = []
472
+ allow(stream).to receive(:write) do |_data|
473
+ write_timestamps << Process.clock_gettime(Process::CLOCK_MONOTONIC)
474
+ sleep 0.05
475
+ end
476
+
477
+ run_stream(controller) do |_parent|
478
+ 5.times { |i| queues[0].enqueue("Chunk#{i}") }
479
+ queues[0].close
480
+
481
+ sleep 1
482
+ end
483
+
484
+ expect(write_timestamps.length).to be >= 2
485
+ gaps = write_timestamps.each_cons(2).map { |a, b| b - a }
486
+ expect(gaps.all? { |gap| gap >= 0.04 }).to be true
487
+ end
488
+ end
345
489
  end
@@ -4,7 +4,7 @@ RSpec.configure do |config|
4
4
  config.before(:each, :caching) do
5
5
  cache_store = ActiveSupport::Cache::MemoryStore.new
6
6
  allow(controller).to receive(:cache_store).and_return(cache_store) if defined?(controller) && controller
7
- allow(::Rails).to receive(:cache).and_return(cache_store)
7
+ allow(Rails).to receive(:cache).and_return(cache_store)
8
8
  ReactOnRailsPro::Cache.instance_variable_set(:@serializer_checksum, nil)
9
9
  Rails.cache.clear
10
10
  end
@@ -9,9 +9,11 @@ module MockBlockHelper
9
9
  # mocked_block = mock_block
10
10
  # testing_method_taking_block(&mocked_block.block)
11
11
  # expect(mocked_block).to have_received(:call).with(1, 2, 3)
12
- def mock_block(return_value: nil)
12
+ def mock_block(&block)
13
13
  double("BlockMock").tap do |mock| # rubocop:disable RSpec/VerifiedDoubles
14
- allow(mock).to receive(:call) { return_value }
14
+ allow(mock).to receive(:call) do |*args, &inner_block|
15
+ block&.call(*args, &inner_block)
16
+ end
15
17
  def mock.block
16
18
  method(:call).to_proc
17
19
  end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ReactOnRails
4
+ module Generators
5
+ # Type signatures for JsDependencyManager module
6
+ #
7
+ # This module provides common functionality for managing JavaScript dependencies
8
+ # in Rails generators using the package_json gem (available via shakapacker).
9
+ module JsDependencyManager
10
+ # Core React dependencies required for React on Rails
11
+ REACT_DEPENDENCIES: Array[String]
12
+
13
+ # CSS processing dependencies for webpack
14
+ CSS_DEPENDENCIES: Array[String]
15
+
16
+ # Development-only dependencies for hot reloading (Webpack)
17
+ DEV_DEPENDENCIES: Array[String]
18
+
19
+ # Rspack core dependencies (only installed when --rspack flag is used)
20
+ RSPACK_DEPENDENCIES: Array[String]
21
+
22
+ # Rspack development dependencies for hot reloading
23
+ RSPACK_DEV_DEPENDENCIES: Array[String]
24
+
25
+ # TypeScript dependencies (only installed when --typescript flag is used)
26
+ TYPESCRIPT_DEPENDENCIES: Array[String]
27
+
28
+ private
29
+
30
+ # Sets up JavaScript dependencies by adding and installing packages
31
+ #
32
+ # This method orchestrates the entire dependency setup process:
33
+ # 1. Adds all required packages to package.json
34
+ # 2. Runs package manager install
35
+ #
36
+ # @return [void]
37
+ def setup_js_dependencies: () -> void
38
+
39
+ # Adds all JavaScript dependencies to package.json
40
+ #
41
+ # This method calls individual add_*_dependencies methods in sequence.
42
+ # All errors are handled gracefully with warnings rather than exceptions.
43
+ #
44
+ # @return [void]
45
+ def add_js_dependencies: () -> void
46
+
47
+ # Adds the react-on-rails package to package.json
48
+ #
49
+ # Uses version matching for stable releases, or latest for pre-releases.
50
+ # Adds error message to GeneratorMessages if package addition fails.
51
+ #
52
+ # @return [void]
53
+ def add_react_on_rails_package: () -> void
54
+
55
+ # Adds React dependencies to package.json
56
+ #
57
+ # Adds error message to GeneratorMessages if package addition fails.
58
+ #
59
+ # @return [void]
60
+ def add_react_dependencies: () -> void
61
+
62
+ # Adds CSS processing dependencies to package.json
63
+ #
64
+ # Adds error message to GeneratorMessages if package addition fails.
65
+ #
66
+ # @return [void]
67
+ def add_css_dependencies: () -> void
68
+
69
+ # Adds Rspack dependencies to package.json
70
+ #
71
+ # Only called when --rspack flag is set.
72
+ # Adds error message to GeneratorMessages if package addition fails.
73
+ #
74
+ # @return [void]
75
+ def add_rspack_dependencies: () -> void
76
+
77
+ # Adds TypeScript dependencies to package.json as dev dependencies
78
+ #
79
+ # Only called when --typescript flag is set.
80
+ # Adds error message to GeneratorMessages if package addition fails.
81
+ #
82
+ # @return [void]
83
+ def add_typescript_dependencies: () -> void
84
+
85
+ # Adds development dependencies to package.json
86
+ #
87
+ # Chooses between Webpack or Rspack dev dependencies based on --rspack flag.
88
+ # Adds error message to GeneratorMessages if package addition fails.
89
+ #
90
+ # @return [void]
91
+ def add_dev_dependencies: () -> void
92
+
93
+ # Adds a single package using package_json gem
94
+ #
95
+ # This method is used internally for adding the react-on-rails package
96
+ # with version-specific handling (react-on-rails@VERSION).
97
+ # For batch operations, use add_packages instead.
98
+ #
99
+ # @param package [String] Package specifier (e.g., "react-on-rails@16.0.0")
100
+ # @param dev [bool] Whether to add as dev dependency
101
+ # @return [bool] true if successful, false otherwise
102
+ def add_package: (String package, ?dev: bool) -> bool
103
+
104
+ # Adds multiple packages at once using package_json gem
105
+ #
106
+ # Delegates to GeneratorHelper's add_npm_dependencies for better
107
+ # package manager abstraction and batch processing efficiency.
108
+ #
109
+ # @param packages [Array<String>] Package names to add
110
+ # @param dev [bool] Whether to add as dev dependencies
111
+ # @return [bool] true if successful, false otherwise
112
+ def add_packages: (Array[String] packages, ?dev: bool) -> bool
113
+
114
+ # Installs JavaScript dependencies using package_json gem
115
+ #
116
+ # Always available via shakapacker dependency chain.
117
+ # Adds warning to GeneratorMessages if installation fails.
118
+ #
119
+ # @return [bool] true if successful, false otherwise
120
+ def install_js_dependencies: () -> bool
121
+ end
122
+ end
123
+ 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.4
4
+ version: 16.2.0.beta.10
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-13 00:00:00.000000000 Z
11
+ date: 2025-11-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -167,6 +167,7 @@ files:
167
167
  - lib/generators/react_on_rails/generator_helper.rb
168
168
  - lib/generators/react_on_rails/generator_messages.rb
169
169
  - lib/generators/react_on_rails/install_generator.rb
170
+ - lib/generators/react_on_rails/js_dependency_manager.rb
170
171
  - lib/generators/react_on_rails/react_no_redux_generator.rb
171
172
  - lib/generators/react_on_rails/react_with_redux_generator.rb
172
173
  - lib/generators/react_on_rails/templates/.eslintrc
@@ -606,6 +607,7 @@ files:
606
607
  - react_on_rails_pro/spec/dummy/bin/setup
607
608
  - react_on_rails_pro/spec/dummy/bin/shakapacker
608
609
  - react_on_rails_pro/spec/dummy/bin/shakapacker-dev-server
610
+ - react_on_rails_pro/spec/dummy/bin/shakapacker-precompile-hook
609
611
  - react_on_rails_pro/spec/dummy/bin/spring
610
612
  - react_on_rails_pro/spec/dummy/bin/sprockets
611
613
  - react_on_rails_pro/spec/dummy/bin/term_display
@@ -756,7 +758,6 @@ files:
756
758
  - react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/SetTimeoutLoggingApp.server.jsx
757
759
  - react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/SimpleComponent.jsx
758
760
  - react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/StreamAsyncComponents.jsx
759
- - react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/TestingStreamableComponent.jsx
760
761
  - react_on_rails_pro/spec/dummy/client/app/routes/routes.jsx
761
762
  - react_on_rails_pro/spec/dummy/client/app/ssr-computations/userQuery.ssr-computation.ts
762
763
  - react_on_rails_pro/spec/dummy/client/app/store/composeInitialState.js
@@ -974,6 +975,7 @@ files:
974
975
  - sig/react_on_rails/configuration.rbs
975
976
  - sig/react_on_rails/controller.rbs
976
977
  - sig/react_on_rails/error.rbs
978
+ - sig/react_on_rails/generators/js_dependency_manager.rbs
977
979
  - sig/react_on_rails/git_utils.rbs
978
980
  - sig/react_on_rails/helper.rbs
979
981
  - sig/react_on_rails/json_parse_error.rbs
@@ -1,15 +0,0 @@
1
- 'use client';
2
-
3
- import React from 'react';
4
-
5
- // Simple test component for streaming SSR tests
6
- function TestingStreamableComponent({ helloWorldData }) {
7
- return (
8
- <div>
9
- <div>Chunk 1: Stream React Server Components</div>
10
- <div>Hello, {helloWorldData.name}!</div>
11
- </div>
12
- );
13
- }
14
-
15
- export default TestingStreamableComponent;