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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 559d44da8c51be70681ed059f69f1fb67856a41f86c95bdd35d2a2bbed79f0f8
4
- data.tar.gz: 92cd87455b4e96407dcc6b06a5c2eb36a66a229305be63dbb55cd02d6774b638
3
+ metadata.gz: da10987e43490e2a8da60fccb2033038aa9fa6a9b5ccce9add3f9d0da795474c
4
+ data.tar.gz: 9e7a3a46c8bb8e0fcc57142b5a1218ea58d5155d54614038d035e2e6b39fdf47
5
5
  SHA512:
6
- metadata.gz: 6877a9fa47c913a103f146b8b6f0e35409618f7341f22fa0f362d73fe2bc2f3348a8d7593aa65dce56b245118a08892b8a2ea8c043ccb21bdc17955145a07189
7
- data.tar.gz: 9c81ce27f0c3fee5aea1767613fe696377ea8a4bf61c68a86291ab171c662388cc931f9c7c47d41fa82ed3a27afa8cb953b4ce25ec0408a096c93a107bb5cde4
6
+ metadata.gz: d9f6d5be2b5bf2437adc711b6ac77e64fb7c6540d34bdce6380e98bf919872ec59e897b87869e25221f1f461ac91b25fc146c48701eb161630cb9bea7c44c7a1
7
+ data.tar.gz: 5ec66977d53c808a8983277c6534e24fe966123105c4738e20995a9bb45be34a139030fe3e70ffe3251bb9961470371841bea040e558deb3fc974c154d6bdb7d
data/CHANGELOG.md CHANGED
@@ -23,6 +23,12 @@ After a release, please make sure to run `bundle exec rake update_changelog`. Th
23
23
 
24
24
  Changes since the last non-beta release.
25
25
 
26
+ #### Fixed
27
+
28
+ - **Duplicate Rake Task Execution**: Fixed rake tasks executing twice during asset precompilation and other rake operations. Rails Engine was loading task files twice: once via explicit `load` calls in the `rake_tasks` block (Railtie layer) and once via automatic file loading from `lib/tasks/` (Engine layer). This caused `react_on_rails:assets:webpack`, `react_on_rails:generate_packs`, and `react_on_rails:locale` tasks to run twice, significantly increasing build times. Removed explicit `load` calls and now rely on Rails Engine's standard auto-loading behavior. [PR 2052](https://github.com/shakacode/react_on_rails/pull/2052) by [justin808](https://github.com/justin808).
29
+
30
+ ### [v16.2.0.beta.8] - 2025-11-16
31
+
26
32
  #### Added
27
33
 
28
34
  - **Rspack Support**: Added `--rspack` flag to `react_on_rails:install` generator for significantly faster builds (~20x improvement with SWC). Includes unified webpack/rspack configuration templates and `bin/switch-bundler` utility to switch between bundlers post-installation. [PR #1852](https://github.com/shakacode/react_on_rails/pull/1852) by [justin808](https://github.com/justin808).
@@ -35,6 +41,10 @@ Changes since the last non-beta release.
35
41
 
36
42
  - **Improved RSC Payload Error Handling**: Errors that happen during generation of RSC payload are transferred properly to rails side and logs the error message and stack. [PR #1888](https://github.com/shakacode/react_on_rails/pull/1888) by [AbanoubGhadban](https://github.com/AbanoubGhadban).
37
43
 
44
+ - **Use as Git dependency**: All packages can now be installed as Git dependencies. This is useful for development and testing purposes. See [CONTRIBUTING.md](./CONTRIBUTING.md#git-dependencies) for documentation. [PR #1873](https://github.com/shakacode/react_on_rails/pull/1873) by [alexeyr-ci2](https://github.com/alexeyr-ci2).
45
+
46
+ - **Doctor Checks for :async Loading Strategy**: Added proactive diagnostic checks to the React on Rails doctor tool to detect usage of the `:async` loading strategy in projects without React on Rails Pro. The feature scans view files and initializer configuration, providing clear guidance to either upgrade to Pro or use alternative loading strategies like `:defer` or `:sync` to avoid component registration race conditions. [PR 2010](https://github.com/shakacode/react_on_rails/pull/2010) by [justin808](https://github.com/justin808).
47
+
38
48
  #### Changed
39
49
 
40
50
  - **Shakapacker 9.0.0 Upgrade**: Upgraded Shakapacker from 8.2.0 to 9.0.0 with Babel transpiler configuration for compatibility. Key changes include:
@@ -45,19 +55,35 @@ Changes since the last non-beta release.
45
55
  - Fixed webpack configuration to process SCSS rules and CSS loaders in a single pass for better performance
46
56
  [PR 1904](https://github.com/shakacode/react_on_rails/pull/1904) by [justin808](https://github.com/justin808).
47
57
 
48
- - **Shakapacker 9.1.0 Upgrade**: Upgraded Shakapacker from 9.0.0 to 9.1.0. This minor version update includes bug fixes and improvements. Updated webpack configuration in Pro dummy apps to use forEach pattern for better compatibility with multiple SCSS rules. [PR 1921](https://github.com/shakacode/react_on_rails/pull/1921) by [justin808](https://github.com/justin808).
58
+ - **`immediate_hydration` now automatically enabled for Pro users**: The `config.immediate_hydration` configuration option has been removed. Immediate hydration is now automatically enabled for React on Rails Pro users and disabled for non-Pro users, simplifying configuration while providing optimal performance by default. Component-level overrides are still supported via the `immediate_hydration` parameter on `react_component`, `redux_store`, and `stream_react_component` helpers. [PR 1997](https://github.com/shakacode/react_on_rails/pull/1997) by [AbanoubGhadban](https://github.com/AbanoubGhadban).
49
59
 
50
- - **Shakapacker 9.2.0 Upgrade**: Upgraded Shakapacker from 9.1.0 to 9.2.0. This minor version update adds a new `bin/shakapacker-config` utility for debugging webpack/rspack configurations with doctor mode, save mode, and stdout mode options. Supports YAML, JSON, and Node.js inspect output formats. by [justin808](https://github.com/justin808).
60
+ - **`generated_component_packs_loading_strategy` now defaults based on Pro license**: When using Shakapacker >= 8.2.0, the default loading strategy is now `:async` for Pro users and `:defer` for non-Pro users. This provides optimal performance for Pro users while maintaining compatibility for non-Pro users. You can still explicitly set the strategy in your configuration. [PR #1993](https://github.com/shakacode/react_on_rails/pull/1993) by [AbanoubGhadban](https://github.com/AbanoubGhadban).
51
61
 
52
- - **Removed Pro Warning Badge**: Removed the visual warning badge that appeared when non-Pro users attempted to enable Pro-only features like `immediate_hydration`. Pro features are now silently disabled when a Pro license is not available, providing a cleaner user experience without intrusive warning banners. [PR #1993](https://github.com/shakacode/react_on_rails/pull/1993) by [AbanoubGhadban](https://github.com/AbanoubGhadban).
62
+ - **Generator Configuration Modernization**: Updated the generator to enable recommended configurations by default for new applications. `config.build_test_command` is now uncommented and set to `"RAILS_ENV=test bin/shakapacker"` by default, enabling automatic asset building during tests for better integration test reliability. `config.auto_load_bundle = true` is now set by default, enabling automatic loading of component bundles. `config.components_subdirectory = "ror_components"` is now set by default, organizing React components in a dedicated subdirectory. **Note:** These changes only affect newly generated applications. Existing applications are unaffected and do not need to make any changes. If you want to adopt these settings in an existing app, you can manually add them to your `config/initializers/react_on_rails.rb` file. [PR 2039](https://github.com/shakacode/react_on_rails/pull/2039) by [justin808](https://github.com/justin808).
53
63
 
54
- - **`immediate_hydration` now automatically enabled for Pro users**: The `config.immediate_hydration` configuration option has been removed. Immediate hydration is now automatically enabled for React on Rails Pro users and disabled for non-Pro users, simplifying configuration while providing optimal performance by default. Component-level overrides are still supported via the `immediate_hydration` parameter on `react_component`, `redux_store`, and `stream_react_component` helpers. [PR 1997](https://github.com/shakacode/react_on_rails/pull/1997) by [AbanoubGhadban](https://github.com/AbanoubGhadban).
64
+ - **Removed Babel Dependency Installation**: The generator no longer installs `@babel/preset-react` or `@babel/preset-typescript` packages. Shakapacker handles JavaScript transpiler configuration (Babel, SWC, or esbuild) via the `javascript_transpiler` setting in `shakapacker.yml`. SWC is now the default transpiler and includes built-in support for React and TypeScript. Users who explicitly choose Babel will need to manually install and configure the required presets. This change reduces unnecessary dependencies and aligns with Shakapacker's modular transpiler approach. [PR 2051](https://github.com/shakacode/react_on_rails/pull/2051) by [justin808](https://github.com/justin808).
55
65
 
56
- - **`generated_component_packs_loading_strategy` now defaults based on Pro license**: When using Shakapacker >= 8.2.0, the default loading strategy is now `:async` for Pro users and `:defer` for non-Pro users. This provides optimal performance for Pro users while maintaining compatibility for non-Pro users. You can still explicitly set the strategy in your configuration. [PR #1993](https://github.com/shakacode/react_on_rails/pull/1993) by [AbanoubGhadban](https://github.com/AbanoubGhadban).
66
+ #### Documentation
57
67
 
58
- #### Bug Fixes
68
+ - **Simplified Configuration Files**: Improved configuration documentation and generator template for better clarity and usability. Reduced generator template from 67 to 42 lines (37% reduction). Added comprehensive testing configuration guide. Reorganized configuration docs into Essential vs Advanced sections. Enhanced Doctor program with diagnostics for server rendering and test compilation consistency. [PR #2011](https://github.com/shakacode/react_on_rails/pull/2011) by [justin808](https://github.com/justin808).
59
69
 
60
- - **Use as Git dependency**: All packages can now be installed as Git dependencies. This is useful for development and testing purposes. See [CONTRIBUTING.md](./CONTRIBUTING.md#git-dependencies) for documentation. [PR #1873](https://github.com/shakacode/react_on_rails/pull/1873) by [alexeyr-ci2](https://github.com/alexeyr-ci2).
70
+ #### Deprecated
71
+
72
+ - **Node Renderer Configuration**: Renamed `bundlePath` configuration option to `serverBundleCachePath` in the node renderer to better describe its purpose and avoid confusion with Shakapacker's public bundle path. The old `bundlePath` option continues to work with deprecation warnings. Both `RENDERER_SERVER_BUNDLE_CACHE_PATH` (new) and `RENDERER_BUNDLE_PATH` (deprecated) environment variables are supported. [PR #2008](https://github.com/shakacode/react_on_rails/pull/2008) by [justin808](https://github.com/justin808).
73
+
74
+ #### Fixed
75
+
76
+ - **Node Renderer Worker Restart**: Fixed "descriptor closed" error that occurred when the node renderer restarts while handling an in-progress request (especially streaming requests). Workers now perform graceful shutdowns: they disconnect from the cluster to stop receiving new requests, wait for active requests to complete, then shut down cleanly. A configurable `gracefulWorkerRestartTimeout` ensures workers are forcibly killed if they don't shut down in time. [PR 1970](https://github.com/shakacode/react_on_rails/pull/1970) by [AbanoubGhadban](https://github.com/AbanoubGhadban).
77
+
78
+ - **Body Duplication Bug On Streaming**: Fixed a bug that happens while streaming if the node renderer connection closed after streaming some chunks to the client. [PR #1995](https://github.com/shakacode/react_on_rails/pull/1995) by [AbanoubGhadban](https://github.com/AbanoubGhadban).
79
+
80
+ - **bin/dev --verbose Option**: Fixed `OptionParser::InvalidOption` error that occurred when using the `--verbose`/`-v` flag. The flag was documented in help text but not functional. Now properly implemented with flag parsing and passing verbose option through method calls. [PR 2023](https://github.com/shakacode/react_on_rails/pull/2023) by [justin808](https://github.com/justin808).
81
+
82
+ - **Shakapacker Template Configuration**: Fixed shakapacker.yml template to prevent unnecessary "Slow setup for development" warnings by setting `compile: false` as the default configuration. This allows development environments to inherit the setting instead of forcing on-demand compilation when using Procfiles with `bin/dev` that already run the shakapacker dev server or watch mode. [PR 2021](https://github.com/shakacode/react_on_rails/pull/2021) by [justin808](https://github.com/justin808).
83
+
84
+ #### Improved
85
+
86
+ - **Concurrent Streaming Performance**: Implemented concurrent draining of streamed React components using the async gem. Instead of processing components sequentially, the system now uses a producer-consumer pattern with bounded buffering to allow multiple components to stream simultaneously while maintaining per-component chunk ordering. [PR 2015](https://github.com/shakacode/react_on_rails/pull/2015) by [ihabadham](https://github.com/ihabadham).
61
87
 
62
88
  #### Breaking Changes
63
89
 
@@ -1819,7 +1845,9 @@ such as:
1819
1845
 
1820
1846
  - Fix several generator-related issues.
1821
1847
 
1822
- [unreleased]: https://github.com/shakacode/react_on_rails/compare/16.1.1...master
1848
+ [unreleased]: https://github.com/shakacode/react_on_rails/compare/v16.2.0.beta.8...master
1849
+ [v16.2.0.beta.8]: https://github.com/shakacode/react_on_rails/compare/16.2.0.beta.4...v16.2.0.beta.8
1850
+ [16.2.0.beta.4]: https://github.com/shakacode/react_on_rails/compare/16.1.1...16.2.0.beta.4
1823
1851
  [16.1.1]: https://github.com/shakacode/react_on_rails/compare/16.1.0...16.1.1
1824
1852
  [16.1.0]: https://github.com/shakacode/react_on_rails/compare/16.0.0...16.1.0
1825
1853
  [16.0.0]: https://github.com/shakacode/react_on_rails/compare/14.2.0...16.0.0
data/CONTRIBUTING.md CHANGED
@@ -129,7 +129,7 @@ Package react-on-rails@12.0.0-12070fd1 added ==> /Users/justin/shakacode/react-o
129
129
  Don't forget you may need to run yarn after adding packages with yalc to install/update dependencies/bin scripts.
130
130
  ```
131
131
 
132
- Of course, you can do the same with `react-on-rails-pro` and `@shakacode-tools/react-on-rails-pro-node-renderer` packages.
132
+ Of course, you can do the same with `react-on-rails-pro` and `react-on-rails-pro-node-renderer` packages.
133
133
 
134
134
  This is the approach `spec/dummy` apps use, so you can also look at their implementation.
135
135
 
@@ -38,7 +38,6 @@ group :development, :test do
38
38
  gem "rubocop", "1.61.0", require: false
39
39
  gem "rubocop-performance", "~>1.20.0", require: false
40
40
  gem "rubocop-rspec", "~>2.26", require: false
41
- gem "scss_lint", require: false
42
41
  gem "spring", "~> 4.0"
43
42
  gem "lefthook", require: false
44
43
  # Added for Ruby 3.5+ compatibility to silence warnings
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- react_on_rails (16.2.0.beta.4)
4
+ react_on_rails (16.2.0.beta.10)
5
5
  addressable
6
6
  connection_pool
7
7
  execjs (~> 2.5)
@@ -332,11 +332,6 @@ GEM
332
332
  rubocop (~> 1.40)
333
333
  ruby-progressbar (1.13.0)
334
334
  rubyzip (2.3.2)
335
- sass (3.7.4)
336
- sass-listen (~> 4.0.0)
337
- sass-listen (4.0.0)
338
- rb-fsevent (~> 0.9, >= 0.9.4)
339
- rb-inotify (~> 0.9, >= 0.9.7)
340
335
  sass-rails (6.0.0)
341
336
  sassc-rails (~> 2.1, >= 2.1.1)
342
337
  sassc (2.4.0)
@@ -347,8 +342,6 @@ GEM
347
342
  sprockets (> 3.0)
348
343
  sprockets-rails
349
344
  tilt
350
- scss_lint (0.60.0)
351
- sass (~> 3.5, >= 3.5.5)
352
345
  sdoc (2.6.1)
353
346
  rdoc (>= 5.0)
354
347
  securerandom (0.4.1)
@@ -473,7 +466,6 @@ DEPENDENCIES
473
466
  rubocop-performance (~> 1.20.0)
474
467
  rubocop-rspec (~> 2.26)
475
468
  sass-rails (~> 6.0)
476
- scss_lint
477
469
  sdoc
478
470
  selenium-webdriver (= 4.9.0)
479
471
  shakapacker (= 9.3.0)
@@ -173,15 +173,41 @@ if [ -z "$FAILED_CHECKS" ]; then
173
173
  exit 0
174
174
  fi
175
175
 
176
- # Map CI job names to local commands
176
+ # Map CI job names to identifiers
177
177
  # NOTE: Version numbers below must match .github/workflows/main.yml matrix configuration
178
178
  declare -A JOB_MAP
179
- JOB_MAP["lint-js-and-ruby"]="bundle exec rubocop && yarn run eslint --report-unused-disable-directives && yarn start format.listDifferent"
180
- JOB_MAP["rspec-package-tests"]="bundle exec rake run_rspec:gem"
181
- JOB_MAP["package-js-tests"]="yarn test"
182
- JOB_MAP["dummy-app-integration-tests (3.4, 22, latest)"]="bundle exec rake run_rspec:all_dummy"
183
- JOB_MAP["dummy-app-integration-tests (3.2, 20, minimum)"]="bundle exec rake run_rspec:all_dummy"
184
- JOB_MAP["examples"]="bundle exec rake run_rspec:shakapacker_examples"
179
+ JOB_MAP["lint-js-and-ruby"]="lint-js-and-ruby"
180
+ JOB_MAP["rspec-package-tests"]="rspec-package-tests"
181
+ JOB_MAP["package-js-tests"]="package-js-tests"
182
+ JOB_MAP["dummy-app-integration-tests (3.4, 22, latest)"]="dummy-app-integration-tests"
183
+ JOB_MAP["dummy-app-integration-tests (3.2, 20, minimum)"]="dummy-app-integration-tests"
184
+ JOB_MAP["examples"]="examples"
185
+
186
+ # Function to execute commands without eval
187
+ run_command() {
188
+ local cmd_id="$1"
189
+ case "$cmd_id" in
190
+ "lint-js-and-ruby")
191
+ bundle exec rubocop && yarn run eslint --report-unused-disable-directives && yarn start format.listDifferent
192
+ ;;
193
+ "rspec-package-tests")
194
+ bundle exec rake run_rspec:gem
195
+ ;;
196
+ "package-js-tests")
197
+ yarn test
198
+ ;;
199
+ "dummy-app-integration-tests")
200
+ bundle exec rake run_rspec:all_dummy
201
+ ;;
202
+ "examples")
203
+ bundle exec rake run_rspec:shakapacker_examples
204
+ ;;
205
+ *)
206
+ echo "Unknown command ID: $cmd_id"
207
+ return 1
208
+ ;;
209
+ esac
210
+ }
185
211
 
186
212
  # Map CI job names to human-readable versions (matches SWITCHING_CI_CONFIGS.md)
187
213
  declare -A JOB_VERSION_MAP
@@ -236,10 +262,10 @@ if [ "$NUM_COMMANDS" -eq 0 ]; then
236
262
  fi
237
263
 
238
264
  echo -e "${BLUE}Will run the following commands:${NC}"
239
- for cmd in "${!COMMANDS_TO_RUN[@]}"; do
240
- job_name="${COMMANDS_TO_RUN[$cmd]}"
265
+ for cmd_id in "${!COMMANDS_TO_RUN[@]}"; do
266
+ job_name="${COMMANDS_TO_RUN[$cmd_id]}"
241
267
  version_info=$(get_version_info "$job_name")
242
- echo -e "${BLUE} • $job_name${version_info}:${NC} $cmd"
268
+ echo -e "${BLUE} • $job_name${version_info}${NC}"
243
269
  done
244
270
  echo ""
245
271
 
@@ -270,17 +296,14 @@ fi
270
296
  # Run commands
271
297
  FAILED_COMMANDS=()
272
298
 
273
- for cmd in "${!COMMANDS_TO_RUN[@]}"; do
274
- job_name="${COMMANDS_TO_RUN[$cmd]}"
299
+ for cmd_id in "${!COMMANDS_TO_RUN[@]}"; do
300
+ job_name="${COMMANDS_TO_RUN[$cmd_id]}"
275
301
  version_info=$(get_version_info "$job_name")
276
302
 
277
303
  echo -e "${BLUE}▶ Running: $job_name${version_info}${NC}"
278
- echo -e "${BLUE}Command: $cmd${NC}"
279
304
  echo ""
280
305
 
281
- # Note: Using eval here is safe because $cmd comes from predefined JOB_MAP,
282
- # not from user input. Commands may contain shell operators like && and ||.
283
- if eval "$cmd"; then
306
+ if run_command "$cmd_id"; then
284
307
  echo -e "${GREEN}✓ $job_name${version_info} passed${NC}"
285
308
  echo ""
286
309
  else
@@ -139,7 +139,7 @@ echo ""
139
139
 
140
140
  # Determine the working directory (check if we need to be in spec/dummy)
141
141
  WORKING_DIR="."
142
- if [[ "${UNIQUE_SPECS[0]}" == *"spec/system"* ]] || [[ "${UNIQUE_SPECS[0]}" == *"spec/helpers"* ]]; then
142
+ if [ ${#UNIQUE_SPECS[@]} -gt 0 ] && ([[ "${UNIQUE_SPECS[0]}" == *"spec/system"* ]] || [[ "${UNIQUE_SPECS[0]}" == *"spec/helpers"* ]]); then
143
143
  if [ -d "spec/dummy" ]; then
144
144
  WORKING_DIR="spec/dummy"
145
145
  echo -e "${BLUE}Running from spec/dummy directory${NC}"
data/bin/ci-switch-config CHANGED
@@ -255,6 +255,9 @@ EOF
255
255
  set_node_version "20.18.1" "$VERSION_MANAGER"
256
256
 
257
257
  # Run conversion script
258
+ # NOTE: This uses whatever 'ruby' is in PATH after version manager updates above.
259
+ # The version manager may not have reloaded yet, so ensure your current Ruby is
260
+ # compatible with script/convert (Ruby 2.6+ should work).
258
261
  print_header "Running script/convert to downgrade dependencies"
259
262
  cd "$PROJECT_ROOT"
260
263
  ruby script/convert
@@ -395,8 +398,11 @@ EOF
395
398
  # Restore files from git
396
399
  print_header "Restoring dependency files from git"
397
400
  cd "$PROJECT_ROOT"
398
- git restore Gemfile.development_dependencies package.json spec/dummy/package.json packages/react-on-rails-pro/package.json 2>/dev/null || true
399
- print_success "Files restored from git"
401
+ if ! git restore Gemfile.development_dependencies package.json spec/dummy/package.json packages/react-on-rails-pro/package.json 2>/dev/null; then
402
+ print_warning "Some files could not be restored (may not exist in git)"
403
+ else
404
+ print_success "Files restored from git"
405
+ fi
400
406
 
401
407
  # Clean and reinstall
402
408
  print_header "Cleaning node_modules and reinstalling"
@@ -17,6 +17,7 @@ else
17
17
  fi
18
18
  printf " %s\n" $files
19
19
 
20
- bundle exec rake autofix
20
+ # Run RuboCop only on changed files (not the full rake autofix)
21
+ bundle exec rubocop -A --force-exclusion -- $files
21
22
 
22
23
  echo "✅ Auto-fix complete (fixed files staged automatically)"
data/knip.ts CHANGED
@@ -5,13 +5,11 @@ const config: KnipConfig = {
5
5
  workspaces: {
6
6
  // Root workspace - manages the monorepo and global tooling
7
7
  '.': {
8
- entry: ['eslint.config.ts'],
8
+ entry: ['eslint.config.ts', 'jest.config.base.js'],
9
9
  project: ['*.{js,mjs,ts}'],
10
10
  ignoreBinaries: [
11
11
  // Has to be installed globally
12
12
  'yalc',
13
- // Used in package.json scripts (devDependency, so unlisted in production mode)
14
- 'nps',
15
13
  // Pro package binaries used in Pro workflows
16
14
  'playwright',
17
15
  'e2e-test',
@@ -41,6 +39,17 @@ const config: KnipConfig = {
41
39
  // SWC transpiler dependencies used in dummy apps
42
40
  '@swc/core',
43
41
  'swc-loader',
42
+ // Test dependencies used by child workspaces (packages/react-on-rails, packages/react-on-rails-pro)
43
+ '@testing-library/dom',
44
+ '@testing-library/jest-dom',
45
+ '@testing-library/react',
46
+ '@types/react-dom',
47
+ 'create-react-class',
48
+ 'jest-fetch-mock',
49
+ 'prop-types',
50
+ 'react',
51
+ 'react-dom',
52
+ 'redux',
44
53
  ],
45
54
  },
46
55
 
@@ -89,6 +98,11 @@ const config: KnipConfig = {
89
98
  'src/RSCRoute.tsx:RSCRouteProps',
90
99
  'src/streamServerRenderedReactComponent.ts:StreamingTrackers',
91
100
  ],
101
+ ignoreDependencies: [
102
+ // Test dependencies used only in tests
103
+ '@types/mock-fs',
104
+ 'mock-fs',
105
+ ],
92
106
  },
93
107
  'spec/dummy': {
94
108
  entry: [
@@ -112,6 +126,12 @@ const config: KnipConfig = {
112
126
  '**/app-react16/**/*',
113
127
  // Playwright support files and helpers - generated by cypress-on-rails gem
114
128
  'e2e/playwright/support/**',
129
+ // Components and files used dynamically by React on Rails (registered at runtime)
130
+ 'client/app/actions/**',
131
+ 'client/app/components/**',
132
+ 'client/app/routes/**',
133
+ 'client/app/startup/**',
134
+ 'client/app/store/**',
115
135
  ],
116
136
  project: ['**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}!', 'config/webpack/*.js'],
117
137
  paths: {
@@ -124,9 +144,6 @@ const config: KnipConfig = {
124
144
  'bin/.*',
125
145
  ],
126
146
  ignoreDependencies: [
127
- // Build-time dependencies not detected by Knip in any mode
128
- '@babel/runtime',
129
- 'mini-css-extract-plugin',
130
147
  // There's no ReScript plugin for Knip
131
148
  '@rescript/react',
132
149
  // The Babel plugin fails to detect it
@@ -136,18 +153,27 @@ const config: KnipConfig = {
136
153
  'node-libs-browser',
137
154
  // The below dependencies are not detected by the Webpack plugin
138
155
  // due to the config issue.
139
- 'css-loader',
140
156
  'expose-loader',
141
157
  'file-loader',
142
158
  'imports-loader',
143
159
  'null-loader',
144
- 'sass',
145
- 'sass-loader',
146
160
  'sass-resources-loader',
147
161
  'style-loader',
148
162
  'url-loader',
149
163
  // Transitive dependency of shakapacker but listed as direct dependency
150
164
  'webpack-merge',
165
+ // Dependencies not detected in production mode (runtime injected or dynamic imports)
166
+ '@babel/runtime',
167
+ 'mini-css-extract-plugin',
168
+ 'css-loader',
169
+ 'sass',
170
+ 'sass-loader',
171
+ // Dependencies used dynamically by React on Rails
172
+ 'create-react-class',
173
+ 'react-helmet',
174
+ '@types/react-helmet',
175
+ 'react-redux',
176
+ 'react-router-dom',
151
177
  ],
152
178
  },
153
179
  },
@@ -4,10 +4,12 @@ require "rails/generators"
4
4
  require "fileutils"
5
5
  require_relative "generator_messages"
6
6
  require_relative "generator_helper"
7
+ require_relative "js_dependency_manager"
7
8
  module ReactOnRails
8
9
  module Generators
9
10
  class BaseGenerator < Rails::Generators::Base
10
11
  include GeneratorHelper
12
+ include JsDependencyManager
11
13
 
12
14
  Rails::Generators.hide_namespace(namespace)
13
15
  source_root(File.expand_path("templates", __dir__))
@@ -107,7 +109,7 @@ module ReactOnRails
107
109
  run "bundle"
108
110
  end
109
111
 
110
- def update_gitignore_for_generated_bundles
112
+ def update_gitignore_for_auto_registration
111
113
  gitignore_path = File.join(destination_root, ".gitignore")
112
114
  return unless File.exist?(gitignore_path)
113
115
 
@@ -146,123 +148,6 @@ module ReactOnRails
146
148
 
147
149
  private
148
150
 
149
- def setup_js_dependencies
150
- add_js_dependencies
151
- install_js_dependencies
152
- end
153
-
154
- def add_js_dependencies
155
- add_react_on_rails_package
156
- add_react_dependencies
157
- add_css_dependencies
158
- add_dev_dependencies
159
- end
160
-
161
- def add_react_on_rails_package
162
- major_minor_patch_only = /\A\d+\.\d+\.\d+\z/
163
-
164
- # Try to use package_json gem first, fall back to direct npm commands
165
- react_on_rails_pkg = if ReactOnRails::VERSION.match?(major_minor_patch_only)
166
- ["react-on-rails@#{ReactOnRails::VERSION}"]
167
- else
168
- puts "Adding the latest react-on-rails NPM module. " \
169
- "Double check this is correct in package.json"
170
- ["react-on-rails"]
171
- end
172
-
173
- puts "Installing React on Rails package..."
174
- return if add_npm_dependencies(react_on_rails_pkg)
175
-
176
- puts "Using direct npm commands as fallback"
177
- success = system("npm", "install", *react_on_rails_pkg)
178
- handle_npm_failure("react-on-rails package", react_on_rails_pkg) unless success
179
- end
180
-
181
- def add_react_dependencies
182
- puts "Installing React dependencies..."
183
- react_deps = %w[
184
- react
185
- react-dom
186
- @babel/preset-react
187
- prop-types
188
- babel-plugin-transform-react-remove-prop-types
189
- babel-plugin-macros
190
- ]
191
- return if add_npm_dependencies(react_deps)
192
-
193
- success = system("npm", "install", *react_deps)
194
- handle_npm_failure("React dependencies", react_deps) unless success
195
- end
196
-
197
- def add_css_dependencies
198
- puts "Installing CSS handling dependencies..."
199
- css_deps = %w[
200
- css-loader
201
- css-minimizer-webpack-plugin
202
- mini-css-extract-plugin
203
- style-loader
204
- ]
205
- return if add_npm_dependencies(css_deps)
206
-
207
- success = system("npm", "install", *css_deps)
208
- handle_npm_failure("CSS dependencies", css_deps) unless success
209
- end
210
-
211
- def add_dev_dependencies
212
- puts "Installing development dependencies..."
213
- dev_deps = %w[
214
- @pmmmwh/react-refresh-webpack-plugin
215
- react-refresh
216
- ]
217
- return if add_npm_dependencies(dev_deps, dev: true)
218
-
219
- success = system("npm", "install", "--save-dev", *dev_deps)
220
- handle_npm_failure("development dependencies", dev_deps, dev: true) unless success
221
- end
222
-
223
- def install_js_dependencies
224
- # Detect which package manager to use
225
- success = if File.exist?(File.join(destination_root, "yarn.lock"))
226
- system("yarn", "install")
227
- elsif File.exist?(File.join(destination_root, "pnpm-lock.yaml"))
228
- system("pnpm", "install")
229
- elsif File.exist?(File.join(destination_root, "package-lock.json")) ||
230
- File.exist?(File.join(destination_root, "package.json"))
231
- # Use npm for package-lock.json or as default fallback
232
- system("npm", "install")
233
- else
234
- true # No package manager detected, skip
235
- end
236
-
237
- unless success
238
- GeneratorMessages.add_warning(<<~MSG.strip)
239
- ⚠️ JavaScript dependencies installation failed.
240
-
241
- This could be due to network issues or missing package manager.
242
- You can install dependencies manually later by running:
243
- • npm install (if using npm)
244
- • yarn install (if using yarn)
245
- • pnpm install (if using pnpm)
246
- MSG
247
- end
248
-
249
- success
250
- end
251
-
252
- def handle_npm_failure(dependency_type, packages, dev: false)
253
- install_command = dev ? "npm install --save-dev" : "npm install"
254
- GeneratorMessages.add_warning(<<~MSG.strip)
255
- ⚠️ Failed to install #{dependency_type}.
256
-
257
- The following packages could not be installed automatically:
258
- #{packages.map { |pkg| " • #{pkg}" }.join("\n")}
259
-
260
- This could be due to network issues or missing package manager.
261
- You can install them manually later by running:
262
- #{install_command} #{packages.join(' ')}
263
- MSG
264
- end
265
-
266
151
  def copy_webpack_main_config(base_path, config)
267
152
  webpack_config_path = "config/webpack/webpack.config.js"
268
153