react_on_rails 12.0.1 → 13.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.
Files changed (152) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +27 -13
  3. data/.eslintrc +2 -1
  4. data/.github/ISSUE_TEMPLATE/bug_report.md +23 -0
  5. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  6. data/.github/workflows/lint-js-and-ruby.yml +53 -0
  7. data/.github/workflows/main.yml +178 -0
  8. data/.github/workflows/package-js-tests.yml +35 -0
  9. data/.github/workflows/rspec-package-specs.yml +45 -0
  10. data/.gitignore +0 -3
  11. data/.prettierignore +2 -0
  12. data/.prettierrc +0 -3
  13. data/.rubocop.yml +48 -39
  14. data/.travis.yml +3 -1
  15. data/CHANGELOG.md +112 -25
  16. data/CONTRIBUTING.md +4 -3
  17. data/Gemfile +1 -33
  18. data/Gemfile.development_dependencies +52 -0
  19. data/NEWS.md +14 -4
  20. data/REACT-ON-RAILS-PRO-LICENSE +1 -1
  21. data/README.md +42 -324
  22. data/Rakefile +1 -1
  23. data/SUMMARY.md +22 -13
  24. data/docs/{basics → additional-details}/generator-details.md +1 -6
  25. data/docs/{outdated → additional-details}/manual-installation-overview.md +6 -6
  26. data/docs/additional-details/recommended-project-structure.md +69 -0
  27. data/docs/additional-details/upgrade-webpacker-v3-to-v4.md +10 -0
  28. data/docs/api/javascript-api.md +2 -2
  29. data/docs/api/redux-store-api.md +3 -3
  30. data/docs/api/view-helpers-api.md +7 -8
  31. data/docs/contributor-info/linters.md +5 -6
  32. data/docs/contributor-info/pull-requests.md +2 -4
  33. data/docs/contributor-info/releasing.md +1 -1
  34. data/docs/{basics → deployment}/heroku-deployment.md +16 -5
  35. data/docs/{basics → guides}/client-vs-server-rendering.md +4 -2
  36. data/docs/{basics → guides}/configuration.md +65 -31
  37. data/docs/guides/deployment.md +4 -0
  38. data/docs/guides/getting-started.md +183 -0
  39. data/docs/guides/hmr-and-hot-reloading-with-the-webpack-dev-server.md +104 -0
  40. data/docs/{outdated → guides}/how-react-on-rails-works.md +10 -10
  41. data/docs/guides/how-to-conditionally-server-render-based-on-device-type.md +39 -0
  42. data/docs/guides/how-to-use-different-files-for-client-and-server-rendering.md +98 -0
  43. data/docs/{basics → guides}/installation-into-an-existing-rails-app.md +14 -7
  44. data/docs/guides/rails-webpacker-react-integration-options.md +182 -0
  45. data/docs/guides/react-on-rails-overview.md +30 -0
  46. data/docs/{basics → guides}/react-server-rendering.md +8 -5
  47. data/docs/{basics → guides}/rspec-configuration.md +11 -11
  48. data/docs/{tutorial.md → guides/tutorial.md} +46 -50
  49. data/docs/{basics → guides}/upgrading-react-on-rails.md +56 -18
  50. data/docs/guides/webpack-configuration.md +42 -0
  51. data/docs/home.md +23 -0
  52. data/docs/{outdated → javascript}/code-splitting.md +5 -5
  53. data/docs/{additional-reading → javascript}/converting-from-custom-webpack-config-to-rails-webpacker-config.md +4 -4
  54. data/docs/{additional-reading → javascript}/images.md +5 -6
  55. data/docs/{articles.md → misc/articles.md} +1 -1
  56. data/docs/misc/doctrine.md +5 -6
  57. data/docs/outdated/deferred-rendering.md +39 -0
  58. data/docs/outdated/rails-assets-relative-paths.md +3 -3
  59. data/docs/outdated/rails-assets.md +8 -8
  60. data/docs/outdated/rails3.md +2 -2
  61. data/docs/{additional-reading → rails}/convert-rails-5-api-only-app.md +1 -1
  62. data/docs/{additional-reading → rails}/rails-engine-integration.md +3 -12
  63. data/docs/{additional-reading → rails}/turbolinks.md +13 -1
  64. data/docs/react-on-rails-pro/react-on-rails-pro.md +43 -0
  65. data/docs/testimonials/testimonials.md +6 -6
  66. data/lib/generators/react_on_rails/base_generator.rb +64 -12
  67. data/lib/generators/react_on_rails/dev_tests_generator.rb +1 -1
  68. data/lib/generators/react_on_rails/generator_helper.rb +4 -0
  69. data/lib/generators/react_on_rails/templates/.eslintrc +3 -1
  70. data/lib/generators/react_on_rails/templates/base/base/Procfile.dev +4 -8
  71. data/lib/generators/react_on_rails/templates/base/base/Procfile.dev-static +9 -0
  72. data/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx +2 -1
  73. data/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorld.module.css +4 -0
  74. data/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorldServer.js +5 -0
  75. data/lib/generators/react_on_rails/templates/base/base/app/javascript/packs/server-bundle.js +8 -0
  76. data/lib/generators/react_on_rails/templates/base/base/app/views/layouts/hello_world.html.erb +1 -0
  77. data/lib/generators/react_on_rails/templates/base/base/babel.config.js.tt +32 -0
  78. data/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb +3 -3
  79. data/lib/generators/react_on_rails/templates/base/base/config/webpack/clientWebpackConfig.js.tt +17 -0
  80. data/lib/generators/react_on_rails/templates/base/base/config/webpack/commonWebpackConfig.js.tt +15 -0
  81. data/lib/generators/react_on_rails/templates/base/base/config/webpack/development.js.tt +25 -0
  82. data/lib/generators/react_on_rails/templates/base/base/config/webpack/production.js.tt +9 -0
  83. data/lib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt +117 -0
  84. data/lib/generators/react_on_rails/templates/base/base/config/webpack/test.js.tt +6 -0
  85. data/lib/generators/react_on_rails/templates/base/base/config/webpack/webpack.config.js.tt +15 -0
  86. data/lib/generators/react_on_rails/templates/base/base/config/webpack/webpackConfig.js.tt +36 -0
  87. data/lib/generators/react_on_rails/templates/base/base/config/webpacker.yml +62 -0
  88. data/lib/generators/react_on_rails/templates/dev_tests/spec/rails_helper.rb +8 -2
  89. data/lib/generators/react_on_rails/templates/dev_tests/spec/simplecov_helper.rb +1 -1
  90. data/lib/generators/react_on_rails/templates/dev_tests/spec/{features → system}/hello_world_spec.rb +2 -2
  91. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx +2 -1
  92. data/lib/react_on_rails/configuration.rb +42 -41
  93. data/lib/react_on_rails/git_utils.rb +3 -3
  94. data/lib/react_on_rails/helper.rb +54 -28
  95. data/lib/react_on_rails/json_parse_error.rb +1 -1
  96. data/lib/react_on_rails/locales/base.rb +11 -15
  97. data/lib/react_on_rails/locales/to_js.rb +0 -4
  98. data/lib/react_on_rails/locales/to_json.rb +0 -4
  99. data/lib/react_on_rails/prerender_error.rb +6 -3
  100. data/lib/react_on_rails/react_component/render_options.rb +30 -7
  101. data/lib/react_on_rails/server_rendering_js_code.rb +13 -0
  102. data/lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb +41 -13
  103. data/lib/react_on_rails/test_helper/webpack_assets_compiler.rb +1 -1
  104. data/lib/react_on_rails/test_helper/webpack_assets_status_checker.rb +3 -3
  105. data/lib/react_on_rails/test_helper.rb +4 -2
  106. data/lib/react_on_rails/utils.rb +24 -4
  107. data/lib/react_on_rails/version.rb +1 -1
  108. data/lib/react_on_rails/webpacker_utils.rb +9 -10
  109. data/lib/tasks/assets.rake +19 -50
  110. data/lib/tasks/locale.rake +1 -2
  111. data/package.json +24 -29
  112. data/rakelib/docker.rake +0 -5
  113. data/rakelib/example_type.rb +10 -3
  114. data/rakelib/examples.rake +3 -5
  115. data/rakelib/lint.rake +3 -9
  116. data/rakelib/release.rake +35 -15
  117. data/rakelib/run_rspec.rake +10 -11
  118. data/rakelib/task_helpers.rb +16 -4
  119. data/react_on_rails.gemspec +5 -20
  120. data/yarn.lock +2616 -5190
  121. metadata +86 -256
  122. data/.release-it.json +0 -3
  123. data/docs/additional-reading/upgrade-webpacker-v3-to-v4.md +0 -10
  124. data/docs/additional-reading/webpack-dev-server.md +0 -15
  125. data/docs/basics/deployment.md +0 -4
  126. data/docs/basics/hmr-and-hot-reloading-with-the-webpack-dev-server.md +0 -49
  127. data/docs/basics/recommended-project-structure.md +0 -77
  128. data/docs/basics/webpack-configuration.md +0 -50
  129. data/lib/generators/react_on_rails/templates/base/base/Procfile.dev-hmr +0 -26
  130. data/ruby-lint.yml +0 -25
  131. /data/docs/{basics → additional-details}/migrating-from-react-rails.md +0 -0
  132. /data/docs/{additional-reading → additional-details}/updating-dependencies.md +0 -0
  133. /data/docs/{additional-reading → deployment}/elastic-beanstalk.md +0 -0
  134. /data/docs/{basics → guides}/i18n.md +0 -0
  135. /data/docs/{basics → guides}/minitest-configuration.md +0 -0
  136. /data/docs/{basics → guides}/render-functions-and-railscontext.md +0 -0
  137. /data/docs/{additional-reading → javascript}/angular-js-integration-migration.md +0 -0
  138. /data/docs/{additional-reading → javascript}/asset-pipeline.md +0 -0
  139. /data/docs/{additional-reading → javascript}/capistrano-deployment.md +0 -0
  140. /data/docs/{additional-reading → javascript}/credits.md +0 -0
  141. /data/docs/{additional-reading → javascript}/foreman-issues.md +0 -0
  142. /data/docs/{additional-reading → javascript}/node-dependencies-and-npm.md +0 -0
  143. /data/docs/{additional-reading → javascript}/react-and-redux.md +0 -0
  144. /data/docs/{additional-reading → javascript}/react-helmet.md +0 -0
  145. /data/docs/{additional-reading → javascript}/react-router.md +0 -0
  146. /data/docs/{additional-reading → javascript}/server-rendering-tips.md +0 -0
  147. /data/docs/{additional-reading → javascript}/troubleshooting-when-using-webpacker.md +0 -0
  148. /data/docs/{additional-reading → javascript}/webpack-v1-notes.md +0 -0
  149. /data/docs/{additional-reading → javascript}/webpack.md +0 -0
  150. /data/docs/{coding-style → misc}/style.md +0 -0
  151. /data/docs/{additional-reading → misc}/tips.md +0 -0
  152. /data/docs/{additional-reading → rails}/rails_view_rendering_from_inline_javascript.md +0 -0
@@ -10,10 +10,10 @@ module ReactOnRails
10
10
  status = `git status --porcelain`
11
11
  return false if $CHILD_STATUS.success? && status.empty?
12
12
 
13
- error = if !$CHILD_STATUS.success?
14
- "You do not have Git installed. Please install Git, and commit your changes before continuing"
15
- else
13
+ error = if $CHILD_STATUS.success?
16
14
  "You have uncommitted code. Please commit or stash your changes before continuing"
15
+ else
16
+ "You do not have Git installed. Please install Git, and commit your changes before continuing"
17
17
  end
18
18
  message_handler.add_error(error)
19
19
  true
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # rubocop:disable Metrics/ModuleLength
4
+ # rubocop:disable Metrics/MethodLength
4
5
  # NOTE:
5
6
  # For any heredoc JS:
6
7
  # 1. The white spacing in this file matters!
@@ -32,8 +33,8 @@ module ReactOnRails
32
33
  # Exposing the react_component_name is necessary to both a plain ReactComponent as well as
33
34
  # a generator:
34
35
  # See README.md for how to "register" your react components.
35
- # See spec/dummy/client/app/startup/serverRegistration.jsx and
36
- # spec/dummy/client/app/startup/ClientRegistration.jsx for examples of this
36
+ # See spec/dummy/client/app/packs/server-bundle.js and
37
+ # spec/dummy/client/app/packs/client-bundle.js for examples of this.
37
38
  #
38
39
  # options:
39
40
  # props: Ruby Hash or JSON string which contains the properties to pass to the react object. Do
@@ -57,14 +58,15 @@ module ReactOnRails
57
58
  server_rendered_html = internal_result[:result]["html"]
58
59
  console_script = internal_result[:result]["consoleReplayScript"]
59
60
 
60
- if server_rendered_html.is_a?(String)
61
+ case server_rendered_html
62
+ when String
61
63
  build_react_component_result_for_server_rendered_string(
62
64
  server_rendered_html: server_rendered_html,
63
65
  component_specification_tag: internal_result[:tag],
64
66
  console_script: console_script,
65
67
  render_options: internal_result[:render_options]
66
68
  )
67
- elsif server_rendered_html.is_a?(Hash)
69
+ when Hash
68
70
  msg = <<~MSG
69
71
  Use react_component_hash (not react_component) to return a Hash to your ruby view code. See
70
72
  https://github.com/shakacode/react_on_rails/blob/master/spec/dummy/client/app/startup/ReactHelmetServerApp.jsx
@@ -152,7 +154,7 @@ module ReactOnRails
152
154
  @registered_stores_defer_render ||= []
153
155
  @registered_stores_defer_render << redux_store_data
154
156
  "YOU SHOULD NOT SEE THIS ON YOUR VIEW -- Uses as a code block, like <% redux_store %> "\
155
- "and not <%= redux store %>"
157
+ "and not <%= redux store %>"
156
158
  else
157
159
  @registered_stores ||= []
158
160
  @registered_stores << redux_store_data
@@ -169,7 +171,7 @@ module ReactOnRails
169
171
  def redux_store_hydration_data
170
172
  return if @registered_stores_defer_render.blank?
171
173
 
172
- @registered_stores_defer_render.reduce("".dup) do |accum, redux_store_data|
174
+ @registered_stores_defer_render.reduce(+"") do |accum, redux_store_data|
173
175
  accum << render_redux_store_data(redux_store_data)
174
176
  end.html_safe
175
177
  end
@@ -181,7 +183,7 @@ module ReactOnRails
181
183
  # Helper method to take javascript expression and returns the output from evaluating it.
182
184
  # If you have more than one line that needs to be executed, wrap it in an IIFE.
183
185
  # JS exceptions are caught and console messages are handled properly.
184
- # Options include:{ prerender:, trace:, raise_on_prerender_error: }
186
+ # Options include:{ prerender:, trace:, raise_on_prerender_error:, throw_js_errors: }
185
187
  def server_render_js(js_expression, options = {})
186
188
  render_options = ReactOnRails::ReactComponent::RenderOptions
187
189
  .new(react_component_name: "generic-js", options: options)
@@ -191,6 +193,8 @@ module ReactOnRails
191
193
  var htmlResult = '';
192
194
  var consoleReplayScript = '';
193
195
  var hasErrors = false;
196
+ var renderingError = null;
197
+ var renderingErrorObject = {};
194
198
 
195
199
  try {
196
200
  htmlResult =
@@ -198,9 +202,17 @@ module ReactOnRails
198
202
  return #{js_expression};
199
203
  })();
200
204
  } catch(e) {
205
+ renderingError = e;
206
+ if (#{render_options.throw_js_errors}) {
207
+ throw e;
208
+ }
201
209
  htmlResult = ReactOnRails.handleError({e: e, name: null,
202
210
  jsCode: '#{escape_javascript(js_expression)}', serverSide: true});
203
211
  hasErrors = true;
212
+ renderingErrorObject = {
213
+ message: renderingError.message,
214
+ stack: renderingError.stack,
215
+ }
204
216
  }
205
217
 
206
218
  consoleReplayScript = ReactOnRails.buildConsoleReplay();
@@ -208,7 +220,8 @@ module ReactOnRails
208
220
  return JSON.stringify({
209
221
  html: htmlResult,
210
222
  consoleReplayScript: consoleReplayScript,
211
- hasErrors: hasErrors
223
+ hasErrors: hasErrors,
224
+ renderingError: renderingErrorObject
212
225
  });
213
226
 
214
227
  })()
@@ -221,9 +234,9 @@ module ReactOnRails
221
234
  console_log_script = result["consoleLogScript"]
222
235
  raw("#{html}#{render_options.replay_console ? console_log_script : ''}")
223
236
  rescue ExecJS::ProgramError => err
224
- raise ReactOnRails::PrerenderError, component_name: "N/A (server_render_js called)",
225
- err: err,
226
- js_code: js_code
237
+ raise ReactOnRails::PrerenderError.new(component_name: "N/A (server_render_js called)",
238
+ err: err,
239
+ js_code: js_code)
227
240
  end
228
241
 
229
242
  def json_safe_and_pretty(hash_or_string)
@@ -231,7 +244,7 @@ module ReactOnRails
231
244
 
232
245
  unless hash_or_string.is_a?(String) || hash_or_string.is_a?(Hash)
233
246
  raise ReactOnRails::Error, "#{__method__} only accepts String or Hash as argument "\
234
- "(#{hash_or_string.class} given)."
247
+ "(#{hash_or_string.class} given)."
235
248
  end
236
249
 
237
250
  json_value = hash_or_string.is_a?(String) ? hash_or_string : hash_or_string.to_json
@@ -243,7 +256,7 @@ module ReactOnRails
243
256
  # second parameter passed to both component and store Render-Functions.
244
257
  # This method can be called from views and from the controller, as `helpers.rails_context`
245
258
  #
246
- # rubocop:disable Metrics/AbcSize
259
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
247
260
  def rails_context(server_side: true)
248
261
  # ALERT: Keep in sync with node_package/src/types/index.ts for the properties of RailsContext
249
262
  @rails_context ||= begin
@@ -254,8 +267,13 @@ module ReactOnRails
254
267
  i18nLocale: I18n.locale,
255
268
  i18nDefaultLocale: I18n.default_locale,
256
269
  rorVersion: ReactOnRails::VERSION,
270
+ # TODO: v13 just use the version if existing
257
271
  rorPro: ReactOnRails::Utils.react_on_rails_pro?
258
272
  }
273
+ if ReactOnRails::Utils.react_on_rails_pro?
274
+ result[:rorProVersion] = ReactOnRails::Utils.react_on_rails_pro_version
275
+ end
276
+
259
277
  if defined?(request) && request.present?
260
278
  # Check for encoding of the request's original_url and try to force-encoding the
261
279
  # URLs as UTF-8. This situation can occur in browsers that do not encode the
@@ -291,7 +309,8 @@ module ReactOnRails
291
309
 
292
310
  @rails_context.merge(serverSide: server_side)
293
311
  end
294
- # rubocop:enable Metrics/AbcSize
312
+
313
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
295
314
 
296
315
  private
297
316
 
@@ -395,7 +414,7 @@ module ReactOnRails
395
414
  # Setup the page_loaded_js, which is the same regardless of prerendering or not!
396
415
  # The reason is that React is smart about not doing extra work if the server rendering did its job.
397
416
  component_specification_tag = content_tag(:script,
398
- json_safe_and_pretty(render_options.props).html_safe,
417
+ json_safe_and_pretty(render_options.client_props).html_safe,
399
418
  type: "application/json",
400
419
  class: "js-react-on-rails-component",
401
420
  "data-component-name" => render_options.react_component_name,
@@ -465,34 +484,34 @@ module ReactOnRails
465
484
  result = ReactOnRails::ServerRenderingPool.server_render_js_with_console_logging(js_code, render_options)
466
485
  rescue StandardError => err
467
486
  # This error came from the renderer
468
- raise ReactOnRails::PrerenderError, component_name: react_component_name,
469
- # Sanitize as this might be browser logged
470
- props: sanitized_props_string(props),
471
- err: err,
472
- js_code: js_code
487
+ raise ReactOnRails::PrerenderError.new(component_name: react_component_name,
488
+ # Sanitize as this might be browser logged
489
+ props: sanitized_props_string(props),
490
+ err: err,
491
+ js_code: js_code)
473
492
  end
474
493
 
475
494
  if result["hasErrors"] && render_options.raise_on_prerender_error
476
495
  # We caught this exception on our backtrace handler
477
- raise ReactOnRails::PrerenderError, component_name: react_component_name,
478
- # Sanitize as this might be browser logged
479
- props: sanitized_props_string(props),
480
- err: nil,
481
- js_code: js_code,
482
- console_messages: result["consoleReplayScript"]
496
+ raise ReactOnRails::PrerenderError.new(component_name: react_component_name,
497
+ # Sanitize as this might be browser logged
498
+ props: sanitized_props_string(props),
499
+ err: nil,
500
+ js_code: js_code,
501
+ console_messages: result["consoleReplayScript"])
483
502
 
484
503
  end
485
504
  result
486
505
  end
487
506
 
488
507
  def initialize_redux_stores
489
- result = <<-JS.dup
508
+ result = +<<-JS
490
509
  ReactOnRails.clearHydratedStores();
491
510
  JS
492
511
 
493
512
  return result unless @registered_stores.present? || @registered_stores_defer_render.present?
494
513
 
495
- declarations = "var reduxProps, store, storeGenerator;\n".dup
514
+ declarations = +"var reduxProps, store, storeGenerator;\n"
496
515
  all_stores = (@registered_stores || []) + (@registered_stores_defer_render || [])
497
516
 
498
517
  result << all_stores.each_with_object(declarations) do |redux_store_data, memo|
@@ -518,6 +537,13 @@ module ReactOnRails
518
537
 
519
538
  controller.is_a?(ActionMailer::Base)
520
539
  end
540
+
541
+ if defined?(ScoutApm)
542
+ include ScoutApm::Tracer
543
+ instrument_method :react_component, type: "ReactOnRails", name: "react_component"
544
+ instrument_method :react_component_hash, type: "ReactOnRails", name: "react_component_hash"
545
+ end
521
546
  end
522
547
  end
523
548
  # rubocop:enable Metrics/ModuleLength
549
+ # rubocop:enable Metrics/MethodLength
@@ -4,7 +4,7 @@ module ReactOnRails
4
4
  class JsonParseError < ::ReactOnRails::Error
5
5
  attr_reader :json
6
6
 
7
- def initialize(parse_error, json)
7
+ def initialize(parse_error:, json:)
8
8
  @json = json
9
9
  @original_error = parse_error
10
10
  super(parse_error.message)
@@ -32,12 +32,12 @@ module ReactOnRails
32
32
  end
33
33
 
34
34
  def exist_files
35
- @exist_files ||= files.select(&File.method(:exist?))
35
+ @exist_files ||= files.select { |file| File.exist?(file) }
36
36
  end
37
37
 
38
38
  def files_are_outdated
39
- latest_yml = locale_files.map(&File.method(:mtime)).max
40
- earliest = exist_files.map(&File.method(:mtime)).min
39
+ latest_yml = locale_files.map { |file| File.mtime(file) }.max
40
+ earliest = exist_files.map { |file| File.mtime(file) }.min
41
41
  latest_yml > earliest
42
42
  end
43
43
 
@@ -54,15 +54,13 @@ module ReactOnRails
54
54
  end
55
55
 
56
56
  def locale_files
57
- @locale_files ||= begin
58
- if i18n_yml_dir.present?
59
- Dir["#{i18n_yml_dir}/**/*.yml"]
60
- else
61
- ReactOnRails::Utils.truthy_presence(
62
- Rails.application && Rails.application.config.i18n.load_path
63
- ).presence
64
- end
65
- end
57
+ @locale_files ||= if i18n_yml_dir.present?
58
+ Dir["#{i18n_yml_dir}/**/*.yml"]
59
+ else
60
+ ReactOnRails::Utils.truthy_presence(
61
+ Rails.application && Rails.application.config.i18n.load_path
62
+ ).presence
63
+ end
66
64
  end
67
65
 
68
66
  def i18n_dir
@@ -87,9 +85,7 @@ module ReactOnRails
87
85
 
88
86
  def generate_file(template, path)
89
87
  result = ERB.new(template).result()
90
- File.open(path, "w") do |f|
91
- f.write(result)
92
- end
88
+ File.write(path, result)
93
89
  end
94
90
 
95
91
  def generate_translations
@@ -5,10 +5,6 @@ require "erb"
5
5
  module ReactOnRails
6
6
  module Locales
7
7
  class ToJs < Base
8
- def initialize
9
- super
10
- end
11
-
12
8
  private
13
9
 
14
10
  def file_format
@@ -5,10 +5,6 @@ require "erb"
5
5
  module ReactOnRails
6
6
  module Locales
7
7
  class ToJson < Base
8
- def initialize
9
- super
10
- end
11
-
12
8
  private
13
9
 
14
10
  def file_format
@@ -46,13 +46,16 @@ module ReactOnRails
46
46
  private
47
47
 
48
48
  def calc_message(component_name, console_messages, err, js_code, props)
49
- message = "ERROR in SERVER PRERENDERING\n".dup
49
+ message = +"ERROR in SERVER PRERENDERING\n"
50
50
  if err
51
51
  message << <<~MSG
52
- Encountered error: \"#{err}\"
52
+ Encountered error:
53
+
54
+ #{err}
55
+
53
56
  MSG
54
57
 
55
- backtrace = err.backtrace.join("\n")
58
+ backtrace = err.backtrace.first(15).join("\n")
56
59
  else
57
60
  backtrace = nil
58
61
  end
@@ -11,6 +11,7 @@ module ReactOnRails
11
11
 
12
12
  NO_PROPS = {}.freeze
13
13
 
14
+ # TODO: remove the required for named params
14
15
  def initialize(react_component_name: required("react_component_name"), options: required("options"))
15
16
  @react_component_name = react_component_name.camelize
16
17
  @options = options
@@ -18,12 +19,30 @@ module ReactOnRails
18
19
 
19
20
  attr_reader :react_component_name
20
21
 
22
+ def throw_js_errors
23
+ options.fetch(:throw_js_errors, false)
24
+ end
25
+
21
26
  def props
22
27
  options.fetch(:props) { NO_PROPS }
23
28
  end
24
29
 
30
+ def client_props
31
+ props_extension = ReactOnRails.configuration.rendering_props_extension
32
+ if props_extension.present?
33
+ if props_extension.respond_to?(:adjust_props_for_client_side_hydration)
34
+ return props_extension.adjust_props_for_client_side_hydration(react_component_name,
35
+ props.clone)
36
+ end
37
+
38
+ raise ReactOnRails::Error, "ReactOnRails: your rendering_props_extension module is missing the "\
39
+ "required adjust_props_for_client_side_hydration method & can not be used"
40
+ end
41
+ props
42
+ end
43
+
25
44
  def random_dom_id
26
- retrieve_key(:random_dom_id)
45
+ retrieve_configuration_value_for(:random_dom_id)
27
46
  end
28
47
 
29
48
  def dom_id
@@ -49,23 +68,23 @@ module ReactOnRails
49
68
  end
50
69
 
51
70
  def prerender
52
- retrieve_key(:prerender)
71
+ retrieve_configuration_value_for(:prerender)
53
72
  end
54
73
 
55
74
  def trace
56
- retrieve_key(:trace)
75
+ retrieve_configuration_value_for(:trace)
57
76
  end
58
77
 
59
78
  def replay_console
60
- retrieve_key(:replay_console)
79
+ retrieve_configuration_value_for(:replay_console)
61
80
  end
62
81
 
63
82
  def raise_on_prerender_error
64
- retrieve_key(:raise_on_prerender_error)
83
+ retrieve_configuration_value_for(:raise_on_prerender_error)
65
84
  end
66
85
 
67
86
  def logging_on_server
68
- retrieve_key(:logging_on_server)
87
+ retrieve_configuration_value_for(:logging_on_server)
69
88
  end
70
89
 
71
90
  def to_s
@@ -76,6 +95,10 @@ module ReactOnRails
76
95
  options[key]
77
96
  end
78
97
 
98
+ def set_option(key, value)
99
+ options[key] = value
100
+ end
101
+
79
102
  private
80
103
 
81
104
  attr_reader :options
@@ -88,7 +111,7 @@ module ReactOnRails
88
111
  "#{base_dom_id}-#{SecureRandom.uuid}"
89
112
  end
90
113
 
91
- def retrieve_key(key)
114
+ def retrieve_configuration_value_for(key)
92
115
  options.fetch(key) do
93
116
  ReactOnRails.configuration.public_send(key)
94
117
  end
@@ -18,6 +18,19 @@ module ReactOnRails
18
18
  react_component_name: nil,
19
19
  render_options: nil
20
20
  )
21
+
22
+ config_server_bundle_js = ReactOnRails.configuration.server_bundle_js_file
23
+
24
+ if render_options.prerender == true && config_server_bundle_js.blank?
25
+ msg = <<~MSG
26
+ The `prerender` option to allow Server Side Rendering is marked as true but the ReactOnRails configuration
27
+ for `server_bundle_js_file` is nil or not present in `config/initializers/react_on_rails.rb`.
28
+ Set `config.server_bundle_js_file` to your javascript bundle to allow server side rendering.
29
+ Read more at https://www.shakacode.com/react-on-rails/docs/guides/react-server-rendering/
30
+ MSG
31
+ raise ReactOnRails::Error, msg
32
+ end
33
+
21
34
  js_code_renderer.render(props_string, rails_context, redux_stores, react_component_name, render_options)
22
35
  end
23
36
 
@@ -47,6 +47,7 @@ module ReactOnRails
47
47
  # Note, js_code does not have to be based on React.
48
48
  # js_code MUST RETURN json stringify Object
49
49
  # Calling code will probably call 'html_safe' on return value before rendering to the view.
50
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/PerceivedComplexity
50
51
  def exec_server_render_js(js_code, render_options, js_evaluator = nil)
51
52
  js_evaluator ||= self
52
53
  if render_options.trace
@@ -55,28 +56,42 @@ module ReactOnRails
55
56
  "tmp/server-generated-#{@file_index % 10}.js")
56
57
  @file_index += 1
57
58
  end
58
- json_string = js_evaluator.eval_js(js_code, render_options)
59
+ begin
60
+ json_string = js_evaluator.eval_js(js_code, render_options)
61
+ rescue StandardError => err
62
+ msg = <<~MSG
63
+ Error evaluating server bundle. Check your webpack configuration.
64
+ ===============================================================
65
+ Caught error:
66
+ #{err.message}
67
+ ===============================================================
68
+ MSG
69
+
70
+ if err.message.include?("ReferenceError: self is not defined")
71
+ msg << "\nError indicates that you may have code-splitting incorrectly enabled.\n"
72
+ end
73
+ raise ReactOnRails::Error, msg, err.backtrace
74
+ end
59
75
  result = nil
60
76
  begin
61
77
  result = JSON.parse(json_string)
62
78
  rescue JSON::ParserError => e
63
- raise ReactOnRails::JsonParseError.new(e, json_string)
79
+ raise ReactOnRails::JsonParseError.new(parse_error: e, json: json_string)
64
80
  end
65
81
 
66
82
  if render_options.logging_on_server
67
83
  console_script = result["consoleReplayScript"]
68
84
  console_script_lines = console_script.split("\n")
69
85
  console_script_lines = console_script_lines[2..-2]
70
- re = /console\.(log|error)\.apply\(console, \["\[SERVER\] (?<msg>.*)"\]\);/
71
- if console_script_lines
72
- console_script_lines.each do |line|
73
- match = re.match(line)
74
- Rails.logger.info { "[react_on_rails] #{match[:msg]}" } if match
75
- end
86
+ re = /console\.(?:log|error)\.apply\(console, \["\[SERVER\] (?<msg>.*)"\]\);/
87
+ console_script_lines&.each do |line|
88
+ match = re.match(line)
89
+ Rails.logger.info { "[react_on_rails] #{match[:msg]}" } if match
76
90
  end
77
91
  end
78
92
  result
79
93
  end
94
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/PerceivedComplexity
80
95
 
81
96
  def trace_js_code_used(msg, js_code, file_name = "tmp/server-generated.js", force: false)
82
97
  return unless ReactOnRails.configuration.trace || force
@@ -133,15 +148,15 @@ module ReactOnRails
133
148
  if ReactOnRails.configuration.trace
134
149
  Rails.logger.info do
135
150
  "[react_on_rails] Created JavaScript context with file "\
136
- "#{ReactOnRails::Utils.server_bundle_js_file_path}"
151
+ "#{ReactOnRails::Utils.server_bundle_js_file_path}"
137
152
  end
138
153
  end
139
154
  ExecJS.compile(base_js_code)
140
155
  rescue StandardError => e
141
156
  msg = "ERROR when compiling base_js_code! "\
142
- "See file #{file_name} to "\
143
- "correlate line numbers of error. Error is\n\n#{e.message}"\
144
- "\n\n#{e.backtrace.join("\n")}"
157
+ "See file #{file_name} to "\
158
+ "correlate line numbers of error. Error is\n\n#{e.message}"\
159
+ "\n\n#{e.backtrace.join("\n")}"
145
160
  Rails.logger.error(msg)
146
161
  trace_js_code_used("Error when compiling JavaScript code for the context.", base_js_code,
147
162
  file_name, force: true)
@@ -150,6 +165,12 @@ module ReactOnRails
150
165
  end
151
166
 
152
167
  def execjs_timer_polyfills
168
+ if ReactOnRails::Utils.react_on_rails_pro? &&
169
+ ReactOnRailsPro.configuration.respond_to?(:include_execjs_polyfills) &&
170
+ ReactOnRailsPro.configuration.include_execjs_polyfills == false
171
+ return ""
172
+ end
173
+
153
174
  <<~JS
154
175
  function getStackTrace () {
155
176
  var stack;
@@ -180,15 +201,17 @@ module ReactOnRails
180
201
  def undefined_for_exec_js_logging(function_name)
181
202
  if ReactOnRails.configuration.trace
182
203
  "console.error('[React on Rails Rendering] #{function_name} is not defined for server rendering.');\n"\
183
- " console.error(getStackTrace().join('\\n'));"
204
+ " console.error(getStackTrace().join('\\n'));"
184
205
  else
185
206
  ""
186
207
  end
187
208
  end
188
209
 
189
210
  # Reimplement console methods for replaying on the client
211
+ # Save a handle to the original console if needed.
190
212
  def console_polyfill
191
213
  <<~JS
214
+ var debugConsole = console;
192
215
  var console = { history: [] };
193
216
  ['error', 'log', 'info', 'warn'].forEach(function (level) {
194
217
  console[level] = function () {
@@ -202,6 +225,11 @@ module ReactOnRails
202
225
  JS
203
226
  end
204
227
 
228
+ if defined?(ScoutApm)
229
+ include ScoutApm::Tracer
230
+ instrument_method :exec_server_render_js, type: "ReactOnRails", name: "ExecJs React Server Rendering"
231
+ end
232
+
205
233
  private
206
234
 
207
235
  def file_url_to_string(url)
@@ -8,7 +8,7 @@ module ReactOnRails
8
8
  def compile_assets
9
9
  if ReactOnRails.configuration.build_test_command.blank?
10
10
  msg = <<~MSG
11
- You are using the React on Rails test helper.
11
+ You are using the React on Rails test helper.
12
12
  Either you used:
13
13
  ReactOnRails::TestHelper.configure_rspec_to_compile_assets or
14
14
  ReactOnRails::TestHelper.ensure_assets_compiled
@@ -66,10 +66,10 @@ module ReactOnRails
66
66
  file_list = make_file_list(make_globs(generated_assets_full_path)).to_ary
67
67
  puts "V" * 80
68
68
  puts "Please define config.webpack_generated_files (array) so the test helper knows "\
69
- "which files are required. If you are using webpacker, you typically need to only "\
70
- "include 'manifest.json'."
69
+ "which files are required. If you are using webpacker, you typically need to only "\
70
+ "include 'manifest.json'."
71
71
  puts "Detected the possible following files to check for webpack compilation in "\
72
- "#{generated_assets_full_path}"
72
+ "#{generated_assets_full_path}"
73
73
  puts file_list.join("\n")
74
74
  puts "^" * 80
75
75
  file_list
@@ -52,6 +52,7 @@ module ReactOnRails
52
52
  # don't provide one.
53
53
  # webpack_generated_files List of files to check for up-to-date-status, defaulting to
54
54
  # webpack_generated_files in your configuration
55
+ # rubocop:disable Metrics/CyclomaticComplexity
55
56
  def self.ensure_assets_compiled(webpack_assets_status_checker: nil,
56
57
  webpack_assets_compiler: nil,
57
58
  source_path: nil,
@@ -71,8 +72,8 @@ module ReactOnRails
71
72
  unless @printed_once
72
73
  puts
73
74
  puts "====> React On Rails: Checking files in "\
74
- "#{webpack_assets_status_checker.generated_assets_full_path} for "\
75
- "outdated/missing bundles based on source_path #{source_path}"
75
+ "#{webpack_assets_status_checker.generated_assets_full_path} for "\
76
+ "outdated/missing bundles based on source_path #{source_path}"
76
77
  puts
77
78
  @printed_once = true
78
79
 
@@ -96,4 +97,5 @@ module ReactOnRails
96
97
  ).call
97
98
  end
98
99
  end
100
+ # rubocop:enable Metrics/CyclomaticComplexity
99
101
  end