react_on_rails 11.2.1 → 12.0.0.pre.beta.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +320 -0
  3. data/.eslintignore +2 -1
  4. data/.eslintrc +23 -1
  5. data/.github/FUNDING.yml +1 -0
  6. data/.gitignore +3 -1
  7. data/.prettierignore +10 -1
  8. data/.prettierrc +3 -0
  9. data/.rubocop.yml +37 -11
  10. data/.travis.yml +10 -20
  11. data/CHANGELOG.md +85 -9
  12. data/CONTRIBUTING.md +60 -71
  13. data/Gemfile +3 -4
  14. data/{COMM-LICENSE → REACT-ON-RAILS-PRO-LICENSE} +6 -9
  15. data/README.md +121 -71
  16. data/Rakefile +0 -7
  17. data/SUMMARY.md +9 -12
  18. data/book.json +5 -5
  19. data/docs/additional-reading/asset-pipeline.md +8 -16
  20. data/docs/additional-reading/react-helmet.md +30 -10
  21. data/docs/additional-reading/react-router.md +52 -75
  22. data/docs/additional-reading/server-rendering-tips.md +12 -7
  23. data/docs/api/javascript-api.md +3 -3
  24. data/docs/api/redux-store-api.md +4 -2
  25. data/docs/api/view-helpers-api.md +8 -9
  26. data/docs/basics/configuration.md +68 -59
  27. data/docs/basics/deployment.md +1 -2
  28. data/docs/basics/hmr-and-hot-reloading-with-the-webpack-dev-server.md +49 -0
  29. data/docs/basics/i18n.md +44 -22
  30. data/docs/basics/installation-into-an-existing-rails-app.md +2 -2
  31. data/docs/basics/minitest-configuration.md +31 -0
  32. data/docs/basics/react-server-rendering.md +1 -1
  33. data/docs/basics/{generator-functions-and-railscontext.md → render-functions-and-railscontext.md} +59 -21
  34. data/docs/basics/rspec-configuration.md +29 -17
  35. data/docs/basics/upgrading-react-on-rails.md +67 -3
  36. data/docs/basics/webpack-configuration.md +15 -1
  37. data/docs/contributor-info/errors-with-hooks.md +45 -0
  38. data/docs/contributor-info/pull-requests.md +44 -0
  39. data/docs/misc/doctrine.md +1 -1
  40. data/docs/{misc-pending → outdated}/code-splitting.md +12 -8
  41. data/docs/{additional-reading → outdated}/heroku-deployment.md +0 -6
  42. data/docs/{basics → outdated}/how-react-on-rails-works.md +3 -3
  43. data/docs/{misc-pending → outdated}/manual-installation-overview.md +5 -5
  44. data/docs/{additional-reading → outdated}/rails-assets-relative-paths.md +3 -3
  45. data/docs/{misc-pending → outdated}/rails-assets.md +4 -7
  46. data/docs/{misc → outdated}/rails3.md +0 -0
  47. data/docs/testimonials/resortpass.md +13 -0
  48. data/docs/testimonials/testimonials.md +11 -1
  49. data/docs/tutorial.md +96 -70
  50. data/jest.config.js +4 -0
  51. data/lib/generators/react_on_rails/base_generator.rb +2 -2
  52. data/lib/generators/react_on_rails/dev_tests_generator.rb +1 -1
  53. data/lib/generators/react_on_rails/generator_helper.rb +4 -6
  54. data/lib/generators/react_on_rails/templates/base/base/Procfile.dev +3 -1
  55. data/lib/generators/react_on_rails/templates/base/base/Procfile.dev-hmr +26 -0
  56. data/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx +20 -40
  57. data/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb +2 -1
  58. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx +4 -8
  59. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/store/helloWorldStore.js +1 -3
  60. data/lib/react_on_rails.rb +3 -1
  61. data/lib/react_on_rails/configuration.rb +13 -22
  62. data/lib/react_on_rails/error.rb +2 -0
  63. data/lib/react_on_rails/helper.rb +100 -143
  64. data/lib/react_on_rails/json_parse_error.rb +2 -0
  65. data/lib/react_on_rails/locales/base.rb +150 -0
  66. data/lib/react_on_rails/locales/to_js.rb +37 -0
  67. data/lib/react_on_rails/locales/to_json.rb +27 -0
  68. data/lib/react_on_rails/prerender_error.rb +11 -15
  69. data/lib/react_on_rails/react_component/render_options.rb +4 -0
  70. data/lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb +41 -46
  71. data/lib/react_on_rails/test_helper/ensure_assets_compiled.rb +7 -8
  72. data/lib/react_on_rails/utils.rb +14 -19
  73. data/lib/react_on_rails/version.rb +1 -1
  74. data/lib/react_on_rails/version_checker.rb +1 -0
  75. data/lib/react_on_rails/webpacker_utils.rb +13 -2
  76. data/lib/tasks/assets.rake +19 -44
  77. data/lib/tasks/locale.rake +4 -2
  78. data/package-scripts.yml +11 -8
  79. data/package.json +29 -28
  80. data/rakelib/dummy_apps.rake +1 -9
  81. data/rakelib/example_type.rb +3 -1
  82. data/rakelib/examples.rake +3 -0
  83. data/rakelib/lint.rake +2 -7
  84. data/rakelib/node_package.rake +2 -2
  85. data/rakelib/release.rake +0 -6
  86. data/rakelib/run_rspec.rake +5 -18
  87. data/react_on_rails.gemspec +3 -5
  88. data/tsconfig.json +14 -0
  89. data/webpackConfigLoader.js +3 -2
  90. data/yarn.lock +4170 -2197
  91. metadata +34 -57
  92. data/Gemfile.rails32 +0 -73
  93. data/docs/additional-reading/babel.md +0 -5
  94. data/docs/additional-reading/hot-reloading-rails-development-asset-pipeline.md +0 -47
  95. data/docs/api/ruby-api-hot-reload-view-helpers.md +0 -44
  96. data/lib/generators/react_on_rails/templates/base/base/Procfile.dev-server +0 -12
  97. data/lib/react_on_rails/assets_precompile.rb +0 -153
  98. data/lib/react_on_rails/locales_to_js.rb +0 -138
@@ -0,0 +1,4 @@
1
+ module.exports = {
2
+ preset: 'ts-jest/presets/js-with-ts',
3
+ testEnvironment: 'jsdom',
4
+ };
@@ -33,7 +33,7 @@ module ReactOnRails
33
33
  app/views/layouts/hello_world.html.erb
34
34
  config/initializers/react_on_rails.rb
35
35
  Procfile.dev
36
- Procfile.dev-server]
36
+ Procfile.dev-hmr]
37
37
  base_files.each { |file| copy_file("#{base_path}#{file}", file) }
38
38
  end
39
39
 
@@ -107,7 +107,7 @@ module ReactOnRails
107
107
  foreman start -f Procfile.dev
108
108
 
109
109
  - To turn on HMR, edit config/webpacker.yml and set HMR to true. Restart the rails server
110
- and bin/webpack-dev-server. Or use Procfile.dev-server.
110
+ and bin/webpack-dev-server. Or use Procfile.dev-hmr.
111
111
 
112
112
  - To server render, change this line app/views/hello_world/index.html.erb to
113
113
  `prerender: true` to see server rendering (right click on page and select "view source").
@@ -50,7 +50,7 @@ module ReactOnRails
50
50
  contents = File.read(package_json)
51
51
  replacement_value = <<-STRING
52
52
  "scripts": {
53
- "postinstall": "yarn link react-on-rails",
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
- # rubocop:disable Layout/IndentHeredoc
19
- <<-MSG
20
- #{file} was not found.
21
- Please add the following content to your #{file} file:
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 = {})
@@ -4,4 +4,6 @@ web: rails s -p 3000
4
4
  # Next line runs a watch process with webpack to compile the changed files.
5
5
  # When making frequent changes to client side assets, you will prefer building webpack assets
6
6
  # upon saving rather than when you refresh your browser page.
7
- client: sh -c 'rm -rf public/packs/* || true && bundle exec rake react_on_rails:locale && bin/webpack -w'
7
+ # Note, if using React on Rails localization you will need to run
8
+ # `bundle exec rake react_on_rails:locale` before you run bin/webpack
9
+ client: sh -c 'rm -rf public/packs/* || true && bin/webpack -w'
@@ -0,0 +1,26 @@
1
+ # Procfile for development using HMR
2
+
3
+ web: rails s -p 3000
4
+
5
+ # Note, hot and live reloading don't work with the default generator setup on
6
+ # top of the rails/webpacker Webpack config with server rendering.
7
+ # If you have server rendering enabled (prerender is true), you either need to
8
+ # a. Ensure that you have dev_server.hmr and dev_server.inline BOTH set to false,
9
+ # and you have this option in your config/initializers/react_on_rails.rb:
10
+ # config.same_bundle_for_client_and_server = true
11
+ # If you have either config/webpacker.yml option set to true, you'll see errors like
12
+ # "ReferenceError: window is not defined" (if hmr is true)
13
+ # "TypeError: Cannot read property 'prototype' of undefined" (if inline is true)
14
+ # b. Skip using the webpack-dev-server. bin/webpack --watch is typically
15
+ fast enough.
16
+ # c. See the React on Rails README for a link to documentation for how to setup
17
+ # SSR with HMR and React hot loading using the webpack-dev-server only for the
18
+ # client bundles and a static file for the server bundle.
19
+
20
+ # Run the webpack-dev-server for client and maybe server files
21
+ webpack-dev-server: bin/webpack-dev-server
22
+
23
+ # Keep the JS fresh for server rendering. Remove if not server rendering.
24
+ # Especially if you have not configured generation of a server bundle without a hash.
25
+ # as that will conflict with the manifest created by the bin/webpack-dev-server
26
+ # rails-server-assets: SERVER_BUNDLE_ONLY=yes bin/webpack --watch
@@ -1,45 +1,25 @@
1
1
  import PropTypes from 'prop-types';
2
- import React from 'react';
2
+ import React, { useState } from 'react';
3
3
 
4
- export default class HelloWorld extends React.Component {
5
- static propTypes = {
6
- name: PropTypes.string.isRequired, // this is passed from the Rails view
7
- };
4
+ const HelloWorld = (props) => {
5
+ const [name, setName] = useState(props.name);
8
6
 
9
- /**
10
- * @param props - Comes from your rails view.
11
- */
12
- constructor(props) {
13
- super(props);
7
+ return (
8
+ <div>
9
+ <h3>Hello, {name}!</h3>
10
+ <hr />
11
+ <form>
12
+ <label htmlFor="name">
13
+ Say hello to:
14
+ <input id="name" type="text" value={name} onChange={(e) => setName(e.target.value)} />
15
+ </label>
16
+ </form>
17
+ </div>
18
+ );
19
+ };
14
20
 
15
- // How to set initial state in ES6 class syntax
16
- // https://reactjs.org/docs/state-and-lifecycle.html#adding-local-state-to-a-class
17
- this.state = { name: this.props.name };
18
- }
21
+ HelloWorld.propTypes = {
22
+ name: PropTypes.string.isRequired, // this is passed from the Rails view
23
+ };
19
24
 
20
- updateName = (name) => {
21
- this.setState({ name });
22
- };
23
-
24
- render() {
25
- return (
26
- <div>
27
- <h3>
28
- Hello, {this.state.name}!
29
- </h3>
30
- <hr />
31
- <form >
32
- <label htmlFor="name">
33
- Say hello to:
34
- </label>
35
- <input
36
- id="name"
37
- type="text"
38
- value={this.state.name}
39
- onChange={(e) => this.updateName(e.target.value)}
40
- />
41
- </form>
42
- </div>
43
- );
44
- }
45
- }
25
+ export default HelloWorld;
@@ -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
- config.build_production_command = "RAILS_ENV=production NODE_ENV=production bin/webpack"
9
+ # If nil, then the standard rails/webpacker assets:precompile will run
10
+ # config.build_production_command = nil
10
11
 
11
12
  ################################################################################
12
13
  ################################################################################
@@ -4,19 +4,15 @@ import React from 'react';
4
4
  const HelloWorld = ({ name, updateName }) => (
5
5
  <div>
6
6
  <h3>
7
- Hello, {name}!
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;
@@ -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/locales_to_js"
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, :symlink_non_digested_assets_regex, :random_dom_id
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
- i18n_dir: nil, i18n_yml_dir: nil, random_dom_id: nil,
62
- server_render_method: nil, symlink_non_digested_assets_regex: nil)
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 = ["hello-world-bundle.js"]
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
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ReactOnRails
2
4
  class Error < StandardError
3
5
  end
@@ -15,68 +15,20 @@ module ReactOnRails
15
15
  module Helper
16
16
  include ReactOnRails::Utils::Required
17
17
 
18
- COMPONENT_HTML_KEY = "componentHtml".freeze
18
+ COMPONENT_HTML_KEY = "componentHtml"
19
19
 
20
- # The env_javascript_include_tag and env_stylesheet_link_tag support the usage of a webpack
21
- # dev server for providing the JS and CSS assets during development mode. See
22
- # https://github.com/shakacode/react-webpack-rails-tutorial/ for a working example.
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
- # The key options are `static` and `hot` which specify what you want for static vs. hot. Both of
25
- # these params are optional, and support either a single value, or an array.
24
+ # let MyReactComponentApp = (props, railsContext) => <MyReactComponent {...props}/>;
26
25
  #
27
- # static vs. hot is picked based on whether
28
- # ENV["REACT_ON_RAILS_ENV"] == "HOT"
26
+ # Alternately, you can define the render function with an additional property
27
+ # `.renderFunction = true`:
29
28
  #
30
- # <%= env_stylesheet_link_tag(static: 'application_static',
31
- # hot: 'application_non_webpack',
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 global default.
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 = <<-MSG.strip_heredoc
116
- Use react_component_hash (not react_component) to return a Hash to your ruby view code. See
117
- https://github.com/shakacode/react_on_rails/blob/master/spec/dummy/client/app/startup/ReactHelmetServerApp.jsx
118
- for an example of the necessary javascript configuration."
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
- msg = <<-MSG.strip_heredoc
124
- ReactOnRails: server_rendered_html is expected to be a String for #{component_name}. If you're
125
- trying to use a generator function to return a Hash to your ruby view code, then use
126
- react_component_hash instead of react_component and see
127
- https://github.com/shakacode/react_on_rails/blob/master/spec/dummy/client/app/startup/ReactHelmetServerApp.jsx
128
- for an example of the JavaScript code."
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 generator function for server rendering must return an Object rather than a React component.
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 = <<-MSG.strip_heredoc
170
- Generator function used by react_component_hash for #{component_name} is expected to return
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.
@@ -277,6 +239,60 @@ module ReactOnRails
277
239
  ReactOnRails::JsonOutput.escape(json_value)
278
240
  end
279
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
+
280
296
  private
281
297
 
282
298
  def build_react_component_result_for_server_rendered_string(
@@ -286,9 +302,15 @@ module ReactOnRails
286
302
  render_options: required("render_options")
287
303
  )
288
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
289
311
  content_tag_options[:id] = render_options.dom_id
290
312
 
291
- rendered_output = content_tag(:div,
313
+ rendered_output = content_tag(content_tag_options_html_tag.to_sym,
292
314
  server_rendered_html.html_safe,
293
315
  content_tag_options)
294
316
 
@@ -336,13 +358,11 @@ module ReactOnRails
336
358
 
337
359
  def compose_react_component_html_with_spec_and_console(component_specification_tag, rendered_output, console_script)
338
360
  # IMPORTANT: Ensure that we mark string as html_safe to avoid escaping.
339
- # rubocop:disable Layout/IndentHeredoc
340
- <<-HTML.html_safe
341
- #{rendered_output}
342
- #{component_specification_tag}
343
- #{console_script}
361
+ <<~HTML.html_safe
362
+ #{rendered_output}
363
+ #{component_specification_tag}
364
+ #{console_script}
344
365
  HTML
345
- # rubocop:enable Layout/IndentHeredoc
346
366
  end
347
367
 
348
368
  # prepend the rails_context if not yet applied
@@ -488,73 +508,10 @@ module ReactOnRails
488
508
  result
489
509
  end
490
510
 
491
- # This is the definitive list of the default values used for the rails_context, which is the
492
- # second parameter passed to both component and store generator functions.
493
- # rubocop:disable Metrics/AbcSize
494
- def rails_context(server_side: required("server_side"))
495
- @rails_context ||= begin
496
- result = {
497
- railsEnv: Rails.env,
498
- inMailer: in_mailer?,
499
- # Locale settings
500
- i18nLocale: I18n.locale,
501
- i18nDefaultLocale: I18n.default_locale,
502
- rorVersion: ReactOnRails::VERSION,
503
- rorPro: ReactOnRails::Utils.react_on_rails_pro?
504
- }
505
- if defined?(request) && request.present?
506
- # Check for encoding of the request's original_url and try to force-encoding the
507
- # URLs as UTF-8. This situation can occur in browsers that do not encode the
508
- # entire URL as UTF-8 already, mostly on the Windows platform (IE11 and lower).
509
- original_url_normalized = request.original_url
510
- if original_url_normalized.encoding.to_s == "ASCII-8BIT"
511
- original_url_normalized = original_url_normalized.force_encoding("ISO-8859-1").encode("UTF-8")
512
- end
513
-
514
- # Using Addressable instead of standard URI to better deal with
515
- # non-ASCII characters (see https://github.com/shakacode/react_on_rails/pull/405)
516
- uri = Addressable::URI.parse(original_url_normalized)
517
- # uri = Addressable::URI.parse("http://foo.com:3000/posts?id=30&limit=5#time=1305298413")
518
-
519
- result.merge!(
520
- # URL settings
521
- href: uri.to_s,
522
- location: "#{uri.path}#{uri.query.present? ? "?#{uri.query}" : ''}",
523
- scheme: uri.scheme, # http
524
- host: uri.host, # foo.com
525
- port: uri.port,
526
- pathname: uri.path, # /posts
527
- search: uri.query, # id=30&limit=5
528
- httpAcceptLanguage: request.env["HTTP_ACCEPT_LANGUAGE"]
529
- )
530
- end
531
- if ReactOnRails.configuration.rendering_extension
532
- custom_context = ReactOnRails.configuration.rendering_extension.custom_context(self)
533
- result.merge!(custom_context) if custom_context
534
- end
535
- result
536
- end
537
-
538
- @rails_context.merge(serverSide: server_side)
539
- end
540
-
541
- # rubocop:enable Metrics/AbcSize
542
-
543
511
  def replay_console_option(val)
544
512
  val.nil? ? ReactOnRails.configuration.replay_console : val
545
513
  end
546
514
 
547
- def use_hot_reloading?
548
- ENV["REACT_ON_RAILS_ENV"] == "HOT"
549
- end
550
-
551
- def send_tag_method(tag_method_name, args)
552
- asset_type = use_hot_reloading? ? :hot : :static
553
- assets = Array(args[asset_type])
554
- options = args.delete_if { |key, _value| %i[hot static].include?(key) }
555
- send(tag_method_name, *assets, options) unless assets.empty?
556
- end
557
-
558
515
  def in_mailer?
559
516
  return false unless defined?(controller)
560
517
  return false unless defined?(ActionMailer::Base)