react_on_rails 16.2.0.beta.3 → 16.2.0.beta.8
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +42 -5
- data/CLAUDE.md +59 -0
- data/CONTRIBUTING.md +49 -1
- data/Gemfile.development_dependencies +1 -1
- data/Gemfile.lock +25 -10
- data/SWITCHING_CI_CONFIGS.md +55 -6
- data/Steepfile +51 -0
- data/bin/ci-rerun-failures +68 -22
- data/bin/ci-run-failed-specs +26 -2
- data/bin/ci-switch-config +262 -34
- data/bin/lefthook/check-trailing-newlines +2 -12
- data/bin/lefthook/eslint-lint +0 -10
- data/bin/lefthook/prettier-format +0 -10
- data/bin/lefthook/ruby-autofix +3 -6
- data/knip.ts +35 -9
- data/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt +32 -52
- data/lib/generators/react_on_rails/templates/base/base/config/shakapacker.yml +5 -1
- data/lib/react_on_rails/configuration.rb +56 -12
- data/lib/react_on_rails/controller.rb +3 -3
- data/lib/react_on_rails/dev/server_manager.rb +11 -4
- data/lib/react_on_rails/doctor.rb +249 -2
- data/lib/react_on_rails/helper.rb +12 -3
- data/lib/react_on_rails/pro_helper.rb +2 -44
- data/lib/react_on_rails/react_component/render_options.rb +7 -7
- data/lib/react_on_rails/utils.rb +40 -0
- data/lib/react_on_rails/version.rb +1 -1
- data/react_on_rails_pro/CHANGELOG.md +142 -29
- data/react_on_rails_pro/CONTRIBUTING.md +2 -13
- data/react_on_rails_pro/Gemfile.development_dependencies +1 -0
- data/react_on_rails_pro/Gemfile.lock +24 -3
- data/react_on_rails_pro/README.md +559 -38
- data/react_on_rails_pro/docs/code-splitting-loadable-components.md +1 -1
- data/react_on_rails_pro/docs/contributors-info/releasing.md +2 -2
- data/react_on_rails_pro/docs/installation.md +129 -109
- data/react_on_rails_pro/docs/node-renderer/basics.md +29 -22
- data/react_on_rails_pro/docs/node-renderer/error-reporting-and-tracing.md +8 -8
- data/react_on_rails_pro/docs/node-renderer/js-configuration.md +25 -23
- data/react_on_rails_pro/docs/node-renderer/troubleshooting.md +2 -0
- data/react_on_rails_pro/docs/updating.md +209 -15
- data/react_on_rails_pro/lib/react_on_rails_pro/concerns/stream.rb +58 -4
- data/react_on_rails_pro/lib/react_on_rails_pro/configuration.rb +17 -3
- data/react_on_rails_pro/lib/react_on_rails_pro/license_public_key.rb +9 -9
- data/react_on_rails_pro/lib/react_on_rails_pro/request.rb +41 -25
- data/react_on_rails_pro/lib/react_on_rails_pro/stream_request.rb +27 -7
- data/react_on_rails_pro/lib/react_on_rails_pro/utils.rb +3 -3
- data/react_on_rails_pro/lib/react_on_rails_pro/version.rb +1 -1
- data/react_on_rails_pro/package-scripts.yml +1 -1
- data/react_on_rails_pro/package.json +5 -8
- data/react_on_rails_pro/packages/node-renderer/src/integrations/api.ts +1 -1
- data/react_on_rails_pro/packages/node-renderer/src/master/restartWorkers.ts +39 -17
- data/react_on_rails_pro/packages/node-renderer/src/master.ts +15 -4
- data/react_on_rails_pro/packages/node-renderer/src/shared/configBuilder.ts +44 -5
- data/react_on_rails_pro/packages/node-renderer/src/shared/utils.ts +4 -2
- data/react_on_rails_pro/packages/node-renderer/src/worker/handleGracefulShutdown.ts +49 -0
- data/react_on_rails_pro/packages/node-renderer/src/worker/vm.ts +3 -3
- data/react_on_rails_pro/packages/node-renderer/src/worker.ts +5 -2
- data/react_on_rails_pro/packages/node-renderer/tests/helper.ts +8 -8
- data/react_on_rails_pro/packages/node-renderer/tests/testingNodeRendererConfigs.js +1 -1
- data/react_on_rails_pro/packages/node-renderer/tests/worker.test.ts +19 -19
- data/react_on_rails_pro/rakelib/public_key_management.rake +6 -5
- data/react_on_rails_pro/rakelib/rbs.rake +47 -0
- data/react_on_rails_pro/react_on_rails_pro.gemspec +1 -0
- data/react_on_rails_pro/sig/react_on_rails_pro/cache.rbs +13 -0
- data/react_on_rails_pro/sig/react_on_rails_pro/configuration.rbs +100 -0
- data/react_on_rails_pro/sig/react_on_rails_pro/error.rbs +4 -0
- data/react_on_rails_pro/sig/react_on_rails_pro/utils.rbs +7 -0
- data/react_on_rails_pro/sig/react_on_rails_pro.rbs +5 -0
- data/react_on_rails_pro/spec/dummy/Gemfile.lock +23 -3
- data/react_on_rails_pro/spec/dummy/app/controllers/pages_controller.rb +3 -3
- data/react_on_rails_pro/spec/dummy/bin/dev +4 -8
- data/react_on_rails_pro/spec/dummy/client/node-renderer.js +4 -4
- data/react_on_rails_pro/spec/dummy/config/environments/production.rb +1 -1
- data/react_on_rails_pro/spec/dummy/config/initializers/react_on_rails.rb +28 -12
- data/react_on_rails_pro/spec/dummy/config.ru +1 -1
- data/react_on_rails_pro/spec/dummy/package.json +2 -2
- data/react_on_rails_pro/spec/dummy/spec/helpers/react_on_rails_pro_helper_spec.rb +40 -11
- data/react_on_rails_pro/spec/dummy/spec/rails_helper.rb +1 -1
- data/react_on_rails_pro/spec/dummy/spec/requests/renderer_console_logging_spec.rb +5 -5
- data/react_on_rails_pro/spec/dummy/spec/system/integration_spec.rb +15 -10
- data/react_on_rails_pro/spec/dummy/spec/system/renderer_integration_spec.rb +3 -3
- data/react_on_rails_pro/spec/dummy/yarn.lock +4 -4
- data/react_on_rails_pro/spec/execjs-compatible-dummy/config/environments/production.rb +1 -1
- data/react_on_rails_pro/spec/execjs-compatible-dummy/config/initializers/react_on_rails.rb +16 -43
- data/react_on_rails_pro/spec/react_on_rails_pro/assets_precompile_spec.rb +15 -18
- data/react_on_rails_pro/spec/react_on_rails_pro/cache_spec.rb +1 -1
- data/react_on_rails_pro/spec/react_on_rails_pro/configuration_spec.rb +5 -3
- data/react_on_rails_pro/spec/react_on_rails_pro/license_validator_spec.rb +27 -12
- data/react_on_rails_pro/spec/react_on_rails_pro/request_spec.rb +0 -27
- data/react_on_rails_pro/spec/react_on_rails_pro/spec_helper.rb +1 -1
- data/react_on_rails_pro/spec/react_on_rails_pro/stream_decorator_spec.rb +89 -0
- data/react_on_rails_pro/spec/react_on_rails_pro/stream_spec.rb +144 -0
- data/react_on_rails_pro/spec/react_on_rails_pro/support/caching.rb +1 -1
- data/react_on_rails_pro/spec/react_on_rails_pro/support/mock_block_helper.rb +4 -2
- data/sig/react_on_rails/controller.rbs +1 -1
- data/sig/react_on_rails/error.rbs +4 -0
- data/sig/react_on_rails/helper.rbs +2 -2
- data/sig/react_on_rails/json_parse_error.rbs +10 -0
- data/sig/react_on_rails/prerender_error.rbs +21 -0
- data/sig/react_on_rails/smart_error.rbs +28 -0
- data/sig/react_on_rails.rbs +3 -24
- metadata +14 -4
- data/lib/react_on_rails/pro_utils.rb +0 -37
- data/react_on_rails_pro/spec/dummy/client/app/ror-auto-load-components/TestingStreamableComponent.jsx +0 -15
data/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt
CHANGED
|
@@ -1,67 +1,47 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
#
|
|
4
|
-
#
|
|
3
|
+
# React on Rails configuration
|
|
4
|
+
# See https://github.com/shakacode/react_on_rails/blob/master/docs/api-reference/configuration.md
|
|
5
|
+
# for complete documentation of all configuration options.
|
|
5
6
|
|
|
6
7
|
ReactOnRails.configure do |config|
|
|
7
|
-
# This configures the script to run to build the production assets by webpack. Set this to nil
|
|
8
|
-
# if you don't want react_on_rails building this file for you.
|
|
9
|
-
# If nil, then the standard shakacode/shakapacker assets:precompile will run
|
|
10
|
-
# config.build_production_command = nil
|
|
11
|
-
|
|
12
|
-
################################################################################
|
|
13
8
|
################################################################################
|
|
14
|
-
#
|
|
15
|
-
# Below options are used with the use of this test helper:
|
|
16
|
-
# ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)
|
|
9
|
+
# Server Rendering (Recommended)
|
|
17
10
|
################################################################################
|
|
18
|
-
|
|
19
|
-
#
|
|
20
|
-
|
|
21
|
-
# ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)
|
|
22
|
-
#
|
|
23
|
-
# with rspec then this controls what npm command is run
|
|
24
|
-
# to automatically refresh your webpack assets on every test run.
|
|
25
|
-
#
|
|
26
|
-
# Alternately, you can remove the `ReactOnRails::TestHelper.configure_rspec_to_compile_assets`
|
|
27
|
-
# and set the config/shakapacker.yml option for test to true.
|
|
28
|
-
config.build_test_command = "RAILS_ENV=test bin/shakapacker"
|
|
11
|
+
# Configure server bundle for server-side rendering with `prerender: true`
|
|
12
|
+
# Set to "" if you're not using server rendering
|
|
13
|
+
config.server_bundle_js_file = "server-bundle.js"
|
|
29
14
|
|
|
30
15
|
################################################################################
|
|
16
|
+
# Test Configuration (Optional)
|
|
31
17
|
################################################################################
|
|
32
|
-
#
|
|
33
|
-
################################################################################
|
|
34
|
-
# This is the file used for server rendering of React when using `(prerender: true)`
|
|
35
|
-
# If you are never using server rendering, you should set this to "".
|
|
36
|
-
# Note, there is only one server bundle, unlike JavaScript where you want to minimize the size
|
|
37
|
-
# of the JS sent to the client. For the server rendering, React on Rails creates a pool of
|
|
38
|
-
# JavaScript execution instances which should handle any component requested.
|
|
18
|
+
# ⚠️ IMPORTANT: Two mutually exclusive approaches - use ONLY ONE:
|
|
39
19
|
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
20
|
+
# RECOMMENDED APPROACH: Set `compile: true` in config/shakapacker.yml test section
|
|
21
|
+
# - Simpler configuration (no additional setup needed)
|
|
22
|
+
# - Handled automatically by Shakapacker
|
|
43
23
|
#
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
# Enforce that server bundles are only loaded from private (non-public) directories.
|
|
51
|
-
# When true, server bundles will only be loaded from the configured server_bundle_output_path.
|
|
52
|
-
# This is recommended for production to prevent server-side code from being exposed.
|
|
53
|
-
config.enforce_private_server_bundles = true
|
|
24
|
+
# ALTERNATIVE APPROACH: Uncomment below AND configure ReactOnRails::TestHelper
|
|
25
|
+
# - Provides explicit control over test asset compilation timing
|
|
26
|
+
# - Requires adding ReactOnRails::TestHelper to spec/rails_helper.rb
|
|
27
|
+
# - See: https://github.com/shakacode/react_on_rails/blob/master/docs/guides/testing-configuration.md
|
|
28
|
+
#
|
|
29
|
+
config.build_test_command = "RAILS_ENV=test bin/shakapacker"
|
|
54
30
|
|
|
55
|
-
|
|
56
|
-
################################################################################
|
|
57
|
-
# FILE SYSTEM BASED COMPONENT REGISTRY
|
|
58
|
-
################################################################################
|
|
59
|
-
# `components_subdirectory` is the name of the matching directories that contain automatically registered components
|
|
60
|
-
# for use in the Rails views. The default is nil, you can enable the feature by updating it in the next line.
|
|
31
|
+
config.auto_load_bundle = true
|
|
61
32
|
config.components_subdirectory = "ror_components"
|
|
33
|
+
################################################################################
|
|
34
|
+
# Advanced Configuration
|
|
35
|
+
################################################################################
|
|
36
|
+
# Most configuration options have sensible defaults and don't need to be set.
|
|
37
|
+
# For advanced options including:
|
|
38
|
+
# - File-based component registry (components_subdirectory, auto_load_bundle)
|
|
39
|
+
# - Component loading strategies (async/defer/sync)
|
|
40
|
+
# - Server bundle security and organization
|
|
41
|
+
# - I18n configuration
|
|
42
|
+
# - Server rendering pool configuration
|
|
43
|
+
# - Custom rendering extensions
|
|
44
|
+
# - And more...
|
|
62
45
|
#
|
|
63
|
-
#
|
|
64
|
-
# generated directory. default is false, you can pass option at the time of individual usage or update the default
|
|
65
|
-
# in the following line
|
|
66
|
-
config.auto_load_bundle = true
|
|
46
|
+
# See: https://github.com/shakacode/react_on_rails/blob/master/docs/api-reference/configuration.md
|
|
67
47
|
end
|
|
@@ -55,6 +55,11 @@ default: &default
|
|
|
55
55
|
# https://webpack.js.org/guides/build-performance/#avoid-production-specific-tooling
|
|
56
56
|
useContentHash: false
|
|
57
57
|
|
|
58
|
+
# On-demand compilation of packs when modified. Defaults to false.
|
|
59
|
+
# Set to false if using bin/shakapacker-dev-server or bin/shakapacker --watch via Procfiles.
|
|
60
|
+
# Set to true only in test environment for on-demand compilation.
|
|
61
|
+
compile: false
|
|
62
|
+
|
|
58
63
|
# Setting the asset host here will override Rails.application.config.asset_host.
|
|
59
64
|
# Here, you can set different asset_host per environment. Note that
|
|
60
65
|
# SHAKAPACKER_ASSET_HOST will override both configurations.
|
|
@@ -72,7 +77,6 @@ default: &default
|
|
|
72
77
|
|
|
73
78
|
development:
|
|
74
79
|
<<: *default
|
|
75
|
-
compile: true
|
|
76
80
|
compiler_strategy: mtime
|
|
77
81
|
|
|
78
82
|
# Reference: https://webpack.js.org/configuration/dev-server/
|
|
@@ -41,8 +41,6 @@ module ReactOnRails
|
|
|
41
41
|
components_subdirectory: nil,
|
|
42
42
|
make_generated_server_bundle_the_entrypoint: false,
|
|
43
43
|
defer_generated_component_packs: false,
|
|
44
|
-
# React on Rails Pro (licensed) feature - enables immediate hydration of React components
|
|
45
|
-
immediate_hydration: false,
|
|
46
44
|
# Maximum time in milliseconds to wait for client-side component registration after page load.
|
|
47
45
|
# If exceeded, an error will be thrown for server-side rendered components not registered on the client.
|
|
48
46
|
# Set to 0 to disable the timeout and wait indefinitely for component registration.
|
|
@@ -64,10 +62,55 @@ module ReactOnRails
|
|
|
64
62
|
:server_render_method, :random_dom_id, :auto_load_bundle,
|
|
65
63
|
:same_bundle_for_client_and_server, :rendering_props_extension,
|
|
66
64
|
:make_generated_server_bundle_the_entrypoint,
|
|
67
|
-
:generated_component_packs_loading_strategy,
|
|
65
|
+
:generated_component_packs_loading_strategy,
|
|
68
66
|
:component_registry_timeout,
|
|
69
67
|
:server_bundle_output_path, :enforce_private_server_bundles
|
|
70
68
|
|
|
69
|
+
# Class instance variable and mutex to track if deprecation warning has been shown
|
|
70
|
+
# Using mutex to ensure thread-safety in multi-threaded environments
|
|
71
|
+
@immediate_hydration_warned = false
|
|
72
|
+
@immediate_hydration_mutex = Mutex.new
|
|
73
|
+
|
|
74
|
+
class << self
|
|
75
|
+
attr_accessor :immediate_hydration_warned, :immediate_hydration_mutex
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Deprecated: immediate_hydration configuration has been removed
|
|
79
|
+
def immediate_hydration=(value)
|
|
80
|
+
warned = false
|
|
81
|
+
self.class.immediate_hydration_mutex.synchronize do
|
|
82
|
+
warned = self.class.immediate_hydration_warned
|
|
83
|
+
self.class.immediate_hydration_warned = true unless warned
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
return if warned
|
|
87
|
+
|
|
88
|
+
Rails.logger.warn <<~WARNING
|
|
89
|
+
[REACT ON RAILS] The 'config.immediate_hydration' configuration option is deprecated and no longer used.
|
|
90
|
+
Immediate hydration is now automatically enabled for React on Rails Pro users.
|
|
91
|
+
Please remove 'config.immediate_hydration = #{value}' from your config/initializers/react_on_rails.rb file.
|
|
92
|
+
See CHANGELOG.md for migration instructions.
|
|
93
|
+
WARNING
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def immediate_hydration
|
|
97
|
+
warned = false
|
|
98
|
+
self.class.immediate_hydration_mutex.synchronize do
|
|
99
|
+
warned = self.class.immediate_hydration_warned
|
|
100
|
+
self.class.immediate_hydration_warned = true unless warned
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
return nil if warned
|
|
104
|
+
|
|
105
|
+
Rails.logger.warn <<~WARNING
|
|
106
|
+
[REACT ON RAILS] The 'config.immediate_hydration' configuration option is deprecated and no longer used.
|
|
107
|
+
Immediate hydration is now automatically enabled for React on Rails Pro users.
|
|
108
|
+
Please remove any references to 'config.immediate_hydration' from your config/initializers/react_on_rails.rb file.
|
|
109
|
+
See CHANGELOG.md for migration instructions.
|
|
110
|
+
WARNING
|
|
111
|
+
nil
|
|
112
|
+
end
|
|
113
|
+
|
|
71
114
|
# rubocop:disable Metrics/AbcSize
|
|
72
115
|
def initialize(node_modules_location: nil, server_bundle_js_file: nil, prerender: nil,
|
|
73
116
|
replay_console: nil, make_generated_server_bundle_the_entrypoint: nil,
|
|
@@ -81,7 +124,7 @@ module ReactOnRails
|
|
|
81
124
|
same_bundle_for_client_and_server: nil,
|
|
82
125
|
i18n_dir: nil, i18n_yml_dir: nil, i18n_output_format: nil, i18n_yml_safe_load_options: nil,
|
|
83
126
|
random_dom_id: nil, server_render_method: nil, rendering_props_extension: nil,
|
|
84
|
-
components_subdirectory: nil, auto_load_bundle: nil,
|
|
127
|
+
components_subdirectory: nil, auto_load_bundle: nil,
|
|
85
128
|
component_registry_timeout: nil, server_bundle_output_path: nil, enforce_private_server_bundles: nil)
|
|
86
129
|
self.node_modules_location = node_modules_location.present? ? node_modules_location : Rails.root
|
|
87
130
|
self.generated_assets_dirs = generated_assets_dirs
|
|
@@ -122,7 +165,6 @@ module ReactOnRails
|
|
|
122
165
|
self.auto_load_bundle = auto_load_bundle
|
|
123
166
|
self.make_generated_server_bundle_the_entrypoint = make_generated_server_bundle_the_entrypoint
|
|
124
167
|
self.defer_generated_component_packs = defer_generated_component_packs
|
|
125
|
-
self.immediate_hydration = immediate_hydration
|
|
126
168
|
self.generated_component_packs_loading_strategy = generated_component_packs_loading_strategy
|
|
127
169
|
self.server_bundle_output_path = server_bundle_output_path
|
|
128
170
|
self.enforce_private_server_bundles = enforce_private_server_bundles
|
|
@@ -154,9 +196,9 @@ module ReactOnRails
|
|
|
154
196
|
raise ReactOnRails::Error, "component_registry_timeout must be a positive integer"
|
|
155
197
|
end
|
|
156
198
|
|
|
157
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
199
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
158
200
|
def validate_generated_component_packs_loading_strategy
|
|
159
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
|
201
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
160
202
|
|
|
161
203
|
if defer_generated_component_packs
|
|
162
204
|
if %i[async sync].include?(generated_component_packs_loading_strategy)
|
|
@@ -176,11 +218,13 @@ module ReactOnRails
|
|
|
176
218
|
1. Use :defer or :sync loading strategy instead of :async
|
|
177
219
|
2. Upgrade to Shakapacker v8.2.0 or above to enable async script loading
|
|
178
220
|
MSG
|
|
179
|
-
if
|
|
180
|
-
#
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
221
|
+
if PackerUtils.supports_async_loading?
|
|
222
|
+
# Default based on Pro license: Pro users get :async, non-Pro users get :defer
|
|
223
|
+
self.generated_component_packs_loading_strategy ||= (Utils.react_on_rails_pro? ? :async : :defer)
|
|
224
|
+
elsif generated_component_packs_loading_strategy.nil?
|
|
225
|
+
Rails.logger.warn("**WARNING** #{msg}")
|
|
226
|
+
self.generated_component_packs_loading_strategy = :sync
|
|
227
|
+
elsif generated_component_packs_loading_strategy == :async
|
|
184
228
|
raise ReactOnRails::Error, "**ERROR** #{msg}"
|
|
185
229
|
end
|
|
186
230
|
|
|
@@ -9,13 +9,13 @@ module ReactOnRails
|
|
|
9
9
|
# JavaScript code.
|
|
10
10
|
# props: Named parameter props which is a Ruby Hash or JSON string which contains the properties
|
|
11
11
|
# to pass to the redux store.
|
|
12
|
-
# immediate_hydration: React on Rails Pro (licensed) feature.
|
|
13
|
-
#
|
|
12
|
+
# immediate_hydration: React on Rails Pro (licensed) feature. When nil (default), Pro users get
|
|
13
|
+
# immediate hydration, non-Pro users don't. Can be explicitly overridden.
|
|
14
14
|
#
|
|
15
15
|
# Be sure to include view helper `redux_store_hydration_data` at the end of your layout or view
|
|
16
16
|
# or else there will be no client side hydration of your stores.
|
|
17
17
|
def redux_store(store_name, props: {}, immediate_hydration: nil)
|
|
18
|
-
immediate_hydration = ReactOnRails.
|
|
18
|
+
immediate_hydration = ReactOnRails::Utils.normalize_immediate_hydration(immediate_hydration, store_name, "Store")
|
|
19
19
|
redux_store_data = { store_name: store_name,
|
|
20
20
|
props: props,
|
|
21
21
|
immediate_hydration: immediate_hydration }
|
|
@@ -144,10 +144,11 @@ module ReactOnRails
|
|
|
144
144
|
puts help_troubleshooting
|
|
145
145
|
end
|
|
146
146
|
|
|
147
|
+
# rubocop:disable Metrics/AbcSize
|
|
147
148
|
def run_from_command_line(args = ARGV)
|
|
148
149
|
require "optparse"
|
|
149
150
|
|
|
150
|
-
options = { route: nil, rails_env: nil }
|
|
151
|
+
options = { route: nil, rails_env: nil, verbose: false }
|
|
151
152
|
|
|
152
153
|
OptionParser.new do |opts|
|
|
153
154
|
opts.banner = "Usage: dev [command] [options]"
|
|
@@ -160,6 +161,10 @@ module ReactOnRails
|
|
|
160
161
|
options[:rails_env] = env
|
|
161
162
|
end
|
|
162
163
|
|
|
164
|
+
opts.on("-v", "--verbose", "Enable verbose output for pack generation") do
|
|
165
|
+
options[:verbose] = true
|
|
166
|
+
end
|
|
167
|
+
|
|
163
168
|
opts.on("-h", "--help", "Prints this help") do
|
|
164
169
|
show_help
|
|
165
170
|
exit
|
|
@@ -172,21 +177,23 @@ module ReactOnRails
|
|
|
172
177
|
# Main execution
|
|
173
178
|
case command
|
|
174
179
|
when "production-assets", "prod"
|
|
175
|
-
start(:production_like, nil, verbose:
|
|
180
|
+
start(:production_like, nil, verbose: options[:verbose], route: options[:route],
|
|
181
|
+
rails_env: options[:rails_env])
|
|
176
182
|
when "static"
|
|
177
|
-
start(:static, "Procfile.dev-static-assets", verbose:
|
|
183
|
+
start(:static, "Procfile.dev-static-assets", verbose: options[:verbose], route: options[:route])
|
|
178
184
|
when "kill"
|
|
179
185
|
kill_processes
|
|
180
186
|
when "help", "--help", "-h"
|
|
181
187
|
show_help
|
|
182
188
|
when "hmr", nil
|
|
183
|
-
start(:development, "Procfile.dev", verbose:
|
|
189
|
+
start(:development, "Procfile.dev", verbose: options[:verbose], route: options[:route])
|
|
184
190
|
else
|
|
185
191
|
puts "Unknown argument: #{command}"
|
|
186
192
|
puts "Run 'dev help' for usage information"
|
|
187
193
|
exit 1
|
|
188
194
|
end
|
|
189
195
|
end
|
|
196
|
+
# rubocop:enable Metrics/AbcSize
|
|
190
197
|
|
|
191
198
|
private
|
|
192
199
|
|
|
@@ -154,6 +154,7 @@ module ReactOnRails
|
|
|
154
154
|
def check_configuration_details
|
|
155
155
|
check_shakapacker_configuration_details
|
|
156
156
|
check_react_on_rails_configuration_details
|
|
157
|
+
check_server_bundle_prerender_consistency
|
|
157
158
|
end
|
|
158
159
|
|
|
159
160
|
def check_bin_dev_launcher
|
|
@@ -166,6 +167,7 @@ module ReactOnRails
|
|
|
166
167
|
|
|
167
168
|
def check_testing_setup
|
|
168
169
|
check_rspec_helper_setup
|
|
170
|
+
check_build_test_configuration
|
|
169
171
|
end
|
|
170
172
|
|
|
171
173
|
def check_development
|
|
@@ -173,6 +175,7 @@ module ReactOnRails
|
|
|
173
175
|
check_procfile_dev
|
|
174
176
|
check_bin_dev_script
|
|
175
177
|
check_gitignore
|
|
178
|
+
check_async_usage
|
|
176
179
|
end
|
|
177
180
|
|
|
178
181
|
def check_javascript_bundles
|
|
@@ -732,10 +735,12 @@ module ReactOnRails
|
|
|
732
735
|
auto_load_match = content.match(/config\.auto_load_bundle\s*=\s*([^\s\n,]+)/)
|
|
733
736
|
checker.add_info(" auto_load_bundle: #{auto_load_match[1]}") if auto_load_match
|
|
734
737
|
|
|
735
|
-
#
|
|
738
|
+
# Deprecated immediate_hydration setting
|
|
736
739
|
immediate_hydration_match = content.match(/config\.immediate_hydration\s*=\s*([^\s\n,]+)/)
|
|
737
740
|
if immediate_hydration_match
|
|
738
|
-
checker.
|
|
741
|
+
checker.add_warning(" ⚠️ immediate_hydration: #{immediate_hydration_match[1]} (DEPRECATED)")
|
|
742
|
+
checker.add_info(" 💡 This setting is no longer used. Immediate hydration is now automatic for Pro users.")
|
|
743
|
+
checker.add_info(" 💡 Remove this line from your config/initializers/react_on_rails.rb file.")
|
|
739
744
|
end
|
|
740
745
|
|
|
741
746
|
# Component registry timeout
|
|
@@ -1108,6 +1113,124 @@ module ReactOnRails
|
|
|
1108
1113
|
end
|
|
1109
1114
|
end
|
|
1110
1115
|
|
|
1116
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
1117
|
+
def check_server_bundle_prerender_consistency
|
|
1118
|
+
config_path = "config/initializers/react_on_rails.rb"
|
|
1119
|
+
return unless File.exist?(config_path)
|
|
1120
|
+
|
|
1121
|
+
checker.add_info("\n🔍 Server Rendering Consistency:")
|
|
1122
|
+
|
|
1123
|
+
begin
|
|
1124
|
+
content = File.read(config_path)
|
|
1125
|
+
|
|
1126
|
+
# Check for server bundle configuration
|
|
1127
|
+
server_bundle_match = content.match(/config\.server_bundle_js_file\s*=\s*["']([^"']+)["']/)
|
|
1128
|
+
server_bundle_set = server_bundle_match && server_bundle_match[1].present?
|
|
1129
|
+
|
|
1130
|
+
# Check for global prerender setting
|
|
1131
|
+
prerender_match = content.match(/config\.prerender\s*=\s*(true)/)
|
|
1132
|
+
prerender_set = prerender_match
|
|
1133
|
+
|
|
1134
|
+
# Check if prerender is used in views
|
|
1135
|
+
uses_prerender = uses_prerender_in_views?
|
|
1136
|
+
|
|
1137
|
+
# Analyze the configuration
|
|
1138
|
+
if (prerender_set || uses_prerender) && !server_bundle_set
|
|
1139
|
+
checker.add_warning(" ⚠️ Server rendering is enabled but server_bundle_js_file is not configured")
|
|
1140
|
+
checker.add_info(" 💡 Set config.server_bundle_js_file = 'server-bundle.js' to enable SSR")
|
|
1141
|
+
checker.add_info(" 💡 See: https://www.shakacode.com/react-on-rails/docs/guides/server-rendering")
|
|
1142
|
+
elsif server_bundle_set && !prerender_set && !uses_prerender
|
|
1143
|
+
checker.add_info(" ℹ️ server_bundle_js_file is configured but prerender doesn't appear to be used")
|
|
1144
|
+
checker.add_info(" 💡 Either use prerender: true in react_component calls or remove server_bundle_js_file")
|
|
1145
|
+
else
|
|
1146
|
+
checker.add_success(" ✅ Server rendering configuration is consistent")
|
|
1147
|
+
end
|
|
1148
|
+
rescue StandardError => e
|
|
1149
|
+
checker.add_warning(" ⚠️ Could not analyze server rendering configuration: #{e.message}")
|
|
1150
|
+
end
|
|
1151
|
+
end
|
|
1152
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
1153
|
+
|
|
1154
|
+
def uses_prerender_in_views?
|
|
1155
|
+
# Check view files for prerender: true
|
|
1156
|
+
view_files = Dir.glob("app/views/**/*.{erb,haml,slim}")
|
|
1157
|
+
view_files.any? do |file|
|
|
1158
|
+
next unless File.exist?(file)
|
|
1159
|
+
|
|
1160
|
+
File.read(file).match?(/prerender:\s*true/)
|
|
1161
|
+
end
|
|
1162
|
+
rescue StandardError
|
|
1163
|
+
false
|
|
1164
|
+
end
|
|
1165
|
+
|
|
1166
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
|
|
1167
|
+
def check_build_test_configuration
|
|
1168
|
+
config_path = "config/initializers/react_on_rails.rb"
|
|
1169
|
+
shakapacker_yml = "config/shakapacker.yml"
|
|
1170
|
+
|
|
1171
|
+
return unless File.exist?(config_path)
|
|
1172
|
+
|
|
1173
|
+
checker.add_info("\n🧪 Test Asset Compilation:")
|
|
1174
|
+
|
|
1175
|
+
begin
|
|
1176
|
+
config_content = File.read(config_path)
|
|
1177
|
+
has_build_test_command = config_content.match(/^\s*config\.build_test_command\s*=\s*["']([^"']+)["']/)
|
|
1178
|
+
uses_test_helper = uses_react_on_rails_test_helper?
|
|
1179
|
+
|
|
1180
|
+
if File.exist?(shakapacker_yml)
|
|
1181
|
+
shakapacker_content = File.read(shakapacker_yml)
|
|
1182
|
+
# Match test section and look for compile: true
|
|
1183
|
+
has_compile_true = shakapacker_content.match(/^test:.*?^\s+compile:\s*true/m)
|
|
1184
|
+
|
|
1185
|
+
if has_build_test_command && has_compile_true
|
|
1186
|
+
checker.add_warning(" ⚠️ Both build_test_command and shakapacker compile: true are configured")
|
|
1187
|
+
checker.add_info(" 💡 These are mutually exclusive - use only one approach")
|
|
1188
|
+
checker.add_info(" 💡 Recommended: Use compile: true in shakapacker.yml (simpler)")
|
|
1189
|
+
checker.add_info(" 💡 Alternative: Use build_test_command with ReactOnRails::TestHelper (explicit control)")
|
|
1190
|
+
checker.add_info(" 📖 See: https://github.com/shakacode/react_on_rails/blob/master/docs/guides/testing-configuration.md")
|
|
1191
|
+
elsif has_build_test_command && !uses_test_helper
|
|
1192
|
+
checker.add_warning(" ⚠️ build_test_command is set but ReactOnRails::TestHelper is not configured")
|
|
1193
|
+
checker.add_info(" 💡 Add to spec/rails_helper.rb:")
|
|
1194
|
+
checker.add_info(" ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)")
|
|
1195
|
+
checker.add_info(" 💡 Or remove build_test_command and use compile: true in shakapacker.yml")
|
|
1196
|
+
elsif !has_build_test_command && uses_test_helper
|
|
1197
|
+
checker.add_error(" 🚫 ReactOnRails::TestHelper is configured but build_test_command is not set")
|
|
1198
|
+
checker.add_info(" 💡 Add to config/initializers/react_on_rails.rb:")
|
|
1199
|
+
checker.add_info(" config.build_test_command = 'RAILS_ENV=test bin/shakapacker'")
|
|
1200
|
+
checker.add_info(" 💡 Or remove TestHelper and use compile: true in shakapacker.yml")
|
|
1201
|
+
elsif !has_build_test_command && !has_compile_true && !uses_test_helper
|
|
1202
|
+
checker.add_warning(" ⚠️ No test asset compilation configured")
|
|
1203
|
+
checker.add_info(" 💡 Recommended: Add to shakapacker.yml test section:")
|
|
1204
|
+
checker.add_info(" compile: true")
|
|
1205
|
+
checker.add_info(" 📖 See: https://github.com/shakacode/react_on_rails/blob/master/docs/guides/testing-configuration.md")
|
|
1206
|
+
elsif has_compile_true
|
|
1207
|
+
checker.add_success(" ✅ Test assets configured via Shakapacker auto-compilation")
|
|
1208
|
+
checker.add_info(" (compile: true in shakapacker.yml)")
|
|
1209
|
+
elsif has_build_test_command && uses_test_helper
|
|
1210
|
+
checker.add_success(" ✅ Test assets configured via React on Rails test helper")
|
|
1211
|
+
checker.add_info(" (build_test_command + ReactOnRails::TestHelper)")
|
|
1212
|
+
end
|
|
1213
|
+
else
|
|
1214
|
+
checker.add_warning(" ⚠️ config/shakapacker.yml not found")
|
|
1215
|
+
end
|
|
1216
|
+
rescue StandardError => e
|
|
1217
|
+
checker.add_warning(" ⚠️ Could not analyze test configuration: #{e.message}")
|
|
1218
|
+
end
|
|
1219
|
+
end
|
|
1220
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
|
|
1221
|
+
|
|
1222
|
+
def uses_react_on_rails_test_helper?
|
|
1223
|
+
spec_helpers = ["spec/rails_helper.rb", "spec/spec_helper.rb", "test/test_helper.rb"]
|
|
1224
|
+
spec_helpers.any? do |helper|
|
|
1225
|
+
next unless File.exist?(helper)
|
|
1226
|
+
|
|
1227
|
+
content = File.read(helper)
|
|
1228
|
+
content.include?("configure_rspec_to_compile_assets") || content.include?("ensure_assets_compiled")
|
|
1229
|
+
end
|
|
1230
|
+
rescue StandardError
|
|
1231
|
+
false
|
|
1232
|
+
end
|
|
1233
|
+
|
|
1111
1234
|
def relativize_path(absolute_path)
|
|
1112
1235
|
return absolute_path unless absolute_path.is_a?(String)
|
|
1113
1236
|
|
|
@@ -1144,6 +1267,130 @@ module ReactOnRails
|
|
|
1144
1267
|
checker.add_info(" #{label}: <error reading value: #{e.message}>")
|
|
1145
1268
|
end
|
|
1146
1269
|
end
|
|
1270
|
+
|
|
1271
|
+
# Comment patterns used for filtering out commented async usage
|
|
1272
|
+
ERB_COMMENT_PATTERN = /<%\s*#.*javascript_pack_tag/
|
|
1273
|
+
HAML_COMMENT_PATTERN = /^\s*-#.*javascript_pack_tag/
|
|
1274
|
+
SLIM_COMMENT_PATTERN = %r{^\s*/.*javascript_pack_tag}
|
|
1275
|
+
HTML_COMMENT_PATTERN = /<!--.*javascript_pack_tag/
|
|
1276
|
+
|
|
1277
|
+
def check_async_usage
|
|
1278
|
+
# When Pro is installed, async is fully supported and is the default behavior
|
|
1279
|
+
# No need to check for async usage in this case
|
|
1280
|
+
return if ReactOnRails::Utils.react_on_rails_pro?
|
|
1281
|
+
|
|
1282
|
+
async_issues = []
|
|
1283
|
+
|
|
1284
|
+
# Check 1: javascript_pack_tag with :async in view files
|
|
1285
|
+
view_files_with_async = scan_view_files_for_async_pack_tag
|
|
1286
|
+
unless view_files_with_async.empty?
|
|
1287
|
+
async_issues << "javascript_pack_tag with :async found in view files:"
|
|
1288
|
+
view_files_with_async.each do |file|
|
|
1289
|
+
async_issues << " • #{file}"
|
|
1290
|
+
end
|
|
1291
|
+
end
|
|
1292
|
+
|
|
1293
|
+
# Check 2: generated_component_packs_loading_strategy = :async
|
|
1294
|
+
if config_has_async_loading_strategy?
|
|
1295
|
+
async_issues << "config.generated_component_packs_loading_strategy = :async in initializer"
|
|
1296
|
+
end
|
|
1297
|
+
|
|
1298
|
+
return if async_issues.empty?
|
|
1299
|
+
|
|
1300
|
+
# Report errors if async usage is found without Pro
|
|
1301
|
+
checker.add_error("🚫 :async usage detected without React on Rails Pro")
|
|
1302
|
+
async_issues.each { |issue| checker.add_error(" #{issue}") }
|
|
1303
|
+
checker.add_info(" 💡 :async can cause race conditions. Options:")
|
|
1304
|
+
checker.add_info(" 1. Upgrade to React on Rails Pro (recommended for :async support)")
|
|
1305
|
+
checker.add_info(" 2. Change to :defer or :sync loading strategy")
|
|
1306
|
+
checker.add_info(" 📖 https://www.shakacode.com/react-on-rails/docs/guides/configuration/")
|
|
1307
|
+
end
|
|
1308
|
+
|
|
1309
|
+
def scan_view_files_for_async_pack_tag
|
|
1310
|
+
view_patterns = ["app/views/**/*.erb", "app/views/**/*.haml", "app/views/**/*.slim"]
|
|
1311
|
+
files_with_async = view_patterns.flat_map { |pattern| scan_pattern_for_async(pattern) }
|
|
1312
|
+
files_with_async.compact
|
|
1313
|
+
rescue Errno::ENOENT, Encoding::InvalidByteSequenceError, Encoding::UndefinedConversionError => e
|
|
1314
|
+
# Log the error if Rails logger is available
|
|
1315
|
+
log_debug("Error scanning view files for async: #{e.message}")
|
|
1316
|
+
[]
|
|
1317
|
+
end
|
|
1318
|
+
|
|
1319
|
+
def scan_pattern_for_async(pattern)
|
|
1320
|
+
Dir.glob(pattern).filter_map do |file|
|
|
1321
|
+
next unless File.exist?(file)
|
|
1322
|
+
|
|
1323
|
+
content = File.read(file)
|
|
1324
|
+
next if content_has_only_commented_async?(content)
|
|
1325
|
+
next unless file_has_async_pack_tag?(content)
|
|
1326
|
+
|
|
1327
|
+
relativize_path(file)
|
|
1328
|
+
end
|
|
1329
|
+
end
|
|
1330
|
+
|
|
1331
|
+
def file_has_async_pack_tag?(content)
|
|
1332
|
+
# Match javascript_pack_tag with :async symbol or async: true hash syntax
|
|
1333
|
+
# Examples that should match:
|
|
1334
|
+
# - javascript_pack_tag "app", :async
|
|
1335
|
+
# - javascript_pack_tag "app", async: true
|
|
1336
|
+
# - javascript_pack_tag "app", :async, other_option: value
|
|
1337
|
+
# Examples that should NOT match:
|
|
1338
|
+
# - javascript_pack_tag "app", defer: "async" (async is a string value, not the option)
|
|
1339
|
+
# - javascript_pack_tag "app", :defer
|
|
1340
|
+
# Note: Theoretical edge case `data: { async: true }` would match but is extremely unlikely
|
|
1341
|
+
# in real code and represents a harmless false positive (showing a warning when not needed)
|
|
1342
|
+
# Use word boundary \b to ensure :async is not part of a longer symbol like :async_mode
|
|
1343
|
+
# [^<]* allows matching across newlines within ERB tags but stops at closing ERB tag
|
|
1344
|
+
content.match?(/javascript_pack_tag[^<]*(?::async\b|async:\s*true)/)
|
|
1345
|
+
end
|
|
1346
|
+
|
|
1347
|
+
def content_has_only_commented_async?(content)
|
|
1348
|
+
# Check if all occurrences of javascript_pack_tag with :async are in comments
|
|
1349
|
+
# Returns true if ONLY commented async usage exists (no active async usage)
|
|
1350
|
+
|
|
1351
|
+
# First check if there's any javascript_pack_tag with :async in the full content
|
|
1352
|
+
return true unless file_has_async_pack_tag?(content)
|
|
1353
|
+
|
|
1354
|
+
# Strategy: Remove all commented lines, then check if any :async remains
|
|
1355
|
+
# This handles both single-line and multi-line tags correctly
|
|
1356
|
+
uncommented_lines = content.each_line.reject do |line|
|
|
1357
|
+
line.match?(ERB_COMMENT_PATTERN) ||
|
|
1358
|
+
line.match?(HAML_COMMENT_PATTERN) ||
|
|
1359
|
+
line.match?(SLIM_COMMENT_PATTERN) ||
|
|
1360
|
+
line.match?(HTML_COMMENT_PATTERN)
|
|
1361
|
+
end
|
|
1362
|
+
|
|
1363
|
+
uncommented_content = uncommented_lines.join
|
|
1364
|
+
# If no async found in uncommented content, all async usage was commented
|
|
1365
|
+
!file_has_async_pack_tag?(uncommented_content)
|
|
1366
|
+
end
|
|
1367
|
+
|
|
1368
|
+
def config_has_async_loading_strategy?
|
|
1369
|
+
config_path = "config/initializers/react_on_rails.rb"
|
|
1370
|
+
return false unless File.exist?(config_path)
|
|
1371
|
+
|
|
1372
|
+
content = File.read(config_path)
|
|
1373
|
+
# Check if generated_component_packs_loading_strategy is set to :async
|
|
1374
|
+
# Filter out commented lines (lines starting with # after optional whitespace)
|
|
1375
|
+
content.each_line.any? do |line|
|
|
1376
|
+
# Skip lines that start with # (after optional whitespace)
|
|
1377
|
+
next if line.match?(/^\s*#/)
|
|
1378
|
+
|
|
1379
|
+
# Match: config.generated_component_packs_loading_strategy = :async
|
|
1380
|
+
# Use word boundary \b to ensure :async is the complete symbol, not part of :async_mode etc.
|
|
1381
|
+
line.match?(/config\.generated_component_packs_loading_strategy\s*=\s*:async\b/)
|
|
1382
|
+
end
|
|
1383
|
+
rescue Errno::ENOENT, Encoding::InvalidByteSequenceError, Encoding::UndefinedConversionError => e
|
|
1384
|
+
# Log the error if Rails logger is available
|
|
1385
|
+
log_debug("Error checking async loading strategy: #{e.message}")
|
|
1386
|
+
false
|
|
1387
|
+
end
|
|
1388
|
+
|
|
1389
|
+
def log_debug(message)
|
|
1390
|
+
return unless defined?(Rails.logger) && Rails.logger
|
|
1391
|
+
|
|
1392
|
+
Rails.logger.debug(message)
|
|
1393
|
+
end
|
|
1147
1394
|
end
|
|
1148
1395
|
# rubocop:enable Metrics/ClassLength
|
|
1149
1396
|
end
|
|
@@ -155,10 +155,10 @@ module ReactOnRails
|
|
|
155
155
|
# props: Ruby Hash or JSON string which contains the properties to pass to the redux store.
|
|
156
156
|
# Options
|
|
157
157
|
# defer: false -- pass as true if you wish to render this below your component.
|
|
158
|
-
# immediate_hydration:
|
|
159
|
-
#
|
|
158
|
+
# immediate_hydration: nil -- React on Rails Pro (licensed) feature. When nil (default), Pro users
|
|
159
|
+
# get immediate hydration, non-Pro users don't. Can be explicitly overridden.
|
|
160
160
|
def redux_store(store_name, props: {}, defer: false, immediate_hydration: nil)
|
|
161
|
-
immediate_hydration = ReactOnRails.
|
|
161
|
+
immediate_hydration = ReactOnRails::Utils.normalize_immediate_hydration(immediate_hydration, store_name, "Store")
|
|
162
162
|
|
|
163
163
|
redux_store_data = { store_name: store_name,
|
|
164
164
|
props: props,
|
|
@@ -587,6 +587,15 @@ module ReactOnRails
|
|
|
587
587
|
# It doesn't make any transformation, it listens and raises error if a chunk has errors
|
|
588
588
|
chunk_json_result
|
|
589
589
|
end
|
|
590
|
+
|
|
591
|
+
result.rescue do |err|
|
|
592
|
+
# This error came from the renderer
|
|
593
|
+
raise ReactOnRails::PrerenderError.new(component_name: react_component_name,
|
|
594
|
+
# Sanitize as this might be browser logged
|
|
595
|
+
props: sanitized_props_string(props),
|
|
596
|
+
err: err,
|
|
597
|
+
js_code: js_code)
|
|
598
|
+
end
|
|
590
599
|
elsif result["hasErrors"] && render_options.raise_on_prerender_error
|
|
591
600
|
raise_prerender_error(result, react_component_name, props, js_code)
|
|
592
601
|
end
|