react_on_rails 11.2.1 → 12.0.0.pre.beta.2

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.
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)