react_on_rails 14.0.5 → 14.1.0.rc.0
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 +26 -1
- data/Gemfile.lock +1 -1
- data/README.md +1 -0
- data/SUMMARY.md +1 -0
- data/lib/react_on_rails/configuration.rb +3 -1
- data/lib/react_on_rails/engine.rb +1 -3
- data/lib/react_on_rails/helper.rb +136 -18
- data/lib/react_on_rails/locales/base.rb +7 -1
- data/lib/react_on_rails/react_component/render_options.rb +16 -0
- data/lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb +34 -20
- data/lib/react_on_rails/version.rb +1 -1
- data/lib/react_on_rails/version_checker.rb +34 -23
- data/tsconfig.json +1 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fa19667ad2e9a246bdfa0fbe76ccd2df1c44a571c0c5adb74e66c2f0775e384a
|
|
4
|
+
data.tar.gz: aa0703cfe098dff2d85950936e58c9cb2affc08ac776402a78ac9bb61f294ac9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6758816cdedf80c7071be8c686a495e70338c39b18d613999fc46fb31d372bf21448b9f99d24f3e5d0c1f56809e1813bdeb1be4ffc8ecdd6a6d7654f8a74c62d
|
|
7
|
+
data.tar.gz: 3cb8d9ea6c601b29bf6baa9ebde16fdfe88ee6ad7243e3709efdb42c6ca485ba7affd9f6b0dc2836f3544af0f9a6c0639b50419f2e1312dd7f4a4927b0bc31c1
|
data/CHANGELOG.md
CHANGED
|
@@ -18,6 +18,30 @@ Please follow the recommendations outlined at [keepachangelog.com](http://keepac
|
|
|
18
18
|
### [Unreleased]
|
|
19
19
|
Changes since the last non-beta release.
|
|
20
20
|
|
|
21
|
+
#### Fixed
|
|
22
|
+
|
|
23
|
+
- Incorrect type and confusing name for `ReactOnRails.registerStore`, use `registerStoreGenerators` instead. [PR 1651](https://github.com/shakacode/react_on_rails/pull/1651) by [alexeyr-ci](https://github.com/alexeyr-ci).
|
|
24
|
+
- Changed the ReactOnRails' version checker to use `ReactOnRails.configuration.node_modules_location` to determine the location of the package.json that the `react-on-rails` dependency is expected to be set by.
|
|
25
|
+
- Also, all errors that would be raised by the version checking have been converted to `Rails.Logger` warnings to avoid any breaking changes. [PR 1657](https://github.com/shakacode/react_on_rails/pull/1657) by [judahmeek](https://github.com/judahmeek).
|
|
26
|
+
- Enable use as a `git:` dependency. [PR 1664](https://github.com/shakacode/react_on_rails/pull/1664) by [alexeyr-ci](https://github.com/alexeyr-ci).
|
|
27
|
+
|
|
28
|
+
#### Added
|
|
29
|
+
- Added streaming server rendering support:
|
|
30
|
+
- [PR #1633](https://github.com/shakacode/react_on_rails/pull/1633) by [AbanoubGhadban](https://github.com/AbanoubGhadban).
|
|
31
|
+
- New `stream_react_component` helper for adding streamed components to views
|
|
32
|
+
- New `streamServerRenderedReactComponent` function in the react-on-rails package that uses React 18's `renderToPipeableStream` API
|
|
33
|
+
- Enables progressive page loading and improved performance for server-rendered React components
|
|
34
|
+
- Added support for replaying console logs that occur during server rendering of streamed React components. This enables debugging of server-side rendering issues by capturing and displaying console output on the client and on the server output. [PR #1647](https://github.com/shakacode/react_on_rails/pull/1647) by [AbanoubGhadban](https://github.com/AbanoubGhadban).
|
|
35
|
+
- Added support for handling errors happening during server rendering of streamed React components. It handles errors that happen during the initial render and errors that happen inside suspense boundaries. [PR #1648](https://github.com/shakacode/react_on_rails/pull/1648) by [AbanoubGhadban](https://github.com/AbanoubGhadban).
|
|
36
|
+
- Added support for passing options to `YAML.safe_load` when loading locale files with `config.i18n_yml_safe_load_options`. [PR #1668](https://github.com/shakacode/react_on_rails/pull/1668) by [dzirtusss](https://github.com/dzirtusss).
|
|
37
|
+
|
|
38
|
+
#### Changed
|
|
39
|
+
- Console replay script generation now awaits the render request promise before generating, allowing it to capture console logs from asynchronous operations. This requires using a version of the Node renderer that supports replaying async console logs. [PR #1649](https://github.com/shakacode/react_on_rails/pull/1649) by [AbanoubGhadban](https://github.com/AbanoubGhadban).
|
|
40
|
+
|
|
41
|
+
### [14.0.5] - 2024-08-20
|
|
42
|
+
#### Fixed
|
|
43
|
+
- Should force load react-components which send over turbo-stream [PR #1620](https://github.com/shakacode/react_on_rails/pull/1620) by [theforestvn88](https://github.com/theforestvn88).
|
|
44
|
+
|
|
21
45
|
### [14.0.4] - 2024-07-02
|
|
22
46
|
|
|
23
47
|
#### Improved
|
|
@@ -1148,7 +1172,8 @@ Best done with Object destructing:
|
|
|
1148
1172
|
##### Fixed
|
|
1149
1173
|
- Fix several generator-related issues.
|
|
1150
1174
|
|
|
1151
|
-
[Unreleased]: https://github.com/shakacode/react_on_rails/compare/14.0.
|
|
1175
|
+
[Unreleased]: https://github.com/shakacode/react_on_rails/compare/14.0.5...master
|
|
1176
|
+
[14.0.5]: https://github.com/shakacode/react_on_rails/compare/14.0.4...14.0.5
|
|
1152
1177
|
[14.0.4]: https://github.com/shakacode/react_on_rails/compare/14.0.3...14.0.4
|
|
1153
1178
|
[14.0.3]: https://github.com/shakacode/react_on_rails/compare/14.0.2...14.0.3
|
|
1154
1179
|
[14.0.2]: https://github.com/shakacode/react_on_rails/compare/14.0.1...14.0.2
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
[](https://github.com/shakacode/react_on_rails/actions/workflows/lint-js-and-ruby.yml)
|
|
18
18
|
|
|
19
19
|
# News
|
|
20
|
+
* [React on Rails Pro](https://www.shakacode.com/react-on-rails-pro/) supports the latest features of React 18, including [React Server Components](https://react.dev/reference/rsc/server-components) and [streaming](https://react.dev/reference/react-dom/server/renderToPipeableStream). Contact [Justin Gordon](mailto:justin@shakacode.com) for more information.
|
|
20
21
|
* ShakaCode now maintains the official successor to `rails/webpacker`, [`shakapacker`](https://github.com/shakacode/shakapacker).
|
|
21
22
|
* Project is updated to support Rails 7 and Shakapacker v6+!
|
|
22
23
|
|
data/SUMMARY.md
CHANGED
|
@@ -17,6 +17,7 @@ Here is the new link:
|
|
|
17
17
|
+ [How React on Rails Works](docs/outdated/how-react-on-rails-works.md)
|
|
18
18
|
+ [Client vs. Server Rendering](./docs/guides/client-vs-server-rendering.md)
|
|
19
19
|
+ [React Server Rendering](./docs/guides/react-server-rendering.md)
|
|
20
|
+
+ [🚀 Next-Gen Server Rendering: Streaming with React 18's Latest APIs](./docs/guides/streaming-server-rendering.md)
|
|
20
21
|
+ [Render-Functions and the RailsContext](docs/guides/render-functions-and-railscontext.md)
|
|
21
22
|
+ [Caching and Performance: React on Rails Pro](https://github.com/shakacode/react_on_rails/wiki).
|
|
22
23
|
+ [Deployment](docs/guides/deployment.md).
|
|
@@ -52,6 +52,7 @@ module ReactOnRails
|
|
|
52
52
|
:generated_assets_dirs, :generated_assets_dir, :components_subdirectory,
|
|
53
53
|
:webpack_generated_files, :rendering_extension, :build_test_command,
|
|
54
54
|
:build_production_command, :i18n_dir, :i18n_yml_dir, :i18n_output_format,
|
|
55
|
+
:i18n_yml_safe_load_options,
|
|
55
56
|
:server_render_method, :random_dom_id, :auto_load_bundle,
|
|
56
57
|
:same_bundle_for_client_and_server, :rendering_props_extension,
|
|
57
58
|
:make_generated_server_bundle_the_entrypoint,
|
|
@@ -69,7 +70,7 @@ module ReactOnRails
|
|
|
69
70
|
rendering_extension: nil, build_test_command: nil,
|
|
70
71
|
build_production_command: nil, defer_generated_component_packs: nil,
|
|
71
72
|
same_bundle_for_client_and_server: nil,
|
|
72
|
-
i18n_dir: nil, i18n_yml_dir: nil, i18n_output_format: nil,
|
|
73
|
+
i18n_dir: nil, i18n_yml_dir: nil, i18n_output_format: nil, i18n_yml_safe_load_options: nil,
|
|
73
74
|
random_dom_id: nil, server_render_method: nil, rendering_props_extension: nil,
|
|
74
75
|
components_subdirectory: nil, auto_load_bundle: nil, force_load: nil)
|
|
75
76
|
self.node_modules_location = node_modules_location.present? ? node_modules_location : Rails.root
|
|
@@ -80,6 +81,7 @@ module ReactOnRails
|
|
|
80
81
|
self.i18n_dir = i18n_dir
|
|
81
82
|
self.i18n_yml_dir = i18n_yml_dir
|
|
82
83
|
self.i18n_output_format = i18n_output_format
|
|
84
|
+
self.i18n_yml_safe_load_options = i18n_yml_safe_load_options
|
|
83
85
|
|
|
84
86
|
self.random_dom_id = random_dom_id
|
|
85
87
|
self.prerender = prerender
|
|
@@ -5,9 +5,7 @@ require "rails/railtie"
|
|
|
5
5
|
module ReactOnRails
|
|
6
6
|
class Engine < ::Rails::Engine
|
|
7
7
|
config.to_prepare do
|
|
8
|
-
|
|
9
|
-
VersionChecker.build.raise_if_gem_and_node_package_versions_differ
|
|
10
|
-
end
|
|
8
|
+
VersionChecker.build.log_if_gem_and_node_package_versions_differ
|
|
11
9
|
ReactOnRails::ServerRenderingPool.reset_pool
|
|
12
10
|
end
|
|
13
11
|
end
|
|
@@ -91,6 +91,64 @@ module ReactOnRails
|
|
|
91
91
|
end
|
|
92
92
|
end
|
|
93
93
|
|
|
94
|
+
# Streams a server-side rendered React component using React's `renderToPipeableStream`.
|
|
95
|
+
# Supports React 18 features like Suspense, concurrent rendering, and selective hydration.
|
|
96
|
+
# Enables progressive rendering and improved performance for large components.
|
|
97
|
+
#
|
|
98
|
+
# Note: This function can only be used with React on Rails Pro.
|
|
99
|
+
# The view that uses this function must be rendered using the
|
|
100
|
+
# `stream_view_containing_react_components` method from the React on Rails Pro gem.
|
|
101
|
+
#
|
|
102
|
+
# Example of an async React component that can benefit from streaming:
|
|
103
|
+
#
|
|
104
|
+
# const AsyncComponent = async () => {
|
|
105
|
+
# const data = await fetchData();
|
|
106
|
+
# return <div>{data}</div>;
|
|
107
|
+
# };
|
|
108
|
+
#
|
|
109
|
+
# function App() {
|
|
110
|
+
# return (
|
|
111
|
+
# <Suspense fallback={<div>Loading...</div>}>
|
|
112
|
+
# <AsyncComponent />
|
|
113
|
+
# </Suspense>
|
|
114
|
+
# );
|
|
115
|
+
# }
|
|
116
|
+
#
|
|
117
|
+
# @param [String] component_name Name of your registered component
|
|
118
|
+
# @param [Hash] options Options for rendering
|
|
119
|
+
# @option options [Hash] :props Props to pass to the react component
|
|
120
|
+
# @option options [String] :dom_id DOM ID of the component container
|
|
121
|
+
# @option options [Hash] :html_options Options passed to content_tag
|
|
122
|
+
# @option options [Boolean] :prerender Set to false to disable server-side rendering
|
|
123
|
+
# @option options [Boolean] :trace Set to true to add extra debugging information to the HTML
|
|
124
|
+
# @option options [Boolean] :raise_on_prerender_error Set to true to raise exceptions during server-side rendering
|
|
125
|
+
# Any other options are passed to the content tag, including the id.
|
|
126
|
+
def stream_react_component(component_name, options = {})
|
|
127
|
+
unless ReactOnRails::Utils.react_on_rails_pro?
|
|
128
|
+
raise ReactOnRails::Error,
|
|
129
|
+
"You must use React on Rails Pro to use the stream_react_component method."
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
if @rorp_rendering_fibers.nil?
|
|
133
|
+
raise ReactOnRails::Error,
|
|
134
|
+
"You must call stream_view_containing_react_components to render the view containing the react component"
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
rendering_fiber = Fiber.new do
|
|
138
|
+
stream = internal_stream_react_component(component_name, options)
|
|
139
|
+
stream.each_chunk do |chunk|
|
|
140
|
+
Fiber.yield chunk
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
@rorp_rendering_fibers << rendering_fiber
|
|
145
|
+
|
|
146
|
+
# return the first chunk of the fiber
|
|
147
|
+
# It contains the initial html of the component
|
|
148
|
+
# all updates will be appended to the stream sent to browser
|
|
149
|
+
rendering_fiber.resume
|
|
150
|
+
end
|
|
151
|
+
|
|
94
152
|
# react_component_hash is used to return multiple HTML strings for server rendering, such as for
|
|
95
153
|
# adding meta-tags to a page.
|
|
96
154
|
# It is exactly like react_component except for the following:
|
|
@@ -330,6 +388,16 @@ module ReactOnRails
|
|
|
330
388
|
|
|
331
389
|
private
|
|
332
390
|
|
|
391
|
+
def internal_stream_react_component(component_name, options = {})
|
|
392
|
+
options = options.merge(stream?: true)
|
|
393
|
+
result = internal_react_component(component_name, options)
|
|
394
|
+
build_react_component_result_for_server_streamed_content(
|
|
395
|
+
rendered_html_stream: result[:result],
|
|
396
|
+
component_specification_tag: result[:tag],
|
|
397
|
+
render_options: result[:render_options]
|
|
398
|
+
)
|
|
399
|
+
end
|
|
400
|
+
|
|
333
401
|
def generated_components_pack_path(component_name)
|
|
334
402
|
"#{ReactOnRails::PackerUtils.packer_source_entry_path}/generated/#{component_name}.js"
|
|
335
403
|
end
|
|
@@ -361,6 +429,32 @@ module ReactOnRails
|
|
|
361
429
|
prepend_render_rails_context(result)
|
|
362
430
|
end
|
|
363
431
|
|
|
432
|
+
def build_react_component_result_for_server_streamed_content(
|
|
433
|
+
rendered_html_stream:,
|
|
434
|
+
component_specification_tag:,
|
|
435
|
+
render_options:
|
|
436
|
+
)
|
|
437
|
+
is_first_chunk = true
|
|
438
|
+
rendered_html_stream.transform do |chunk_json_result|
|
|
439
|
+
if is_first_chunk
|
|
440
|
+
is_first_chunk = false
|
|
441
|
+
build_react_component_result_for_server_rendered_string(
|
|
442
|
+
server_rendered_html: chunk_json_result["html"],
|
|
443
|
+
component_specification_tag: component_specification_tag,
|
|
444
|
+
console_script: chunk_json_result["consoleReplayScript"],
|
|
445
|
+
render_options: render_options
|
|
446
|
+
)
|
|
447
|
+
else
|
|
448
|
+
result_console_script = render_options.replay_console ? chunk_json_result["consoleReplayScript"] : ""
|
|
449
|
+
# No need to prepend component_specification_tag or add rails context again
|
|
450
|
+
# as they're already included in the first chunk
|
|
451
|
+
compose_react_component_html_with_spec_and_console(
|
|
452
|
+
"", chunk_json_result["html"], result_console_script
|
|
453
|
+
)
|
|
454
|
+
end
|
|
455
|
+
end
|
|
456
|
+
end
|
|
457
|
+
|
|
364
458
|
def build_react_component_result_for_server_rendered_hash(
|
|
365
459
|
server_rendered_html: required("server_rendered_html"),
|
|
366
460
|
component_specification_tag: required("component_specification_tag"),
|
|
@@ -397,27 +491,30 @@ module ReactOnRails
|
|
|
397
491
|
|
|
398
492
|
def compose_react_component_html_with_spec_and_console(component_specification_tag, rendered_output, console_script)
|
|
399
493
|
# IMPORTANT: Ensure that we mark string as html_safe to avoid escaping.
|
|
400
|
-
<<~HTML
|
|
494
|
+
html_content = <<~HTML
|
|
401
495
|
#{rendered_output}
|
|
402
496
|
#{component_specification_tag}
|
|
403
497
|
#{console_script}
|
|
404
498
|
HTML
|
|
499
|
+
html_content.strip.html_safe
|
|
405
500
|
end
|
|
406
501
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
return render_value if @rendered_rails_context
|
|
502
|
+
def rails_context_if_not_already_rendered
|
|
503
|
+
return "" if @rendered_rails_context
|
|
410
504
|
|
|
411
505
|
data = rails_context(server_side: false)
|
|
412
506
|
|
|
413
507
|
@rendered_rails_context = true
|
|
414
508
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
509
|
+
content_tag(:script,
|
|
510
|
+
json_safe_and_pretty(data).html_safe,
|
|
511
|
+
type: "application/json",
|
|
512
|
+
id: "js-react-on-rails-context")
|
|
513
|
+
end
|
|
419
514
|
|
|
420
|
-
|
|
515
|
+
# prepend the rails_context if not yet applied
|
|
516
|
+
def prepend_render_rails_context(render_value)
|
|
517
|
+
"#{rails_context_if_not_already_rendered}\n#{render_value}".strip.html_safe
|
|
421
518
|
end
|
|
422
519
|
|
|
423
520
|
def internal_react_component(react_component_name, options = {})
|
|
@@ -473,6 +570,25 @@ ReactOnRails.reactOnRailsComponentLoaded('#{render_options.dom_id}');
|
|
|
473
570
|
props.is_a?(String) ? props : props.to_json
|
|
474
571
|
end
|
|
475
572
|
|
|
573
|
+
def raise_prerender_error(json_result, react_component_name, props, js_code)
|
|
574
|
+
raise ReactOnRails::PrerenderError.new(
|
|
575
|
+
component_name: react_component_name,
|
|
576
|
+
props: sanitized_props_string(props),
|
|
577
|
+
err: nil,
|
|
578
|
+
js_code: js_code,
|
|
579
|
+
console_messages: json_result["consoleReplayScript"]
|
|
580
|
+
)
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
def should_raise_streaming_prerender_error?(chunk_json_result, render_options)
|
|
584
|
+
chunk_json_result["hasErrors"] &&
|
|
585
|
+
(if chunk_json_result["isShellReady"]
|
|
586
|
+
render_options.raise_non_shell_server_rendering_errors
|
|
587
|
+
else
|
|
588
|
+
render_options.raise_on_prerender_error
|
|
589
|
+
end)
|
|
590
|
+
end
|
|
591
|
+
|
|
476
592
|
# Returns object with values that are NOT html_safe!
|
|
477
593
|
def server_rendered_react_component(render_options)
|
|
478
594
|
return { "html" => "", "consoleReplayScript" => "" } unless render_options.prerender
|
|
@@ -520,16 +636,18 @@ ReactOnRails.reactOnRailsComponentLoaded('#{render_options.dom_id}');
|
|
|
520
636
|
js_code: js_code)
|
|
521
637
|
end
|
|
522
638
|
|
|
523
|
-
if
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
639
|
+
if render_options.stream?
|
|
640
|
+
result.transform do |chunk_json_result|
|
|
641
|
+
if should_raise_streaming_prerender_error?(chunk_json_result, render_options)
|
|
642
|
+
raise_prerender_error(chunk_json_result, react_component_name, props, js_code)
|
|
643
|
+
end
|
|
644
|
+
# It doesn't make any transformation, it listens and raises error if a chunk has errors
|
|
645
|
+
chunk_json_result
|
|
646
|
+
end
|
|
647
|
+
elsif result["hasErrors"] && render_options.raise_on_prerender_error
|
|
648
|
+
raise_prerender_error(result, react_component_name, props, js_code)
|
|
532
649
|
end
|
|
650
|
+
|
|
533
651
|
result
|
|
534
652
|
end
|
|
535
653
|
|
|
@@ -115,11 +115,17 @@ module ReactOnRails
|
|
|
115
115
|
translations = {}
|
|
116
116
|
defaults = {}
|
|
117
117
|
locale_files.each do |f|
|
|
118
|
-
|
|
118
|
+
safe_load_options = ReactOnRails.configuration.i18n_yml_safe_load_options || {}
|
|
119
|
+
translation = YAML.safe_load(File.open(f), **safe_load_options)
|
|
119
120
|
key = translation.keys[0]
|
|
120
121
|
val = flatten(translation[key])
|
|
121
122
|
translations = translations.deep_merge(key => val)
|
|
122
123
|
defaults = defaults.deep_merge(flatten_defaults(val)) if key == default_locale
|
|
124
|
+
rescue Psych::Exception => e
|
|
125
|
+
raise ReactOnRails::Error, <<~MSG
|
|
126
|
+
Error parsing #{f}: #{e.message}
|
|
127
|
+
Consider fixing unsafe YAML or permitting with config.i18n_yml_safe_load_options
|
|
128
|
+
MSG
|
|
123
129
|
end
|
|
124
130
|
[translations.to_json, defaults.to_json]
|
|
125
131
|
end
|
|
@@ -87,6 +87,10 @@ module ReactOnRails
|
|
|
87
87
|
retrieve_configuration_value_for(:raise_on_prerender_error)
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
+
def raise_non_shell_server_rendering_errors
|
|
91
|
+
retrieve_react_on_rails_pro_config_value_for(:raise_non_shell_server_rendering_errors)
|
|
92
|
+
end
|
|
93
|
+
|
|
90
94
|
def logging_on_server
|
|
91
95
|
retrieve_configuration_value_for(:logging_on_server)
|
|
92
96
|
end
|
|
@@ -107,6 +111,10 @@ module ReactOnRails
|
|
|
107
111
|
options[key] = value
|
|
108
112
|
end
|
|
109
113
|
|
|
114
|
+
def stream?
|
|
115
|
+
options[:stream?]
|
|
116
|
+
end
|
|
117
|
+
|
|
110
118
|
private
|
|
111
119
|
|
|
112
120
|
attr_reader :options
|
|
@@ -124,6 +132,14 @@ module ReactOnRails
|
|
|
124
132
|
ReactOnRails.configuration.public_send(key)
|
|
125
133
|
end
|
|
126
134
|
end
|
|
135
|
+
|
|
136
|
+
def retrieve_react_on_rails_pro_config_value_for(key)
|
|
137
|
+
options.fetch(key) do
|
|
138
|
+
return nil unless ReactOnRails::Utils.react_on_rails_pro?
|
|
139
|
+
|
|
140
|
+
ReactOnRailsPro.configuration.public_send(key)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
127
143
|
end
|
|
128
144
|
end
|
|
129
145
|
end
|
|
@@ -46,7 +46,7 @@ module ReactOnRails
|
|
|
46
46
|
# Note, js_code does not have to be based on React.
|
|
47
47
|
# js_code MUST RETURN json stringify Object
|
|
48
48
|
# Calling code will probably call 'html_safe' on return value before rendering to the view.
|
|
49
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
49
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
|
50
50
|
def exec_server_render_js(js_code, render_options, js_evaluator = nil)
|
|
51
51
|
js_evaluator ||= self
|
|
52
52
|
if render_options.trace
|
|
@@ -56,7 +56,11 @@ module ReactOnRails
|
|
|
56
56
|
@file_index += 1
|
|
57
57
|
end
|
|
58
58
|
begin
|
|
59
|
-
|
|
59
|
+
result = if render_options.stream?
|
|
60
|
+
js_evaluator.eval_streaming_js(js_code, render_options)
|
|
61
|
+
else
|
|
62
|
+
js_evaluator.eval_js(js_code, render_options)
|
|
63
|
+
end
|
|
60
64
|
rescue StandardError => err
|
|
61
65
|
msg = <<~MSG
|
|
62
66
|
Error evaluating server bundle. Check your webpack configuration.
|
|
@@ -71,26 +75,14 @@ module ReactOnRails
|
|
|
71
75
|
end
|
|
72
76
|
raise ReactOnRails::Error, msg, err.backtrace
|
|
73
77
|
end
|
|
74
|
-
result = nil
|
|
75
|
-
begin
|
|
76
|
-
result = JSON.parse(json_string)
|
|
77
|
-
rescue JSON::ParserError => e
|
|
78
|
-
raise ReactOnRails::JsonParseError.new(parse_error: e, json: json_string)
|
|
79
|
-
end
|
|
80
78
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
console_script_lines&.each do |line|
|
|
87
|
-
match = re.match(line)
|
|
88
|
-
Rails.logger.info { "[react_on_rails] #{match[:msg]}" } if match
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
result
|
|
79
|
+
return parse_result_and_replay_console_messages(result, render_options) unless render_options.stream?
|
|
80
|
+
|
|
81
|
+
# Streamed component is returned as stream of strings.
|
|
82
|
+
# We need to parse each chunk and replay the console messages.
|
|
83
|
+
result.transform { |chunk| parse_result_and_replay_console_messages(chunk, render_options) }
|
|
92
84
|
end
|
|
93
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
|
85
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
|
94
86
|
|
|
95
87
|
def trace_js_code_used(msg, js_code, file_name = "tmp/server-generated.js", force: false)
|
|
96
88
|
return unless ReactOnRails.configuration.trace || force
|
|
@@ -233,6 +225,28 @@ module ReactOnRails
|
|
|
233
225
|
msg = "file_url_to_string #{url} failed\nError is: #{e}"
|
|
234
226
|
raise ReactOnRails::Error, msg
|
|
235
227
|
end
|
|
228
|
+
|
|
229
|
+
def parse_result_and_replay_console_messages(result_string, render_options)
|
|
230
|
+
result = nil
|
|
231
|
+
begin
|
|
232
|
+
result = JSON.parse(result_string)
|
|
233
|
+
rescue JSON::ParserError => e
|
|
234
|
+
raise ReactOnRails::JsonParseError.new(parse_error: e, json: result_string)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
if render_options.logging_on_server
|
|
238
|
+
console_script = result["consoleReplayScript"]
|
|
239
|
+
console_script_lines = console_script.split("\n")
|
|
240
|
+
# Regular expression to match console.log or console.error calls with SERVER prefix
|
|
241
|
+
re = /console\.(?:log|error)\.apply\(console, \["\[SERVER\] (?<msg>.*)"\]\);/
|
|
242
|
+
console_script_lines&.each do |line|
|
|
243
|
+
match = re.match(line)
|
|
244
|
+
# Log matched messages to Rails logger with react_on_rails prefix
|
|
245
|
+
Rails.logger.info { "[react_on_rails] #{match[:msg]}" } if match
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
result
|
|
249
|
+
end
|
|
236
250
|
end
|
|
237
251
|
# rubocop:enable Metrics/ClassLength
|
|
238
252
|
end
|
|
@@ -19,8 +19,9 @@ module ReactOnRails
|
|
|
19
19
|
# For compatibility, the gem and the node package versions should always match,
|
|
20
20
|
# unless the user really knows what they're doing. So we will give a
|
|
21
21
|
# warning if they do not.
|
|
22
|
-
def
|
|
23
|
-
return if node_package_version.
|
|
22
|
+
def log_if_gem_and_node_package_versions_differ
|
|
23
|
+
return if node_package_version.raw.nil? || node_package_version.local_path_or_url?
|
|
24
|
+
return log_node_semver_version_warning if node_package_version.semver_wildcard?
|
|
24
25
|
|
|
25
26
|
node_major_minor_patch = node_package_version.major_minor_patch
|
|
26
27
|
gem_major_minor_patch = gem_major_minor_patch_version
|
|
@@ -28,9 +29,7 @@ module ReactOnRails
|
|
|
28
29
|
node_major_minor_patch[1] == gem_major_minor_patch[1] &&
|
|
29
30
|
node_major_minor_patch[2] == gem_major_minor_patch[2]
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
raise_node_semver_version_warning if node_package_version.semver_wildcard?
|
|
32
|
+
log_differing_versions_warning unless versions_match
|
|
34
33
|
end
|
|
35
34
|
|
|
36
35
|
private
|
|
@@ -46,15 +45,15 @@ module ReactOnRails
|
|
|
46
45
|
MSG
|
|
47
46
|
end
|
|
48
47
|
|
|
49
|
-
def
|
|
50
|
-
msg = "**
|
|
51
|
-
|
|
48
|
+
def log_differing_versions_warning
|
|
49
|
+
msg = "**WARNING** ReactOnRails: ReactOnRails gem and node package versions do not match\n#{common_error_msg}"
|
|
50
|
+
Rails.logger.warn(msg)
|
|
52
51
|
end
|
|
53
52
|
|
|
54
|
-
def
|
|
55
|
-
msg = "**
|
|
53
|
+
def log_node_semver_version_warning
|
|
54
|
+
msg = "**WARNING** ReactOnRails: Your node package version for react-on-rails contains a " \
|
|
56
55
|
"^ or ~\n#{common_error_msg}"
|
|
57
|
-
|
|
56
|
+
Rails.logger.warn(msg)
|
|
58
57
|
end
|
|
59
58
|
|
|
60
59
|
def gem_version
|
|
@@ -74,7 +73,7 @@ module ReactOnRails
|
|
|
74
73
|
end
|
|
75
74
|
|
|
76
75
|
def self.package_json_path
|
|
77
|
-
Rails.root.join(
|
|
76
|
+
Rails.root.join(ReactOnRails.configuration.node_modules_location, "package.json")
|
|
78
77
|
end
|
|
79
78
|
|
|
80
79
|
def initialize(package_json)
|
|
@@ -82,29 +81,41 @@ module ReactOnRails
|
|
|
82
81
|
end
|
|
83
82
|
|
|
84
83
|
def raw
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
parsed_package_contents
|
|
89
|
-
|
|
90
|
-
|
|
84
|
+
return @raw if defined?(@raw)
|
|
85
|
+
|
|
86
|
+
if File.exist?(package_json)
|
|
87
|
+
parsed_package_contents = JSON.parse(package_json_contents)
|
|
88
|
+
if parsed_package_contents.key?("dependencies") &&
|
|
89
|
+
parsed_package_contents["dependencies"].key?("react-on-rails")
|
|
90
|
+
return @raw = parsed_package_contents["dependencies"]["react-on-rails"]
|
|
91
|
+
end
|
|
91
92
|
end
|
|
93
|
+
msg = "No 'react-on-rails' entry in the dependencies of #{NodePackageVersion.package_json_path}, " \
|
|
94
|
+
"which is the expected location according to ReactOnRails.configuration.node_modules_location"
|
|
95
|
+
Rails.logger.warn(msg)
|
|
96
|
+
@raw = nil
|
|
92
97
|
end
|
|
93
98
|
|
|
94
99
|
def semver_wildcard?
|
|
95
|
-
|
|
100
|
+
# See https://docs.npmjs.com/cli/v10/configuring-npm/package-json#dependencies
|
|
101
|
+
# We want to disallow all expressions other than exact versions
|
|
102
|
+
# and the ones allowed by local_path_or_url?
|
|
103
|
+
raw.blank? || raw.match(/[~^><|*-]/).present?
|
|
96
104
|
end
|
|
97
105
|
|
|
98
|
-
def
|
|
99
|
-
|
|
106
|
+
def local_path_or_url?
|
|
107
|
+
# See https://docs.npmjs.com/cli/v10/configuring-npm/package-json#dependencies
|
|
108
|
+
# All path and protocol "version ranges" include / somewhere,
|
|
109
|
+
# but we want to make an exception for npm:@scope/pkg@version.
|
|
110
|
+
!raw.nil? && raw.include?("/") && !raw.start_with?("npm:")
|
|
100
111
|
end
|
|
101
112
|
|
|
102
113
|
def major_minor_patch
|
|
103
|
-
return if
|
|
114
|
+
return if local_path_or_url?
|
|
104
115
|
|
|
105
116
|
match = raw.match(MAJOR_MINOR_PATCH_VERSION_REGEX)
|
|
106
117
|
unless match
|
|
107
|
-
raise ReactOnRails::Error, "Cannot parse version number '#{raw}' (
|
|
118
|
+
raise ReactOnRails::Error, "Cannot parse version number '#{raw}' (only exact versions are supported)"
|
|
108
119
|
end
|
|
109
120
|
|
|
110
121
|
[match[1], match[2], match[3]]
|
data/tsconfig.json
CHANGED
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: 14.0.
|
|
4
|
+
version: 14.1.0.rc.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Justin Gordon
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2025-01-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: addressable
|