react_on_rails 11.3.0 → 12.0.0.pre.beta.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 +23 -1
- data/.github/FUNDING.yml +1 -0
- data/.gitignore +3 -1
- data/.prettierignore +10 -1
- data/.prettierrc +3 -0
- data/.rubocop.yml +37 -11
- data/.travis.yml +9 -22
- data/CHANGELOG.md +46 -4
- data/CONTRIBUTING.md +60 -71
- data/Gemfile +3 -4
- data/{COMM-LICENSE → REACT-ON-RAILS-PRO-LICENSE} +6 -9
- data/README.md +102 -69
- data/Rakefile +0 -7
- data/SUMMARY.md +7 -11
- data/book.json +5 -5
- data/docs/additional-reading/asset-pipeline.md +8 -16
- 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/api/javascript-api.md +3 -3
- data/docs/api/redux-store-api.md +4 -2
- data/docs/api/view-helpers-api.md +6 -7
- data/docs/basics/configuration.md +60 -57
- data/docs/basics/deployment.md +1 -2
- data/docs/basics/i18n.md +44 -22
- data/docs/basics/installation-into-an-existing-rails-app.md +2 -2
- data/docs/basics/react-server-rendering.md +1 -1
- data/docs/basics/{generator-functions-and-railscontext.md → render-functions-and-railscontext.md} +59 -21
- data/docs/basics/upgrading-react-on-rails.md +50 -2
- data/docs/basics/webpack-configuration.md +15 -1
- 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 +12 -8
- data/docs/{additional-reading → outdated}/heroku-deployment.md +0 -6
- data/docs/{basics → outdated}/how-react-on-rails-works.md +2 -2
- 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 +4 -7
- data/docs/{misc → outdated}/rails3.md +0 -0
- data/docs/tutorial.md +54 -34
- data/jest.config.js +4 -0
- data/lib/generators/react_on_rails/dev_tests_generator.rb +1 -1
- data/lib/generators/react_on_rails/generator_helper.rb +4 -6
- data/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx +9 -8
- 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.rb +3 -1
- data/lib/react_on_rails/configuration.rb +13 -22
- data/lib/react_on_rails/error.rb +2 -0
- data/lib/react_on_rails/helper.rb +41 -91
- data/lib/react_on_rails/json_parse_error.rb +2 -0
- data/lib/react_on_rails/locales/base.rb +142 -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/server_rendering_pool/ruby_embedded_java_script.rb +41 -46
- data/lib/react_on_rails/test_helper/ensure_assets_compiled.rb +7 -8
- data/lib/react_on_rails/utils.rb +14 -19
- data/lib/react_on_rails/version.rb +1 -1
- data/lib/react_on_rails/version_checker.rb +1 -0
- data/lib/react_on_rails/webpacker_utils.rb +13 -2
- data/lib/tasks/assets.rake +5 -45
- data/lib/tasks/locale.rake +8 -2
- data/package-scripts.yml +11 -8
- data/package.json +29 -28
- 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/run_rspec.rake +5 -18
- data/react_on_rails.gemspec +3 -5
- data/tsconfig.json +14 -0
- data/webpackConfigLoader.js +3 -2
- data/yarn.lock +4170 -2197
- metadata +30 -56
- data/Gemfile.rails32 +0 -73
- data/docs/additional-reading/babel.md +0 -5
- data/docs/additional-reading/hot-reloading-rails-development-asset-pipeline.md +0 -47
- data/docs/api/ruby-api-hot-reload-view-helpers.md +0 -44
- data/lib/react_on_rails/assets_precompile.rb +0 -153
- data/lib/react_on_rails/locales_to_js.rb +0 -138
data/jest.config.js
ADDED
@@ -50,7 +50,7 @@ module ReactOnRails
|
|
50
50
|
contents = File.read(package_json)
|
51
51
|
replacement_value = <<-STRING
|
52
52
|
"scripts": {
|
53
|
-
"postinstall": "
|
53
|
+
"postinstall": "yalc link react-on-rails",
|
54
54
|
STRING
|
55
55
|
new_client_package_json_contents = contents.gsub(/ {2}"scripts": {/,
|
56
56
|
replacement_value)
|
@@ -15,13 +15,11 @@ module GeneratorHelper
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def setup_file_error(file, data)
|
18
|
-
|
19
|
-
|
20
|
-
#{file}
|
21
|
-
|
22
|
-
#{data}
|
18
|
+
<<~MSG
|
19
|
+
#{file} was not found.
|
20
|
+
Please add the following content to your #{file} file:
|
21
|
+
#{data}
|
23
22
|
MSG
|
24
|
-
# rubocop:enable Layout/IndentHeredoc
|
25
23
|
end
|
26
24
|
|
27
25
|
def empty_directory_with_keep_file(destination, config = {})
|
@@ -25,19 +25,20 @@ export default class HelloWorld extends React.Component {
|
|
25
25
|
return (
|
26
26
|
<div>
|
27
27
|
<h3>
|
28
|
-
Hello,
|
28
|
+
Hello,
|
29
|
+
{this.state.name}!
|
29
30
|
</h3>
|
30
31
|
<hr />
|
31
|
-
<form
|
32
|
+
<form>
|
32
33
|
<label htmlFor="name">
|
33
34
|
Say hello to:
|
35
|
+
<input
|
36
|
+
id="name"
|
37
|
+
type="text"
|
38
|
+
value={this.state.name}
|
39
|
+
onChange={(e) => this.updateName(e.target.value)}
|
40
|
+
/>
|
34
41
|
</label>
|
35
|
-
<input
|
36
|
-
id="name"
|
37
|
-
type="text"
|
38
|
-
value={this.state.name}
|
39
|
-
onChange={(e) => this.updateName(e.target.value)}
|
40
|
-
/>
|
41
42
|
</form>
|
42
43
|
</div>
|
43
44
|
);
|
@@ -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;
|
data/lib/react_on_rails.rb
CHANGED
@@ -22,4 +22,6 @@ require "react_on_rails/webpacker_utils"
|
|
22
22
|
require "react_on_rails/test_helper/webpack_assets_compiler"
|
23
23
|
require "react_on_rails/test_helper/webpack_assets_status_checker"
|
24
24
|
require "react_on_rails/test_helper/ensure_assets_compiled"
|
25
|
-
require "react_on_rails/
|
25
|
+
require "react_on_rails/locales/base"
|
26
|
+
require "react_on_rails/locales/to_js"
|
27
|
+
require "react_on_rails/locales/to_json"
|
@@ -31,10 +31,11 @@ module ReactOnRails
|
|
31
31
|
webpack_generated_files: %w[manifest.json],
|
32
32
|
rendering_extension: nil,
|
33
33
|
server_render_method: nil,
|
34
|
-
symlink_non_digested_assets_regex: nil,
|
35
34
|
build_test_command: "",
|
36
35
|
build_production_command: "",
|
37
|
-
random_dom_id: DEFAULT_RANDOM_DOM_ID
|
36
|
+
random_dom_id: DEFAULT_RANDOM_DOM_ID,
|
37
|
+
same_bundle_for_client_and_server: false,
|
38
|
+
i18n_output_format: nil
|
38
39
|
)
|
39
40
|
end
|
40
41
|
|
@@ -46,8 +47,9 @@ module ReactOnRails
|
|
46
47
|
:generated_assets_dirs, :generated_assets_dir,
|
47
48
|
:webpack_generated_files, :rendering_extension, :build_test_command,
|
48
49
|
:build_production_command,
|
49
|
-
:i18n_dir, :i18n_yml_dir,
|
50
|
-
:server_render_method, :
|
50
|
+
:i18n_dir, :i18n_yml_dir, :i18n_output_format,
|
51
|
+
:server_render_method, :random_dom_id,
|
52
|
+
:same_bundle_for_client_and_server
|
51
53
|
|
52
54
|
def initialize(node_modules_location: nil, server_bundle_js_file: nil, prerender: nil,
|
53
55
|
replay_console: nil,
|
@@ -58,16 +60,17 @@ module ReactOnRails
|
|
58
60
|
generated_assets_dir: nil, webpack_generated_files: nil,
|
59
61
|
rendering_extension: nil, build_test_command: nil,
|
60
62
|
build_production_command: nil,
|
61
|
-
|
62
|
-
|
63
|
+
same_bundle_for_client_and_server: nil,
|
64
|
+
i18n_dir: nil, i18n_yml_dir: nil, i18n_output_format: nil,
|
65
|
+
random_dom_id: nil, server_render_method: nil)
|
63
66
|
self.node_modules_location = node_modules_location.present? ? node_modules_location : Rails.root
|
64
|
-
self.server_bundle_js_file = server_bundle_js_file
|
65
67
|
self.generated_assets_dirs = generated_assets_dirs
|
66
68
|
self.generated_assets_dir = generated_assets_dir
|
67
69
|
self.build_test_command = build_test_command
|
68
70
|
self.build_production_command = build_production_command
|
69
71
|
self.i18n_dir = i18n_dir
|
70
72
|
self.i18n_yml_dir = i18n_yml_dir
|
73
|
+
self.i18n_output_format = i18n_output_format
|
71
74
|
|
72
75
|
self.random_dom_id = random_dom_id
|
73
76
|
self.prerender = prerender
|
@@ -83,6 +86,8 @@ module ReactOnRails
|
|
83
86
|
self.skip_display_none = skip_display_none
|
84
87
|
|
85
88
|
# Server rendering:
|
89
|
+
self.server_bundle_js_file = server_bundle_js_file
|
90
|
+
self.same_bundle_for_client_and_server = same_bundle_for_client_and_server
|
86
91
|
self.server_renderer_pool_size = self.development_mode ? 1 : server_renderer_pool_size
|
87
92
|
self.server_renderer_timeout = server_renderer_timeout # seconds
|
88
93
|
|
@@ -90,7 +95,6 @@ module ReactOnRails
|
|
90
95
|
self.rendering_extension = rendering_extension
|
91
96
|
|
92
97
|
self.server_render_method = server_render_method
|
93
|
-
self.symlink_non_digested_assets_regex = symlink_non_digested_assets_regex
|
94
98
|
end
|
95
99
|
|
96
100
|
# on ReactOnRails
|
@@ -99,7 +103,6 @@ module ReactOnRails
|
|
99
103
|
configure_generated_assets_dirs_deprecation
|
100
104
|
configure_skip_display_none_deprecation
|
101
105
|
ensure_generated_assets_dir_present
|
102
|
-
ensure_server_bundle_js_file_has_no_path
|
103
106
|
check_i18n_directory_exists
|
104
107
|
check_i18n_yml_directory_exists
|
105
108
|
check_server_render_method_is_only_execjs
|
@@ -198,24 +201,12 @@ module ReactOnRails
|
|
198
201
|
def ensure_webpack_generated_files_exists
|
199
202
|
return unless webpack_generated_files.empty?
|
200
203
|
|
201
|
-
files = ["
|
204
|
+
files = ["manifest.json"]
|
202
205
|
files << server_bundle_js_file if server_bundle_js_file.present?
|
203
206
|
|
204
207
|
self.webpack_generated_files = files
|
205
208
|
end
|
206
209
|
|
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
210
|
def configure_skip_display_none_deprecation
|
220
211
|
return if skip_display_none.nil?
|
221
212
|
|
data/lib/react_on_rails/error.rb
CHANGED
@@ -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
|
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.
|
@@ -278,11 +240,12 @@ module ReactOnRails
|
|
278
240
|
end
|
279
241
|
|
280
242
|
# This is the definitive list of the default values used for the rails_context, which is the
|
281
|
-
# second parameter passed to both component and store
|
243
|
+
# second parameter passed to both component and store render functions.
|
282
244
|
# This method can be called from views and from the controller, as `helpers.rails_context`
|
283
245
|
#
|
284
246
|
# rubocop:disable Metrics/AbcSize
|
285
247
|
def rails_context(server_side: true)
|
248
|
+
# ALERT: Keep in sync with node_package/src/types/index.ts for the properties of RailsContext
|
286
249
|
@rails_context ||= begin
|
287
250
|
result = {
|
288
251
|
railsEnv: Rails.env,
|
@@ -395,13 +358,11 @@ module ReactOnRails
|
|
395
358
|
|
396
359
|
def compose_react_component_html_with_spec_and_console(component_specification_tag, rendered_output, console_script)
|
397
360
|
# IMPORTANT: Ensure that we mark string as html_safe to avoid escaping.
|
398
|
-
|
399
|
-
|
400
|
-
#{
|
401
|
-
|
402
|
-
#{console_script}
|
361
|
+
<<~HTML.html_safe
|
362
|
+
#{rendered_output}
|
363
|
+
#{component_specification_tag}
|
364
|
+
#{console_script}
|
403
365
|
HTML
|
404
|
-
# rubocop:enable Layout/IndentHeredoc
|
405
366
|
end
|
406
367
|
|
407
368
|
# prepend the rails_context if not yet applied
|
@@ -551,17 +512,6 @@ module ReactOnRails
|
|
551
512
|
val.nil? ? ReactOnRails.configuration.replay_console : val
|
552
513
|
end
|
553
514
|
|
554
|
-
def use_hot_reloading?
|
555
|
-
ENV["REACT_ON_RAILS_ENV"] == "HOT"
|
556
|
-
end
|
557
|
-
|
558
|
-
def send_tag_method(tag_method_name, args)
|
559
|
-
asset_type = use_hot_reloading? ? :hot : :static
|
560
|
-
assets = Array(args[asset_type])
|
561
|
-
options = args.delete_if { |key, _value| %i[hot static].include?(key) }
|
562
|
-
send(tag_method_name, *assets, options) unless assets.empty?
|
563
|
-
end
|
564
|
-
|
565
515
|
def in_mailer?
|
566
516
|
return false unless defined?(controller)
|
567
517
|
return false unless defined?(ActionMailer::Base)
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "erb"
|
4
|
+
|
5
|
+
module ReactOnRails
|
6
|
+
module Locales
|
7
|
+
class Base
|
8
|
+
def initialize
|
9
|
+
return if i18n_dir.nil?
|
10
|
+
return unless obsolete?
|
11
|
+
|
12
|
+
@translations, @defaults = generate_translations
|
13
|
+
convert
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def file_format; end
|
19
|
+
|
20
|
+
def obsolete?
|
21
|
+
return true if exist_files.empty?
|
22
|
+
|
23
|
+
files_are_outdated
|
24
|
+
end
|
25
|
+
|
26
|
+
def exist_files
|
27
|
+
@exist_files ||= files.select(&File.method(:exist?))
|
28
|
+
end
|
29
|
+
|
30
|
+
def files_are_outdated
|
31
|
+
latest_yml = locale_files.map(&File.method(:mtime)).max
|
32
|
+
earliest = exist_files.map(&File.method(:mtime)).min
|
33
|
+
latest_yml > earliest
|
34
|
+
end
|
35
|
+
|
36
|
+
def file_names
|
37
|
+
%w[translations default]
|
38
|
+
end
|
39
|
+
|
40
|
+
def files
|
41
|
+
@files ||= file_names.map { |n| file(n) }
|
42
|
+
end
|
43
|
+
|
44
|
+
def file(name)
|
45
|
+
"#{i18n_dir}/#{name}.#{file_format}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def locale_files
|
49
|
+
@locale_files ||= begin
|
50
|
+
if i18n_yml_dir.present?
|
51
|
+
Dir["#{i18n_yml_dir}/**/*.yml"]
|
52
|
+
else
|
53
|
+
ReactOnRails::Utils.truthy_presence(
|
54
|
+
Rails.application && Rails.application.config.i18n.load_path
|
55
|
+
).presence
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def i18n_dir
|
61
|
+
@i18n_dir ||= ReactOnRails.configuration.i18n_dir
|
62
|
+
end
|
63
|
+
|
64
|
+
def i18n_yml_dir
|
65
|
+
@i18n_yml_dir ||= ReactOnRails.configuration.i18n_yml_dir
|
66
|
+
end
|
67
|
+
|
68
|
+
def default_locale
|
69
|
+
@default_locale ||= I18n.default_locale.to_s || "en"
|
70
|
+
end
|
71
|
+
|
72
|
+
def convert
|
73
|
+
file_names.each do |name|
|
74
|
+
template = send("template_#{name}")
|
75
|
+
path = file(name)
|
76
|
+
generate_file(template, path)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def generate_file(template, path)
|
81
|
+
result = ERB.new(template).result()
|
82
|
+
File.open(path, "w") do |f|
|
83
|
+
f.write(result)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def generate_translations
|
88
|
+
translations = {}
|
89
|
+
defaults = {}
|
90
|
+
locale_files.each do |f|
|
91
|
+
translation = YAML.safe_load(File.open(f))
|
92
|
+
key = translation.keys[0]
|
93
|
+
val = flatten(translation[key])
|
94
|
+
translations = translations.deep_merge(key => val)
|
95
|
+
defaults = defaults.deep_merge(flatten_defaults(val)) if key == default_locale
|
96
|
+
end
|
97
|
+
[translations.to_json, defaults.to_json]
|
98
|
+
end
|
99
|
+
|
100
|
+
def format(input)
|
101
|
+
input.to_s.tr(".", "_").camelize(:lower).to_sym
|
102
|
+
end
|
103
|
+
|
104
|
+
def flatten_defaults(val)
|
105
|
+
flatten(val).each_with_object({}) do |(k, v), h|
|
106
|
+
key = format(k)
|
107
|
+
h[key] = { id: k, defaultMessage: v }
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def flatten(translations)
|
112
|
+
translations.each_with_object({}) do |(k, v), h|
|
113
|
+
if v.is_a? Hash
|
114
|
+
flatten(v).map { |hk, hv| h["#{k}.#{hk}".to_sym] = hv }
|
115
|
+
elsif v.is_a?(String)
|
116
|
+
h[k] = v.gsub("%{", "{")
|
117
|
+
elsif !v.is_a?(Array)
|
118
|
+
h[k] = v
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def template_translations
|
124
|
+
<<-JS.strip_heredoc
|
125
|
+
export const translations = #{@translations};
|
126
|
+
JS
|
127
|
+
end
|
128
|
+
|
129
|
+
def template_default
|
130
|
+
<<-JS.strip_heredoc
|
131
|
+
import { defineMessages } from 'react-intl';
|
132
|
+
|
133
|
+
const defaultLocale = \'#{default_locale}\';
|
134
|
+
|
135
|
+
const defaultMessages = defineMessages(#{@defaults});
|
136
|
+
|
137
|
+
export { defaultMessages, defaultLocale };
|
138
|
+
JS
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|