react_on_rails 11.1.4 → 12.0.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/.circleci/config.yml +320 -0
- data/.eslintignore +2 -1
- data/.eslintrc +30 -2
- data/.github/FUNDING.yml +1 -0
- data/.gitignore +3 -1
- data/.prettierignore +10 -0
- data/.prettierrc +23 -0
- data/.release-it.json +3 -0
- data/.rubocop.yml +37 -11
- data/.travis.yml +10 -20
- data/CHANGELOG.md +126 -4
- data/CONTRIBUTING.md +61 -80
- data/Gemfile +3 -5
- data/KUDOS.md +4 -1
- data/REACT-ON-RAILS-PRO-LICENSE +95 -0
- data/README.md +148 -89
- data/Rakefile +0 -7
- data/SUMMARY.md +12 -12
- data/book.json +5 -5
- data/docs/additional-reading/asset-pipeline.md +8 -16
- data/docs/additional-reading/images.md +1 -1
- data/docs/additional-reading/rails_view_rendering_from_inline_javascript.md +2 -1
- data/docs/additional-reading/react-helmet.md +30 -10
- data/docs/additional-reading/react-router.md +52 -75
- data/docs/additional-reading/server-rendering-tips.md +12 -7
- data/docs/additional-reading/upgrade-webpacker-v3-to-v4.md +10 -0
- data/docs/api/javascript-api.md +3 -3
- data/docs/api/redux-store-api.md +4 -2
- data/docs/api/view-helpers-api.md +17 -14
- data/docs/basics/client-vs-server-rendering.md +8 -2
- data/docs/basics/configuration.md +87 -73
- data/docs/basics/deployment.md +2 -4
- data/docs/basics/generator-details.md +1 -1
- data/docs/basics/heroku-deployment.md +24 -0
- data/docs/basics/hmr-and-hot-reloading-with-the-webpack-dev-server.md +49 -0
- data/docs/basics/i18n.md +45 -23
- data/docs/basics/installation-into-an-existing-rails-app.md +4 -9
- data/docs/basics/minitest-configuration.md +31 -0
- data/docs/basics/react-server-rendering.md +3 -1
- data/docs/basics/recommended-project-structure.md +21 -15
- data/docs/basics/{generator-functions-and-railscontext.md → render-functions-and-railscontext.md} +59 -21
- data/docs/basics/rspec-configuration.md +29 -18
- data/docs/basics/upgrading-react-on-rails.md +74 -3
- data/docs/basics/webpack-configuration.md +29 -8
- data/docs/contributor-info/errors-with-hooks.md +45 -0
- data/docs/contributor-info/pull-requests.md +44 -0
- data/docs/misc/doctrine.md +1 -1
- data/docs/{misc-pending → outdated}/code-splitting.md +13 -9
- data/docs/{basics → outdated}/how-react-on-rails-works.md +8 -4
- data/docs/{misc-pending → outdated}/manual-installation-overview.md +5 -5
- data/docs/{additional-reading → outdated}/rails-assets-relative-paths.md +3 -3
- data/docs/{misc-pending → outdated}/rails-assets.md +2 -12
- data/docs/testimonials/hvmn.md +25 -0
- data/docs/testimonials/resortpass.md +13 -0
- data/docs/testimonials/testimonials.md +28 -0
- data/docs/tutorial.md +182 -44
- data/jest.config.js +4 -0
- data/lib/generators/react_on_rails/base_generator.rb +2 -2
- data/lib/generators/react_on_rails/dev_tests_generator.rb +2 -1
- data/lib/generators/react_on_rails/generator_helper.rb +4 -6
- data/lib/generators/react_on_rails/install_generator.rb +2 -0
- data/lib/generators/react_on_rails/templates/base/base/Procfile.dev +3 -1
- data/lib/generators/react_on_rails/templates/base/base/Procfile.dev-hmr +26 -0
- data/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx +20 -40
- data/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb +4 -1
- data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx +4 -8
- data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/store/helloWorldStore.js +1 -3
- data/lib/react_on_rails/configuration.rb +60 -29
- data/lib/react_on_rails/error.rb +2 -0
- data/lib/react_on_rails/git_utils.rb +2 -0
- data/lib/react_on_rails/helper.rb +110 -159
- data/lib/react_on_rails/json_output.rb +1 -1
- data/lib/react_on_rails/json_parse_error.rb +2 -0
- data/lib/react_on_rails/locales/base.rb +150 -0
- data/lib/react_on_rails/locales/to_js.rb +37 -0
- data/lib/react_on_rails/locales/to_json.rb +27 -0
- data/lib/react_on_rails/prerender_error.rb +11 -15
- data/lib/react_on_rails/react_component/render_options.rb +4 -0
- data/lib/react_on_rails/server_rendering_js_code.rb +42 -0
- data/lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb +85 -60
- data/lib/react_on_rails/test_helper/ensure_assets_compiled.rb +7 -8
- data/lib/react_on_rails/test_helper/webpack_assets_compiler.rb +17 -0
- data/lib/react_on_rails/utils.rb +19 -20
- data/lib/react_on_rails/version.rb +1 -1
- data/lib/react_on_rails/version_checker.rb +5 -1
- data/lib/react_on_rails/webpacker_utils.rb +27 -2
- data/lib/react_on_rails.rb +4 -1
- data/lib/tasks/assets.rake +33 -46
- data/lib/tasks/locale.rake +4 -2
- data/package-scripts.yml +49 -0
- data/package.json +41 -31
- data/rakelib/dummy_apps.rake +1 -9
- data/rakelib/example_type.rb +3 -1
- data/rakelib/examples.rake +3 -0
- data/rakelib/lint.rake +2 -7
- data/rakelib/node_package.rake +2 -2
- data/rakelib/release.rake +3 -8
- data/rakelib/run_rspec.rake +5 -18
- data/react_on_rails.gemspec +4 -5
- data/tsconfig.json +14 -0
- data/webpackConfigLoader.js +5 -4
- data/yarn.lock +7177 -2311
- metadata +54 -55
- data/Gemfile.rails32 +0 -74
- data/docs/additional-reading/babel.md +0 -5
- data/docs/additional-reading/heroku-deployment.md +0 -92
- data/docs/additional-reading/hot-reloading-rails-development.md +0 -57
- data/docs/api/ruby-api-hot-reload-view-helpers.md +0 -44
- data/docs/testimonials.md +0 -11
- data/lib/generators/react_on_rails/templates/base/base/Procfile.dev-server +0 -12
- data/lib/react_on_rails/assets_precompile.rb +0 -150
- data/lib/react_on_rails/locales_to_js.rb +0 -134
- /data/docs/{misc → outdated}/rails3.md +0 -0
data/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
ReactOnRails.configure do |config|
|
|
7
7
|
# This configures the script to run to build the production assets by webpack. Set this to nil
|
|
8
8
|
# if you don't want react_on_rails building this file for you.
|
|
9
|
-
|
|
9
|
+
# If nil, then the standard rails/webpacker assets:precompile will run
|
|
10
|
+
# config.build_production_command = nil
|
|
10
11
|
|
|
11
12
|
################################################################################
|
|
12
13
|
################################################################################
|
|
@@ -22,6 +23,8 @@ ReactOnRails.configure do |config|
|
|
|
22
23
|
# with rspec then this controls what yarn command is run
|
|
23
24
|
# to automatically refresh your webpack assets on every test run.
|
|
24
25
|
#
|
|
26
|
+
# Alternately, you can remove the `ReactOnRails::TestHelper.configure_rspec_to_compile_assets`
|
|
27
|
+
# and set the config/webpacker.yml option for test to true.
|
|
25
28
|
config.build_test_command = "RAILS_ENV=test bin/webpack"
|
|
26
29
|
|
|
27
30
|
################################################################################
|
|
@@ -4,19 +4,15 @@ import React from 'react';
|
|
|
4
4
|
const HelloWorld = ({ name, updateName }) => (
|
|
5
5
|
<div>
|
|
6
6
|
<h3>
|
|
7
|
-
Hello,
|
|
7
|
+
Hello,
|
|
8
|
+
{name}!
|
|
8
9
|
</h3>
|
|
9
10
|
<hr />
|
|
10
|
-
<form
|
|
11
|
+
<form>
|
|
11
12
|
<label htmlFor="name">
|
|
12
13
|
Say hello to:
|
|
14
|
+
<input id="name" type="text" value={name} onChange={(e) => updateName(e.target.value)} />
|
|
13
15
|
</label>
|
|
14
|
-
<input
|
|
15
|
-
id="name"
|
|
16
|
-
type="text"
|
|
17
|
-
value={name}
|
|
18
|
-
onChange={(e) => updateName(e.target.value)}
|
|
19
|
-
/>
|
|
20
16
|
</form>
|
|
21
17
|
</div>
|
|
22
18
|
);
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { createStore } from 'redux';
|
|
2
2
|
import helloWorldReducer from '../reducers/helloWorldReducer';
|
|
3
3
|
|
|
4
|
-
const configureStore = (railsProps) => (
|
|
5
|
-
createStore(helloWorldReducer, railsProps)
|
|
6
|
-
);
|
|
4
|
+
const configureStore = (railsProps) => createStore(helloWorldReducer, railsProps);
|
|
7
5
|
|
|
8
6
|
export default configureStore;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# rubocop:disable Metrics/ClassLength
|
|
4
|
+
|
|
3
5
|
module ReactOnRails
|
|
4
6
|
def self.configure
|
|
5
7
|
yield(configuration)
|
|
@@ -21,7 +23,7 @@ module ReactOnRails
|
|
|
21
23
|
prerender: false,
|
|
22
24
|
replay_console: true,
|
|
23
25
|
logging_on_server: true,
|
|
24
|
-
raise_on_prerender_error:
|
|
26
|
+
raise_on_prerender_error: Rails.env.development?,
|
|
25
27
|
trace: Rails.env.development?,
|
|
26
28
|
development_mode: Rails.env.development?,
|
|
27
29
|
server_renderer_pool_size: DEFAULT_POOL_SIZE,
|
|
@@ -31,10 +33,11 @@ module ReactOnRails
|
|
|
31
33
|
webpack_generated_files: %w[manifest.json],
|
|
32
34
|
rendering_extension: nil,
|
|
33
35
|
server_render_method: nil,
|
|
34
|
-
symlink_non_digested_assets_regex: nil,
|
|
35
36
|
build_test_command: "",
|
|
36
37
|
build_production_command: "",
|
|
37
|
-
random_dom_id: DEFAULT_RANDOM_DOM_ID
|
|
38
|
+
random_dom_id: DEFAULT_RANDOM_DOM_ID,
|
|
39
|
+
same_bundle_for_client_and_server: false,
|
|
40
|
+
i18n_output_format: nil
|
|
38
41
|
)
|
|
39
42
|
end
|
|
40
43
|
|
|
@@ -46,8 +49,9 @@ module ReactOnRails
|
|
|
46
49
|
:generated_assets_dirs, :generated_assets_dir,
|
|
47
50
|
:webpack_generated_files, :rendering_extension, :build_test_command,
|
|
48
51
|
:build_production_command,
|
|
49
|
-
:i18n_dir, :i18n_yml_dir,
|
|
50
|
-
:server_render_method, :
|
|
52
|
+
:i18n_dir, :i18n_yml_dir, :i18n_output_format,
|
|
53
|
+
:server_render_method, :random_dom_id,
|
|
54
|
+
:same_bundle_for_client_and_server
|
|
51
55
|
|
|
52
56
|
def initialize(node_modules_location: nil, server_bundle_js_file: nil, prerender: nil,
|
|
53
57
|
replay_console: nil,
|
|
@@ -58,16 +62,17 @@ module ReactOnRails
|
|
|
58
62
|
generated_assets_dir: nil, webpack_generated_files: nil,
|
|
59
63
|
rendering_extension: nil, build_test_command: nil,
|
|
60
64
|
build_production_command: nil,
|
|
61
|
-
|
|
62
|
-
|
|
65
|
+
same_bundle_for_client_and_server: nil,
|
|
66
|
+
i18n_dir: nil, i18n_yml_dir: nil, i18n_output_format: nil,
|
|
67
|
+
random_dom_id: nil, server_render_method: nil)
|
|
63
68
|
self.node_modules_location = node_modules_location.present? ? node_modules_location : Rails.root
|
|
64
|
-
self.server_bundle_js_file = server_bundle_js_file
|
|
65
69
|
self.generated_assets_dirs = generated_assets_dirs
|
|
66
70
|
self.generated_assets_dir = generated_assets_dir
|
|
67
71
|
self.build_test_command = build_test_command
|
|
68
72
|
self.build_production_command = build_production_command
|
|
69
73
|
self.i18n_dir = i18n_dir
|
|
70
74
|
self.i18n_yml_dir = i18n_yml_dir
|
|
75
|
+
self.i18n_output_format = i18n_output_format
|
|
71
76
|
|
|
72
77
|
self.random_dom_id = random_dom_id
|
|
73
78
|
self.prerender = prerender
|
|
@@ -83,6 +88,8 @@ module ReactOnRails
|
|
|
83
88
|
self.skip_display_none = skip_display_none
|
|
84
89
|
|
|
85
90
|
# Server rendering:
|
|
91
|
+
self.server_bundle_js_file = server_bundle_js_file
|
|
92
|
+
self.same_bundle_for_client_and_server = same_bundle_for_client_and_server
|
|
86
93
|
self.server_renderer_pool_size = self.development_mode ? 1 : server_renderer_pool_size
|
|
87
94
|
self.server_renderer_timeout = server_renderer_timeout # seconds
|
|
88
95
|
|
|
@@ -90,7 +97,6 @@ module ReactOnRails
|
|
|
90
97
|
self.rendering_extension = rendering_extension
|
|
91
98
|
|
|
92
99
|
self.server_render_method = server_render_method
|
|
93
|
-
self.symlink_non_digested_assets_regex = symlink_non_digested_assets_regex
|
|
94
100
|
end
|
|
95
101
|
|
|
96
102
|
# on ReactOnRails
|
|
@@ -99,15 +105,50 @@ module ReactOnRails
|
|
|
99
105
|
configure_generated_assets_dirs_deprecation
|
|
100
106
|
configure_skip_display_none_deprecation
|
|
101
107
|
ensure_generated_assets_dir_present
|
|
102
|
-
ensure_server_bundle_js_file_has_no_path
|
|
103
108
|
check_i18n_directory_exists
|
|
104
109
|
check_i18n_yml_directory_exists
|
|
105
110
|
check_server_render_method_is_only_execjs
|
|
106
111
|
error_if_using_webpacker_and_generated_assets_dir_not_match_public_output_path
|
|
112
|
+
check_deprecated_settings
|
|
107
113
|
end
|
|
108
114
|
|
|
109
115
|
private
|
|
110
116
|
|
|
117
|
+
def check_deprecated_settings
|
|
118
|
+
if build_production_command.present? &&
|
|
119
|
+
ReactOnRails::WebpackerUtils.webpacker_webpack_production_config_exists?
|
|
120
|
+
msg = <<~MSG
|
|
121
|
+
Setting ReactOnRails configuration for `build_production_command` is
|
|
122
|
+
not necessary if you have config/webpack/production.js. When that file
|
|
123
|
+
exists, React on Rails DOES NOT modify the standard assets:precompile.
|
|
124
|
+
If you want React on Rails to modify to the standard assets:precompile
|
|
125
|
+
to use your config/initializers/react_on_rails.rb config.build_production_command
|
|
126
|
+
then delete the config/webpack/production.js.
|
|
127
|
+
MSG
|
|
128
|
+
Rails.logger.warn(msg)
|
|
129
|
+
end
|
|
130
|
+
#
|
|
131
|
+
# msg = <<~MSG
|
|
132
|
+
# ReactOnRails configuration for `build_production_command` is removed.
|
|
133
|
+
# Move this command into `bin/webpack` converting the script to a shell script.
|
|
134
|
+
# MSG
|
|
135
|
+
# raise ReactOnRails::Error, msg
|
|
136
|
+
# Commenting out until v13 when
|
|
137
|
+
# https://github.com/rails/webpacker/issues/2640 gets resolved
|
|
138
|
+
# if node_modules_location.present?
|
|
139
|
+
# Rails.logger.warn("ReactOnRails configuration for `node_modules_location` is deprecated. "\
|
|
140
|
+
# "Instead, prepend a `cd client` (or whichever location) before your test command.")
|
|
141
|
+
# end
|
|
142
|
+
#
|
|
143
|
+
# return unless build_production_command.present?
|
|
144
|
+
#
|
|
145
|
+
# msg = <<~MSG
|
|
146
|
+
# ReactOnRails configuration for `build_production_command` is removed.
|
|
147
|
+
# Move this command into `bin/webpack` converting the script to a shell script.
|
|
148
|
+
# MSG
|
|
149
|
+
# raise ReactOnRails::Error, msg
|
|
150
|
+
end
|
|
151
|
+
|
|
111
152
|
def error_if_using_webpacker_and_generated_assets_dir_not_match_public_output_path
|
|
112
153
|
return unless ReactOnRails::WebpackerUtils.using_webpacker?
|
|
113
154
|
|
|
@@ -119,12 +160,12 @@ module ReactOnRails
|
|
|
119
160
|
Rails.logger.warn("You specified generated_assets_dir in `config/initializers/react_on_rails.rb` "\
|
|
120
161
|
"with Webpacker. Remove this line from your configuration file.")
|
|
121
162
|
else
|
|
122
|
-
msg =
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
163
|
+
msg = <<~MSG
|
|
164
|
+
Error configuring /config/initializers/react_on_rails.rb: You are using webpacker
|
|
165
|
+
and your specified value for generated_assets_dir = #{generated_assets_dir}
|
|
166
|
+
that does not match the value for public_output_path specified in
|
|
167
|
+
webpacker.yml = #{webpacker_public_output_path}. You should remove the configuration
|
|
168
|
+
value for "generated_assets_dir" from your config/initializers/react_on_rails.rb file.
|
|
128
169
|
MSG
|
|
129
170
|
raise ReactOnRails::Error, msg
|
|
130
171
|
end
|
|
@@ -198,27 +239,17 @@ module ReactOnRails
|
|
|
198
239
|
def ensure_webpack_generated_files_exists
|
|
199
240
|
return unless webpack_generated_files.empty?
|
|
200
241
|
|
|
201
|
-
files = ["
|
|
242
|
+
files = ["manifest.json"]
|
|
202
243
|
files << server_bundle_js_file if server_bundle_js_file.present?
|
|
203
244
|
|
|
204
245
|
self.webpack_generated_files = files
|
|
205
246
|
end
|
|
206
247
|
|
|
207
|
-
def ensure_server_bundle_js_file_has_no_path
|
|
208
|
-
return unless server_bundle_js_file.include?(File::SEPARATOR)
|
|
209
|
-
|
|
210
|
-
assets_dir = ReactOnRails::Utils.generated_assets_full_path
|
|
211
|
-
self.server_bundle_js_file = File.basename(server_bundle_js_file)
|
|
212
|
-
|
|
213
|
-
Rails.logger.warn do
|
|
214
|
-
"[DEPRECATION] ReactOnRails: remove path from server_bundle_js_file in configuration. "\
|
|
215
|
-
"All generated files must go in #{assets_dir}. Using file basename #{server_bundle_js_file}"
|
|
216
|
-
end
|
|
217
|
-
end
|
|
218
|
-
|
|
219
248
|
def configure_skip_display_none_deprecation
|
|
220
249
|
return if skip_display_none.nil?
|
|
250
|
+
|
|
221
251
|
Rails.logger.warn "[DEPRECATION] ReactOnRails: remove skip_display_none from configuration."
|
|
222
252
|
end
|
|
223
253
|
end
|
|
224
254
|
end
|
|
255
|
+
# rubocop:enable Metrics/ClassLength
|
data/lib/react_on_rails/error.rb
CHANGED
|
@@ -6,8 +6,10 @@ module ReactOnRails
|
|
|
6
6
|
module GitUtils
|
|
7
7
|
def self.uncommitted_changes?(message_handler)
|
|
8
8
|
return false if ENV["COVERAGE"] == "true"
|
|
9
|
+
|
|
9
10
|
status = `git status --porcelain`
|
|
10
11
|
return false if $CHILD_STATUS.success? && status.empty?
|
|
12
|
+
|
|
11
13
|
error = if !$CHILD_STATUS.success?
|
|
12
14
|
"You do not have Git installed. Please install Git, and commit your changes before continuing"
|
|
13
15
|
else
|
|
@@ -15,68 +15,20 @@ module ReactOnRails
|
|
|
15
15
|
module Helper
|
|
16
16
|
include ReactOnRails::Utils::Required
|
|
17
17
|
|
|
18
|
-
COMPONENT_HTML_KEY = "componentHtml"
|
|
18
|
+
COMPONENT_HTML_KEY = "componentHtml"
|
|
19
19
|
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
20
|
+
# react_component_name: can be a React function or class component or a "render function".
|
|
21
|
+
# "render functions" differ from a React function in that they take two parameters, the
|
|
22
|
+
# props and the railsContext, like this:
|
|
23
23
|
#
|
|
24
|
-
#
|
|
25
|
-
# these params are optional, and support either a single value, or an array.
|
|
24
|
+
# let MyReactComponentApp = (props, railsContext) => <MyReactComponent {...props}/>;
|
|
26
25
|
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
26
|
+
# Alternately, you can define the render function with an additional property
|
|
27
|
+
# `.renderFunction = true`:
|
|
29
28
|
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
# media: 'all',
|
|
33
|
-
# 'data-turbolinks-track' => "reload") %>
|
|
29
|
+
# let MyReactComponentApp = (props) => <MyReactComponent {...props}/>;
|
|
30
|
+
# MyReactComponent.renderFunction = true;
|
|
34
31
|
#
|
|
35
|
-
# <!-- These do not use turbolinks, so no data-turbolinks-track -->
|
|
36
|
-
# <!-- This is to load the hot assets. -->
|
|
37
|
-
# <%= env_javascript_include_tag(hot: ['http://localhost:3500/vendor-bundle.js',
|
|
38
|
-
# 'http://localhost:3500/app-bundle.js']) %>
|
|
39
|
-
#
|
|
40
|
-
# <!-- These do use turbolinks -->
|
|
41
|
-
# <%= env_javascript_include_tag(static: 'application_static',
|
|
42
|
-
# hot: 'application_non_webpack',
|
|
43
|
-
# 'data-turbolinks-track' => "reload") %>
|
|
44
|
-
#
|
|
45
|
-
# NOTE: for Turbolinks 2.x, use 'data-turbolinks-track' => true
|
|
46
|
-
# See application.html.erb for usage example
|
|
47
|
-
# https://github.com/shakacode/react-webpack-rails-tutorial/blob/master/app%2Fviews%2Flayouts%2Fapplication.html.erb
|
|
48
|
-
def env_javascript_include_tag(args = {})
|
|
49
|
-
send_tag_method(:javascript_include_tag, args)
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# Helper to set CSS assets depending on if we want static or "hot", which means from the
|
|
53
|
-
# Webpack dev server.
|
|
54
|
-
#
|
|
55
|
-
# In this example, application_non_webpack is simply a CSS asset pipeline file which includes
|
|
56
|
-
# styles not placed in the webpack build.
|
|
57
|
-
#
|
|
58
|
-
# We don't need styles from the webpack build, as those will come via the JavaScript include
|
|
59
|
-
# tags.
|
|
60
|
-
#
|
|
61
|
-
# The key options are `static` and `hot` which specify what you want for static vs. hot. Both of
|
|
62
|
-
# these params are optional, and support either a single value, or an array.
|
|
63
|
-
#
|
|
64
|
-
# <%= env_stylesheet_link_tag(static: 'application_static',
|
|
65
|
-
# hot: 'application_non_webpack',
|
|
66
|
-
# media: 'all',
|
|
67
|
-
# 'data-turbolinks-track' => true) %>
|
|
68
|
-
#
|
|
69
|
-
def env_stylesheet_link_tag(args = {})
|
|
70
|
-
send_tag_method(:stylesheet_link_tag, args)
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
# react_component_name: can be a React component, created using a ES6 class, or
|
|
74
|
-
# React.createClass, or a
|
|
75
|
-
# `generator function` that returns a React component
|
|
76
|
-
# using ES6
|
|
77
|
-
# let MyReactComponentApp = (props, railsContext) => <MyReactComponent {...props}/>;
|
|
78
|
-
# or using ES5
|
|
79
|
-
# var MyReactComponentApp = function(props, railsContext) { return <YourReactComponent {...props}/>; }
|
|
80
32
|
# Exposing the react_component_name is necessary to both a plain ReactComponent as well as
|
|
81
33
|
# a generator:
|
|
82
34
|
# See README.md for how to "register" your react components.
|
|
@@ -98,7 +50,8 @@ module ReactOnRails
|
|
|
98
50
|
# raise_on_prerender_error: <true/false> Default to false. True will raise exception on server
|
|
99
51
|
# if the JS code throws
|
|
100
52
|
# Any other options are passed to the content tag, including the id.
|
|
101
|
-
# random_dom_id can be set to override the
|
|
53
|
+
# random_dom_id can be set to override the default from the config/initializers. That's only
|
|
54
|
+
# used if you have multiple instance of the same component on the Rails view.
|
|
102
55
|
def react_component(component_name, options = {})
|
|
103
56
|
internal_result = internal_react_component(component_name, options)
|
|
104
57
|
server_rendered_html = internal_result[:result]["html"]
|
|
@@ -112,20 +65,24 @@ module ReactOnRails
|
|
|
112
65
|
render_options: internal_result[:render_options]
|
|
113
66
|
)
|
|
114
67
|
elsif server_rendered_html.is_a?(Hash)
|
|
115
|
-
msg =
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
68
|
+
msg = <<~MSG
|
|
69
|
+
Use react_component_hash (not react_component) to return a Hash to your ruby view code. See
|
|
70
|
+
https://github.com/shakacode/react_on_rails/blob/master/spec/dummy/client/app/startup/ReactHelmetServerApp.jsx
|
|
71
|
+
for an example of the necessary javascript configuration.
|
|
119
72
|
MSG
|
|
120
73
|
raise ReactOnRails::Error, msg
|
|
121
|
-
|
|
122
74
|
else
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
75
|
+
class_name = server_rendered_html.class.name
|
|
76
|
+
msg = <<~MSG
|
|
77
|
+
ReactOnRails: server_rendered_html is expected to be a String or Hash for #{component_name}.
|
|
78
|
+
Type is #{class_name}
|
|
79
|
+
Value:
|
|
80
|
+
#{server_rendered_html}
|
|
81
|
+
|
|
82
|
+
If you're trying to use a render function to return a Hash to your ruby view code, then use
|
|
83
|
+
react_component_hash instead of react_component and see
|
|
84
|
+
https://github.com/shakacode/react_on_rails/blob/master/spec/dummy/client/app/startup/ReactHelmetServerApp.jsx
|
|
85
|
+
for an example of the JavaScript code.
|
|
129
86
|
MSG
|
|
130
87
|
raise ReactOnRails::Error, msg
|
|
131
88
|
end
|
|
@@ -136,7 +93,7 @@ module ReactOnRails
|
|
|
136
93
|
# It is exactly like react_component except for the following:
|
|
137
94
|
# 1. prerender: true is automatically added, as this method doesn't make sense for client only
|
|
138
95
|
# rendering.
|
|
139
|
-
# 2. Your JavaScript for server rendering must return an Object
|
|
96
|
+
# 2. Your JavaScript render function for server rendering must return an Object rather than a React component.
|
|
140
97
|
# 3. Your view code must expect an object and not a string.
|
|
141
98
|
#
|
|
142
99
|
# Here is an example of the view code:
|
|
@@ -166,10 +123,12 @@ module ReactOnRails
|
|
|
166
123
|
render_options: internal_result[:render_options]
|
|
167
124
|
)
|
|
168
125
|
else
|
|
169
|
-
msg =
|
|
170
|
-
|
|
126
|
+
msg = <<~MSG
|
|
127
|
+
render function used by react_component_hash for #{component_name} is expected to return
|
|
171
128
|
an Object. See https://github.com/shakacode/react_on_rails/blob/master/spec/dummy/client/app/startup/ReactHelmetServerApp.jsx
|
|
172
|
-
for an example of the JavaScript code.
|
|
129
|
+
for an example of the JavaScript code.
|
|
130
|
+
Note, your render function must either take 2 params or have the property
|
|
131
|
+
`.renderFunction = true` added to it to distinguish it from a React Function Component.
|
|
173
132
|
MSG
|
|
174
133
|
raise ReactOnRails::Error, msg
|
|
175
134
|
end
|
|
@@ -178,6 +137,9 @@ module ReactOnRails
|
|
|
178
137
|
# Separate initialization of store from react_component allows multiple react_component calls to
|
|
179
138
|
# use the same Redux store.
|
|
180
139
|
#
|
|
140
|
+
# NOTE: This technique not recommended as it prevents dynamic code splitting for performance.
|
|
141
|
+
# Instead, you should use the standard react_component view helper.
|
|
142
|
+
#
|
|
181
143
|
# store_name: name of the store, corresponding to your call to ReactOnRails.registerStores in your
|
|
182
144
|
# JavaScript code.
|
|
183
145
|
# props: Ruby Hash or JSON string which contains the properties to pass to the redux store.
|
|
@@ -206,6 +168,7 @@ module ReactOnRails
|
|
|
206
168
|
# that contains a data props.
|
|
207
169
|
def redux_store_hydration_data
|
|
208
170
|
return if @registered_stores_defer_render.blank?
|
|
171
|
+
|
|
209
172
|
@registered_stores_defer_render.reduce("".dup) do |accum, redux_store_data|
|
|
210
173
|
accum << render_redux_store_data(redux_store_data)
|
|
211
174
|
end.html_safe
|
|
@@ -265,6 +228,7 @@ module ReactOnRails
|
|
|
265
228
|
|
|
266
229
|
def json_safe_and_pretty(hash_or_string)
|
|
267
230
|
return "{}" if hash_or_string.nil?
|
|
231
|
+
|
|
268
232
|
unless hash_or_string.is_a?(String) || hash_or_string.is_a?(Hash)
|
|
269
233
|
raise ReactOnRails::Error, "#{__method__} only accepts String or Hash as argument "\
|
|
270
234
|
"(#{hash_or_string.class} given)."
|
|
@@ -275,6 +239,60 @@ module ReactOnRails
|
|
|
275
239
|
ReactOnRails::JsonOutput.escape(json_value)
|
|
276
240
|
end
|
|
277
241
|
|
|
242
|
+
# This is the definitive list of the default values used for the rails_context, which is the
|
|
243
|
+
# second parameter passed to both component and store render functions.
|
|
244
|
+
# This method can be called from views and from the controller, as `helpers.rails_context`
|
|
245
|
+
#
|
|
246
|
+
# rubocop:disable Metrics/AbcSize
|
|
247
|
+
def rails_context(server_side: true)
|
|
248
|
+
# ALERT: Keep in sync with node_package/src/types/index.ts for the properties of RailsContext
|
|
249
|
+
@rails_context ||= begin
|
|
250
|
+
result = {
|
|
251
|
+
railsEnv: Rails.env,
|
|
252
|
+
inMailer: in_mailer?,
|
|
253
|
+
# Locale settings
|
|
254
|
+
i18nLocale: I18n.locale,
|
|
255
|
+
i18nDefaultLocale: I18n.default_locale,
|
|
256
|
+
rorVersion: ReactOnRails::VERSION,
|
|
257
|
+
rorPro: ReactOnRails::Utils.react_on_rails_pro?
|
|
258
|
+
}
|
|
259
|
+
if defined?(request) && request.present?
|
|
260
|
+
# Check for encoding of the request's original_url and try to force-encoding the
|
|
261
|
+
# URLs as UTF-8. This situation can occur in browsers that do not encode the
|
|
262
|
+
# entire URL as UTF-8 already, mostly on the Windows platform (IE11 and lower).
|
|
263
|
+
original_url_normalized = request.original_url
|
|
264
|
+
if original_url_normalized.encoding.to_s == "ASCII-8BIT"
|
|
265
|
+
original_url_normalized = original_url_normalized.force_encoding("ISO-8859-1").encode("UTF-8")
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# Using Addressable instead of standard URI to better deal with
|
|
269
|
+
# non-ASCII characters (see https://github.com/shakacode/react_on_rails/pull/405)
|
|
270
|
+
uri = Addressable::URI.parse(original_url_normalized)
|
|
271
|
+
# uri = Addressable::URI.parse("http://foo.com:3000/posts?id=30&limit=5#time=1305298413")
|
|
272
|
+
|
|
273
|
+
result.merge!(
|
|
274
|
+
# URL settings
|
|
275
|
+
href: uri.to_s,
|
|
276
|
+
location: "#{uri.path}#{uri.query.present? ? "?#{uri.query}" : ''}",
|
|
277
|
+
scheme: uri.scheme, # http
|
|
278
|
+
host: uri.host, # foo.com
|
|
279
|
+
port: uri.port,
|
|
280
|
+
pathname: uri.path, # /posts
|
|
281
|
+
search: uri.query, # id=30&limit=5
|
|
282
|
+
httpAcceptLanguage: request.env["HTTP_ACCEPT_LANGUAGE"]
|
|
283
|
+
)
|
|
284
|
+
end
|
|
285
|
+
if ReactOnRails.configuration.rendering_extension
|
|
286
|
+
custom_context = ReactOnRails.configuration.rendering_extension.custom_context(self)
|
|
287
|
+
result.merge!(custom_context) if custom_context
|
|
288
|
+
end
|
|
289
|
+
result
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
@rails_context.merge(serverSide: server_side)
|
|
293
|
+
end
|
|
294
|
+
# rubocop:enable Metrics/AbcSize
|
|
295
|
+
|
|
278
296
|
private
|
|
279
297
|
|
|
280
298
|
def build_react_component_result_for_server_rendered_string(
|
|
@@ -284,9 +302,15 @@ module ReactOnRails
|
|
|
284
302
|
render_options: required("render_options")
|
|
285
303
|
)
|
|
286
304
|
content_tag_options = render_options.html_options
|
|
305
|
+
if content_tag_options.key?(:tag)
|
|
306
|
+
content_tag_options_html_tag = content_tag_options[:tag]
|
|
307
|
+
content_tag_options.delete(:tag)
|
|
308
|
+
else
|
|
309
|
+
content_tag_options_html_tag = "div"
|
|
310
|
+
end
|
|
287
311
|
content_tag_options[:id] = render_options.dom_id
|
|
288
312
|
|
|
289
|
-
rendered_output = content_tag(
|
|
313
|
+
rendered_output = content_tag(content_tag_options_html_tag.to_sym,
|
|
290
314
|
server_rendered_html.html_safe,
|
|
291
315
|
content_tag_options)
|
|
292
316
|
|
|
@@ -334,13 +358,11 @@ module ReactOnRails
|
|
|
334
358
|
|
|
335
359
|
def compose_react_component_html_with_spec_and_console(component_specification_tag, rendered_output, console_script)
|
|
336
360
|
# IMPORTANT: Ensure that we mark string as html_safe to avoid escaping.
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
#{
|
|
340
|
-
|
|
341
|
-
#{console_script}
|
|
361
|
+
<<~HTML.html_safe
|
|
362
|
+
#{rendered_output}
|
|
363
|
+
#{component_specification_tag}
|
|
364
|
+
#{console_script}
|
|
342
365
|
HTML
|
|
343
|
-
# rubocop:enable Layout/IndentHeredoc
|
|
344
366
|
end
|
|
345
367
|
|
|
346
368
|
# prepend the rails_context if not yet applied
|
|
@@ -431,22 +453,13 @@ module ReactOnRails
|
|
|
431
453
|
#
|
|
432
454
|
# Read more here: http://timelessrepo.com/json-isnt-a-javascript-subset
|
|
433
455
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
(
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
name: '#{react_component_name}',
|
|
442
|
-
domNodeId: '#{render_options.dom_id}',
|
|
443
|
-
props: props,
|
|
444
|
-
trace: #{render_options.trace},
|
|
445
|
-
railsContext: railsContext
|
|
446
|
-
});
|
|
447
|
-
})()
|
|
448
|
-
JS
|
|
449
|
-
# rubocop:enable Layout/IndentHeredoc
|
|
456
|
+
js_code = ReactOnRails::ServerRenderingJsCode.server_rendering_component_js_code(
|
|
457
|
+
props_string: props_string(props).gsub("\u2028", '\u2028').gsub("\u2029", '\u2029'),
|
|
458
|
+
rails_context: rails_context(server_side: true).to_json,
|
|
459
|
+
redux_stores: initialize_redux_stores,
|
|
460
|
+
react_component_name: react_component_name,
|
|
461
|
+
render_options: render_options
|
|
462
|
+
)
|
|
450
463
|
|
|
451
464
|
begin
|
|
452
465
|
result = ReactOnRails::ServerRenderingPool.server_render_js_with_console_logging(js_code, render_options)
|
|
@@ -478,6 +491,7 @@ module ReactOnRails
|
|
|
478
491
|
JS
|
|
479
492
|
|
|
480
493
|
return result unless @registered_stores.present? || @registered_stores_defer_render.present?
|
|
494
|
+
|
|
481
495
|
declarations = "var reduxProps, store, storeGenerator;\n".dup
|
|
482
496
|
all_stores = (@registered_stores || []) + (@registered_stores_defer_render || [])
|
|
483
497
|
|
|
@@ -494,73 +508,10 @@ module ReactOnRails
|
|
|
494
508
|
result
|
|
495
509
|
end
|
|
496
510
|
|
|
497
|
-
# This is the definitive list of the default values used for the rails_context, which is the
|
|
498
|
-
# second parameter passed to both component and store generator functions.
|
|
499
|
-
# rubocop:disable Metrics/AbcSize
|
|
500
|
-
def rails_context(server_side: required("server_side"))
|
|
501
|
-
@rails_context ||= begin
|
|
502
|
-
result = {
|
|
503
|
-
railsEnv: Rails.env,
|
|
504
|
-
inMailer: in_mailer?,
|
|
505
|
-
# Locale settings
|
|
506
|
-
i18nLocale: I18n.locale,
|
|
507
|
-
i18nDefaultLocale: I18n.default_locale,
|
|
508
|
-
rorVersion: ReactOnRails::VERSION,
|
|
509
|
-
rorPro: ReactOnRails::Utils.react_on_rails_pro?
|
|
510
|
-
}
|
|
511
|
-
if defined?(request) && request.present?
|
|
512
|
-
# Check for encoding of the request's original_url and try to force-encoding the
|
|
513
|
-
# URLs as UTF-8. This situation can occur in browsers that do not encode the
|
|
514
|
-
# entire URL as UTF-8 already, mostly on the Windows platform (IE11 and lower).
|
|
515
|
-
original_url_normalized = request.original_url
|
|
516
|
-
if original_url_normalized.encoding.to_s == "ASCII-8BIT"
|
|
517
|
-
original_url_normalized = original_url_normalized.force_encoding("ISO-8859-1").encode("UTF-8")
|
|
518
|
-
end
|
|
519
|
-
|
|
520
|
-
# Using Addressable instead of standard URI to better deal with
|
|
521
|
-
# non-ASCII characters (see https://github.com/shakacode/react_on_rails/pull/405)
|
|
522
|
-
uri = Addressable::URI.parse(original_url_normalized)
|
|
523
|
-
# uri = Addressable::URI.parse("http://foo.com:3000/posts?id=30&limit=5#time=1305298413")
|
|
524
|
-
|
|
525
|
-
result.merge!(
|
|
526
|
-
# URL settings
|
|
527
|
-
href: uri.to_s,
|
|
528
|
-
location: "#{uri.path}#{uri.query.present? ? "?#{uri.query}" : ''}",
|
|
529
|
-
scheme: uri.scheme, # http
|
|
530
|
-
host: uri.host, # foo.com
|
|
531
|
-
port: uri.port,
|
|
532
|
-
pathname: uri.path, # /posts
|
|
533
|
-
search: uri.query, # id=30&limit=5
|
|
534
|
-
httpAcceptLanguage: request.env["HTTP_ACCEPT_LANGUAGE"]
|
|
535
|
-
)
|
|
536
|
-
end
|
|
537
|
-
if ReactOnRails.configuration.rendering_extension
|
|
538
|
-
custom_context = ReactOnRails.configuration.rendering_extension.custom_context(self)
|
|
539
|
-
result.merge!(custom_context) if custom_context
|
|
540
|
-
end
|
|
541
|
-
result
|
|
542
|
-
end
|
|
543
|
-
|
|
544
|
-
@rails_context.merge(serverSide: server_side)
|
|
545
|
-
end
|
|
546
|
-
|
|
547
|
-
# rubocop:enable Metrics/AbcSize
|
|
548
|
-
|
|
549
511
|
def replay_console_option(val)
|
|
550
512
|
val.nil? ? ReactOnRails.configuration.replay_console : val
|
|
551
513
|
end
|
|
552
514
|
|
|
553
|
-
def use_hot_reloading?
|
|
554
|
-
ENV["REACT_ON_RAILS_ENV"] == "HOT"
|
|
555
|
-
end
|
|
556
|
-
|
|
557
|
-
def send_tag_method(tag_method_name, args)
|
|
558
|
-
asset_type = use_hot_reloading? ? :hot : :static
|
|
559
|
-
assets = Array(args[asset_type])
|
|
560
|
-
options = args.delete_if { |key, _value| %i[hot static].include?(key) }
|
|
561
|
-
send(tag_method_name, *assets, options) unless assets.empty?
|
|
562
|
-
end
|
|
563
|
-
|
|
564
515
|
def in_mailer?
|
|
565
516
|
return false unless defined?(controller)
|
|
566
517
|
return false unless defined?(ActionMailer::Base)
|
|
@@ -11,7 +11,7 @@ module ReactOnRails
|
|
|
11
11
|
"\u2028" => '\u2028',
|
|
12
12
|
"\u2029" => '\u2029'
|
|
13
13
|
}.freeze
|
|
14
|
-
ESCAPE_REGEXP = /[\u2028\u2029&><]/u
|
|
14
|
+
ESCAPE_REGEXP = /[\u2028\u2029&><]/u.freeze
|
|
15
15
|
|
|
16
16
|
def self.escape(json)
|
|
17
17
|
return escape_without_erb_util(json) if Utils.rails_version_less_than_4_1_1
|