react_on_rails 11.0.5 → 13.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (199) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +338 -0
  3. data/.eslintignore +2 -1
  4. data/.eslintrc +32 -3
  5. data/.github/FUNDING.yml +1 -0
  6. data/.github/ISSUE_TEMPLATE/bug_report.md +23 -0
  7. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  8. data/.github/PULL_REQUEST_TEMPLATE.md +19 -0
  9. data/.github/workflows/lint-js-and-ruby.yml +54 -0
  10. data/.github/workflows/main.yml +183 -0
  11. data/.github/workflows/package-js-tests.yml +35 -0
  12. data/.github/workflows/rspec-package-specs.yml +46 -0
  13. data/.gitignore +3 -4
  14. data/.prettierignore +14 -0
  15. data/.prettierrc +20 -0
  16. data/.rubocop.yml +76 -34
  17. data/.travis.yml +15 -22
  18. data/CHANGELOG.md +443 -55
  19. data/CONTRIBUTING.md +62 -80
  20. data/Gemfile +1 -35
  21. data/Gemfile.development_dependencies +50 -0
  22. data/KUDOS.md +4 -1
  23. data/{docs/LICENSE.md → LICENSE.md} +1 -1
  24. data/NEWS.md +14 -4
  25. data/REACT-ON-RAILS-PRO-LICENSE +95 -0
  26. data/README.md +107 -802
  27. data/Rakefile +1 -8
  28. data/SUMMARY.md +51 -29
  29. data/book.json +5 -5
  30. data/docs/{basics/generator.md → additional-details/generator-details.md} +5 -13
  31. data/docs/{basics/installation-overview.md → additional-details/manual-installation-overview.md} +9 -14
  32. data/docs/{basics → additional-details}/migrating-from-react-rails.md +1 -1
  33. data/docs/additional-details/recommended-project-structure.md +69 -0
  34. data/docs/additional-details/tips-for-usage-with-sp6.md +15 -0
  35. data/docs/additional-details/upgrade-webpacker-v3-to-v4.md +10 -0
  36. data/docs/api/javascript-api.md +35 -6
  37. data/docs/api/redux-store-api.md +102 -0
  38. data/docs/api/view-helpers-api.md +133 -0
  39. data/docs/contributor-info/errors-with-hooks.md +45 -0
  40. data/docs/contributor-info/linters.md +5 -6
  41. data/docs/contributor-info/pull-requests.md +42 -0
  42. data/docs/contributor-info/releasing.md +1 -1
  43. data/docs/deployment/heroku-deployment.md +39 -0
  44. data/docs/getting-started.md +196 -0
  45. data/docs/guides/client-vs-server-rendering.md +27 -0
  46. data/docs/guides/configuration.md +289 -0
  47. data/docs/guides/deployment.md +5 -0
  48. data/docs/guides/file-system-based-automated-bundle-generation.md +197 -0
  49. data/docs/guides/hmr-and-hot-reloading-with-the-webpack-dev-server.md +104 -0
  50. data/docs/guides/how-react-on-rails-works.md +44 -0
  51. data/docs/guides/how-to-conditionally-server-render-based-on-device-type.md +40 -0
  52. data/docs/guides/how-to-use-different-files-for-client-and-server-rendering.md +98 -0
  53. data/docs/guides/i18n.md +87 -0
  54. data/docs/guides/installation-into-an-existing-rails-app.md +66 -0
  55. data/docs/guides/minitest-configuration.md +31 -0
  56. data/docs/guides/rails-webpacker-react-integration-options.md +213 -0
  57. data/docs/guides/react-on-rails-overview.md +29 -0
  58. data/docs/guides/react-server-rendering.md +32 -0
  59. data/docs/guides/render-functions-and-railscontext.md +205 -0
  60. data/docs/guides/rspec-configuration.md +73 -0
  61. data/docs/guides/tutorial.md +371 -0
  62. data/docs/{basics → guides}/upgrading-react-on-rails.md +126 -3
  63. data/docs/guides/webpack-configuration.md +42 -0
  64. data/docs/home.md +23 -0
  65. data/docs/javascript/asset-pipeline.md +12 -0
  66. data/docs/{additional-reading → javascript}/code-splitting.md +21 -11
  67. data/docs/javascript/converting-from-custom-webpack-config-to-rails-webpacker-config.md +10 -0
  68. data/docs/javascript/credits.md +10 -0
  69. data/docs/{additional-reading → javascript}/images.md +5 -6
  70. data/docs/javascript/react-helmet.md +100 -0
  71. data/docs/javascript/react-router.md +90 -0
  72. data/docs/{additional-reading → javascript}/server-rendering-tips.md +15 -12
  73. data/docs/javascript/troubleshooting-when-using-shakapacker.md +77 -0
  74. data/docs/{additional-reading → javascript}/webpack.md +2 -2
  75. data/docs/misc/articles.md +20 -0
  76. data/docs/misc/doctrine.md +5 -6
  77. data/docs/outdated/deferred-rendering.md +39 -0
  78. data/docs/{additional-reading → outdated}/rails-assets-relative-paths.md +4 -4
  79. data/docs/{additional-reading → outdated}/rails-assets.md +12 -20
  80. data/docs/{misc → outdated}/rails3.md +2 -2
  81. data/docs/rails/convert-rails-5-api-only-app.md +19 -0
  82. data/docs/rails/rails-engine-integration.md +32 -0
  83. data/docs/{additional-reading → rails}/rails_view_rendering_from_inline_javascript.md +2 -1
  84. data/docs/{additional-reading → rails}/turbolinks.md +13 -1
  85. data/docs/react-on-rails-pro/react-on-rails-pro.md +43 -0
  86. data/docs/testimonials/hvmn.md +25 -0
  87. data/docs/testimonials/resortpass.md +13 -0
  88. data/docs/testimonials/testimonials.md +28 -0
  89. data/jest.config.js +4 -0
  90. data/lib/generators/USAGE +1 -1
  91. data/lib/generators/react_on_rails/adapt_for_older_shakapacker_generator.rb +41 -0
  92. data/lib/generators/react_on_rails/base_generator.rb +55 -43
  93. data/lib/generators/react_on_rails/bin/dev +30 -0
  94. data/lib/generators/react_on_rails/bin/dev-static +30 -0
  95. data/lib/generators/react_on_rails/dev_tests_generator.rb +4 -3
  96. data/lib/generators/react_on_rails/generator_helper.rb +8 -6
  97. data/lib/generators/react_on_rails/generator_messages.rb +40 -0
  98. data/lib/generators/react_on_rails/install_generator.rb +37 -0
  99. data/lib/generators/react_on_rails/templates/.eslintrc +3 -1
  100. data/lib/generators/react_on_rails/templates/base/base/Procfile.dev +4 -6
  101. data/lib/generators/react_on_rails/templates/base/base/Procfile.dev-static +9 -0
  102. data/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx +21 -40
  103. data/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorld.module.css +4 -0
  104. data/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorldServer.js +5 -0
  105. data/lib/generators/react_on_rails/templates/base/base/app/javascript/packs/server-bundle.js +8 -0
  106. data/lib/generators/react_on_rails/templates/base/base/app/views/layouts/hello_world.html.erb +2 -1
  107. data/lib/generators/react_on_rails/templates/base/base/babel.config.js.tt +32 -0
  108. data/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb +20 -4
  109. data/lib/generators/react_on_rails/templates/base/base/config/shakapacker.yml +62 -0
  110. data/lib/generators/react_on_rails/templates/base/base/config/webpack/clientWebpackConfig.js.tt +17 -0
  111. data/lib/generators/react_on_rails/templates/base/base/config/webpack/commonWebpackConfig.js.tt +17 -0
  112. data/lib/generators/react_on_rails/templates/base/base/config/webpack/development.js.tt +25 -0
  113. data/lib/generators/react_on_rails/templates/base/base/config/webpack/production.js.tt +9 -0
  114. data/lib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt +117 -0
  115. data/lib/generators/react_on_rails/templates/base/base/config/webpack/test.js.tt +9 -0
  116. data/lib/generators/react_on_rails/templates/base/base/config/webpack/webpack.config.js.tt +15 -0
  117. data/lib/generators/react_on_rails/templates/base/base/config/webpack/webpackConfig.js.tt +36 -0
  118. data/lib/generators/react_on_rails/templates/dev_tests/spec/rails_helper.rb +8 -2
  119. data/lib/generators/react_on_rails/templates/dev_tests/spec/simplecov_helper.rb +1 -1
  120. data/lib/generators/react_on_rails/templates/dev_tests/spec/{features → system}/hello_world_spec.rb +2 -2
  121. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx +6 -9
  122. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/store/helloWorldStore.js +1 -3
  123. data/lib/react_on_rails/configuration.rb +198 -145
  124. data/lib/react_on_rails/error.rb +2 -0
  125. data/lib/react_on_rails/git_utils.rb +5 -3
  126. data/lib/react_on_rails/{react_on_rails_helper.rb → helper.rb} +201 -190
  127. data/lib/react_on_rails/json_output.rb +1 -1
  128. data/lib/react_on_rails/json_parse_error.rb +28 -0
  129. data/lib/react_on_rails/locales/base.rb +169 -0
  130. data/lib/react_on_rails/locales/to_js.rb +33 -0
  131. data/lib/react_on_rails/locales/to_json.rb +23 -0
  132. data/lib/react_on_rails/packs_generator.rb +234 -0
  133. data/lib/react_on_rails/prerender_error.rb +35 -27
  134. data/lib/react_on_rails/react_component/render_options.rb +64 -9
  135. data/lib/react_on_rails/server_rendering_js_code.rb +55 -0
  136. data/lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb +126 -76
  137. data/lib/react_on_rails/server_rendering_pool.rb +0 -1
  138. data/lib/react_on_rails/test_helper/ensure_assets_compiled.rb +9 -8
  139. data/lib/react_on_rails/test_helper/webpack_assets_compiler.rb +17 -0
  140. data/lib/react_on_rails/test_helper/webpack_assets_status_checker.rb +13 -12
  141. data/lib/react_on_rails/test_helper.rb +24 -3
  142. data/lib/react_on_rails/utils.rb +94 -25
  143. data/lib/react_on_rails/version.rb +1 -1
  144. data/lib/react_on_rails/version_checker.rb +5 -1
  145. data/lib/react_on_rails/version_syntax_converter.rb +14 -12
  146. data/lib/react_on_rails/webpacker_utils.rb +105 -5
  147. data/lib/react_on_rails.rb +8 -2
  148. data/lib/tasks/assets.rake +28 -60
  149. data/lib/tasks/generate_packs.rake +11 -0
  150. data/lib/tasks/locale.rake +5 -4
  151. data/package-scripts.yml +49 -0
  152. data/package.json +52 -47
  153. data/rakelib/docker.rake +0 -5
  154. data/rakelib/dummy_apps.rake +5 -8
  155. data/rakelib/example_type.rb +12 -3
  156. data/rakelib/examples.rake +5 -4
  157. data/rakelib/lint.rake +5 -16
  158. data/rakelib/node_package.rake +2 -2
  159. data/rakelib/release.rake +37 -23
  160. data/rakelib/run_rspec.rake +16 -44
  161. data/rakelib/task_helpers.rb +16 -4
  162. data/react_on_rails.gemspec +6 -22
  163. data/tsconfig.json +14 -0
  164. data/webpackConfigLoader.js +5 -4
  165. data/yarn.lock +5935 -3106
  166. metadata +122 -272
  167. data/Gemfile.rails32 +0 -74
  168. data/docs/additional-reading/asset-pipeline.md +0 -20
  169. data/docs/additional-reading/babel.md +0 -5
  170. data/docs/additional-reading/caching-and-performance.md +0 -4
  171. data/docs/additional-reading/heroku-deployment.md +0 -92
  172. data/docs/additional-reading/hot-reloading-rails-development.md +0 -57
  173. data/docs/additional-reading/node-server-rendering.md +0 -5
  174. data/docs/additional-reading/rails-engine-integration.md +0 -34
  175. data/docs/additional-reading/react-helmet.md +0 -80
  176. data/docs/additional-reading/react-router.md +0 -113
  177. data/docs/additional-reading/recommended-project-structure.md +0 -49
  178. data/docs/additional-reading/rspec-configuration.md +0 -56
  179. data/docs/additional-reading/webpack-dev-server.md +0 -15
  180. data/docs/api/ruby-api-hot-reload-view-helpers.md +0 -44
  181. data/docs/api/ruby-api.md +0 -8
  182. data/docs/basics/configuration.md +0 -163
  183. data/docs/basics/i18n.md +0 -77
  184. data/docs/tutorial.md +0 -220
  185. data/lib/generators/react_on_rails/templates/base/base/Procfile.dev-server +0 -12
  186. data/lib/react_on_rails/assets_precompile.rb +0 -150
  187. data/lib/react_on_rails/locales_to_js.rb +0 -134
  188. data/ruby-lint.yml +0 -25
  189. /data/docs/{additional-reading → additional-details}/updating-dependencies.md +0 -0
  190. /data/docs/{additional-reading → deployment}/elastic-beanstalk.md +0 -0
  191. /data/docs/{additional-reading → javascript}/angular-js-integration-migration.md +0 -0
  192. /data/docs/{additional-reading → javascript}/capistrano-deployment.md +0 -0
  193. /data/docs/{additional-reading → javascript}/foreman-issues.md +0 -0
  194. /data/docs/{additional-reading → javascript}/node-dependencies-and-npm.md +0 -0
  195. /data/docs/{additional-reading → javascript}/react-and-redux.md +0 -0
  196. /data/docs/{additional-reading → javascript}/troubleshooting-when-using-webpacker.md +0 -0
  197. /data/docs/{additional-reading → javascript}/webpack-v1-notes.md +0 -0
  198. /data/docs/{coding-style → misc}/style.md +0 -0
  199. /data/docs/{additional-reading → misc}/tips.md +0 -0
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ReactOnRails
4
+ module ServerRenderingJsCode
5
+ class << self
6
+ def js_code_renderer
7
+ @js_code_renderer ||= if ReactOnRails::Utils.react_on_rails_pro?
8
+ ReactOnRailsPro::ServerRenderingJsCode
9
+ else
10
+ self
11
+ end
12
+ end
13
+
14
+ def server_rendering_component_js_code(
15
+ props_string: nil,
16
+ rails_context: nil,
17
+ redux_stores: nil,
18
+ react_component_name: nil,
19
+ render_options: nil
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
+
34
+ js_code_renderer.render(props_string, rails_context, redux_stores, react_component_name, render_options)
35
+ end
36
+
37
+ def render(props_string, rails_context, redux_stores, react_component_name, render_options)
38
+ <<-JS
39
+ (function() {
40
+ var railsContext = #{rails_context};
41
+ #{redux_stores}
42
+ var props = #{props_string};
43
+ return ReactOnRails.serverRenderReactComponent({
44
+ name: '#{react_component_name}',
45
+ domNodeId: '#{render_options.dom_id}',
46
+ props: props,
47
+ trace: #{render_options.trace},
48
+ railsContext: railsContext
49
+ });
50
+ })()
51
+ JS
52
+ end
53
+ end
54
+ end
55
+ end
@@ -1,10 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "open-uri"
4
+ require "execjs"
4
5
 
5
6
  module ReactOnRails
6
7
  module ServerRenderingPool
8
+ # rubocop:disable Metrics/ClassLength
7
9
  class RubyEmbeddedJavaScript
10
+ # rubocop:enable Metrics/ClassLength
8
11
  class << self
9
12
  def reset_pool
10
13
  options = {
@@ -17,12 +20,17 @@ module ReactOnRails
17
20
  def reset_pool_if_server_bundle_was_modified
18
21
  return unless ReactOnRails.configuration.development_mode
19
22
 
20
- file_mtime = File.mtime(ReactOnRails::Utils.server_bundle_js_file_path)
21
- @server_bundle_timestamp ||= file_mtime
22
- return if @server_bundle_timestamp == file_mtime
23
+ if ReactOnRails::Utils.server_bundle_path_is_http?
24
+ return if @server_bundle_url == ReactOnRails::Utils.server_bundle_js_file_path
23
25
 
24
- @server_bundle_timestamp = file_mtime
26
+ @server_bundle_url = ReactOnRails::Utils.server_bundle_js_file_path
27
+ else
28
+ file_mtime = File.mtime(ReactOnRails::Utils.server_bundle_js_file_path)
29
+ @server_bundle_timestamp ||= file_mtime
30
+ return if @server_bundle_timestamp == file_mtime
25
31
 
32
+ @server_bundle_timestamp = file_mtime
33
+ end
26
34
  ReactOnRails::ServerRenderingPool.reset_pool
27
35
  end
28
36
 
@@ -39,6 +47,7 @@ module ReactOnRails
39
47
  # Note, js_code does not have to be based on React.
40
48
  # js_code MUST RETURN json stringify Object
41
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
42
51
  def exec_server_render_js(js_code, render_options, js_evaluator = nil)
43
52
  js_evaluator ||= self
44
53
  if render_options.trace
@@ -47,26 +56,46 @@ module ReactOnRails
47
56
  "tmp/server-generated-#{@file_index % 10}.js")
48
57
  @file_index += 1
49
58
  end
50
- json_string = js_evaluator.eval_js(js_code)
51
- result = JSON.parse(json_string)
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
75
+ result = nil
76
+ begin
77
+ result = JSON.parse(json_string)
78
+ rescue JSON::ParserError => e
79
+ raise ReactOnRails::JsonParseError.new(parse_error: e, json: json_string)
80
+ end
52
81
 
53
82
  if render_options.logging_on_server
54
83
  console_script = result["consoleReplayScript"]
55
84
  console_script_lines = console_script.split("\n")
56
85
  console_script_lines = console_script_lines[2..-2]
57
- re = /console\.(log|error)\.apply\(console, \["\[SERVER\] (?<msg>.*)"\]\);/
58
- if console_script_lines
59
- console_script_lines.each do |line|
60
- match = re.match(line)
61
- Rails.logger.info { "[react_on_rails] #{match[:msg]}" } if match
62
- 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
63
90
  end
64
91
  end
65
92
  result
66
93
  end
94
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/PerceivedComplexity
67
95
 
68
96
  def trace_js_code_used(msg, js_code, file_name = "tmp/server-generated.js", force: false)
69
97
  return unless ReactOnRails.configuration.trace || force
98
+
70
99
  # Set to anything to print generated code.
71
100
  File.write(file_name, js_code)
72
101
  msg = <<-MSG.strip_heredoc
@@ -82,45 +111,50 @@ module ReactOnRails
82
111
  end
83
112
  end
84
113
 
85
- def eval_js(js_code)
114
+ def eval_js(js_code, _render_options)
86
115
  @js_context_pool.with do |js_context|
87
- result = js_context.eval(js_code)
88
- js_context.eval("console.history = []")
89
- result
116
+ js_context.eval(js_code)
90
117
  end
91
118
  end
92
119
 
93
- def create_js_context
94
- return if ReactOnRails.configuration.server_bundle_js_file.blank?
95
-
120
+ def read_bundle_js_code
96
121
  server_js_file = ReactOnRails::Utils.server_bundle_js_file_path
97
-
98
- begin
99
- bundle_js_code = File.read(server_js_file)
100
- rescue StandardError => e
101
- msg = "You specified server rendering JS file: #{server_js_file}, but it cannot be "\
122
+ if ReactOnRails::Utils.server_bundle_path_is_http?
123
+ file_url_to_string(server_js_file)
124
+ else
125
+ File.read(server_js_file)
126
+ end
127
+ rescue StandardError => e
128
+ msg = "You specified server rendering JS file: #{server_js_file}, but it cannot be "\
102
129
  "read. You may set the server_bundle_js_file in your configuration to be \"\" to "\
103
130
  "avoid this warning.\nError is: #{e}"
104
- raise ReactOnRails::Error, msg
105
- end
106
- # rubocop:disable Layout/IndentHeredoc
107
- base_js_code = <<-JS
108
- #{console_polyfill}
109
- #{execjs_timer_polyfills}
110
- #{bundle_js_code};
131
+ raise ReactOnRails::Error, msg
132
+ end
133
+
134
+ def create_js_context
135
+ return if ReactOnRails.configuration.server_bundle_js_file.blank?
136
+
137
+ bundle_js_code = read_bundle_js_code
138
+ base_js_code = <<~JS
139
+ #{console_polyfill}
140
+ #{execjs_timer_polyfills}
141
+ #{bundle_js_code};
111
142
  JS
112
- # rubocop:enable Layout/IndentHeredoc
143
+
113
144
  file_name = "tmp/base_js_code.js"
114
145
  begin
115
146
  if ReactOnRails.configuration.trace
116
- Rails.logger.info { "[react_on_rails] Created JavaScript context with file #{server_js_file}" }
147
+ Rails.logger.info do
148
+ "[react_on_rails] Created JavaScript context with file "\
149
+ "#{ReactOnRails::Utils.server_bundle_js_file_path}"
150
+ end
117
151
  end
118
152
  ExecJS.compile(base_js_code)
119
153
  rescue StandardError => e
120
154
  msg = "ERROR when compiling base_js_code! "\
121
- "See file #{file_name} to "\
122
- "correlate line numbers of error. Error is\n\n#{e.message}"\
123
- "\n\n#{e.backtrace.join("\n")}"
155
+ "See file #{file_name} to "\
156
+ "correlate line numbers of error. Error is\n\n#{e.message}"\
157
+ "\n\n#{e.backtrace.join("\n")}"
124
158
  Rails.logger.error(msg)
125
159
  trace_js_code_used("Error when compiling JavaScript code for the context.", base_js_code,
126
160
  file_name, force: true)
@@ -129,60 +163,76 @@ module ReactOnRails
129
163
  end
130
164
 
131
165
  def execjs_timer_polyfills
132
- # rubocop:disable Layout/IndentHeredoc
133
- <<-JS
134
- function getStackTrace () {
135
- var stack;
136
- try {
137
- throw new Error('');
138
- }
139
- catch (error) {
140
- stack = error.stack || '';
141
- }
142
- stack = stack.split('\\n').map(function (line) { return line.trim(); });
143
- return stack.splice(stack[0] == 'Error' ? 2 : 1);
144
- }
145
-
146
- function setInterval() {
147
- #{undefined_for_exec_js_logging('setInterval')}
148
- }
149
-
150
- function setTimeout() {
151
- #{undefined_for_exec_js_logging('setTimeout')}
152
- }
153
-
154
- function clearTimeout() {
155
- #{undefined_for_exec_js_logging('clearTimeout')}
156
- }
166
+ <<~JS
167
+ function getStackTrace () {
168
+ var stack;
169
+ try {
170
+ throw new Error('');
171
+ }
172
+ catch (error) {
173
+ stack = error.stack || '';
174
+ }
175
+ stack = stack.split('\\n').map(function (line) { return line.trim(); });
176
+ return stack.splice(stack[0] == 'Error' ? 2 : 1);
177
+ }
178
+
179
+ function setInterval() {
180
+ #{undefined_for_exec_js_logging('setInterval')}
181
+ }
182
+
183
+ function setTimeout() {
184
+ #{undefined_for_exec_js_logging('setTimeout')}
185
+ }
186
+
187
+ function clearTimeout() {
188
+ #{undefined_for_exec_js_logging('clearTimeout')}
189
+ }
157
190
  JS
158
- # rubocop:enable Layout/IndentHeredoc
159
191
  end
160
192
 
161
193
  def undefined_for_exec_js_logging(function_name)
162
194
  if ReactOnRails.configuration.trace
163
195
  "console.error('[React on Rails Rendering] #{function_name} is not defined for server rendering.');\n"\
164
- " console.error(getStackTrace().join('\\n'));"
196
+ " console.error(getStackTrace().join('\\n'));"
165
197
  else
166
198
  ""
167
199
  end
168
200
  end
169
201
 
170
202
  # Reimplement console methods for replaying on the client
203
+ # Save a handle to the original console if needed.
171
204
  def console_polyfill
172
- # rubocop:disable Layout/IndentHeredoc
173
- <<-JS
174
- var console = { history: [] };
175
- ['error', 'log', 'info', 'warn'].forEach(function (level) {
176
- console[level] = function () {
177
- var argArray = Array.prototype.slice.call(arguments);
178
- if (argArray.length > 0) {
179
- argArray[0] = '[SERVER] ' + argArray[0];
180
- }
181
- console.history.push({level: level, arguments: argArray});
182
- };
183
- });
205
+ <<~JS
206
+ var debugConsole = console;
207
+ var console = { history: [] };
208
+ ['error', 'log', 'info', 'warn'].forEach(function (level) {
209
+ console[level] = function () {
210
+ var argArray = Array.prototype.slice.call(arguments);
211
+ if (argArray.length > 0) {
212
+ argArray[0] = '[SERVER] ' + argArray[0];
213
+ }
214
+ console.history.push({level: level, arguments: argArray});
215
+ };
216
+ });
184
217
  JS
185
- # rubocop:enable Layout/IndentHeredoc
218
+ end
219
+
220
+ if defined?(ScoutApm)
221
+ include ScoutApm::Tracer
222
+ instrument_method :exec_server_render_js, type: "ReactOnRails", name: "ExecJs React Server Rendering"
223
+ end
224
+
225
+ private
226
+
227
+ def file_url_to_string(url)
228
+ response = Net::HTTP.get_response(URI.parse(url))
229
+ content_type_header = response["content-type"]
230
+ match = content_type_header.match(/\A.*; charset=(?<encoding>.*)\z/)
231
+ encoding_type = match[:encoding]
232
+ response.body.force_encoding(encoding_type)
233
+ rescue StandardError => e
234
+ msg = "file_url_to_string #{url} failed\nError is: #{e}"
235
+ raise ReactOnRails::Error, msg
186
236
  end
187
237
  end
188
238
  end
@@ -5,7 +5,6 @@ require_relative "server_rendering_pool/ruby_embedded_java_script"
5
5
 
6
6
  # Based on the react-rails gem.
7
7
  # None of these methods should be called directly.
8
- # See app/helpers/react_on_rails_helper.rb
9
8
  module ReactOnRails
10
9
  module ServerRenderingPool
11
10
  class << self
@@ -5,6 +5,7 @@ module ReactOnRails
5
5
  class EnsureAssetsCompiled
6
6
  class << self
7
7
  attr_accessor :has_been_run
8
+
8
9
  @has_been_run = false
9
10
  end
10
11
 
@@ -29,13 +30,15 @@ module ReactOnRails
29
30
  # Be sure we don't do this again.
30
31
  self.class.has_been_run = true
31
32
 
32
- ReactOnRails::LocalesToJs.new
33
+ ReactOnRails::Locales.compile
33
34
 
34
35
  stale_gen_files = webpack_assets_status_checker.stale_generated_webpack_files
35
36
 
36
37
  # All done if no stale files!
37
38
  return if stale_gen_files.empty?
38
39
 
40
+ ReactOnRails::PacksGenerator.instance.generate_packs_if_stale if ReactOnRails.configuration.auto_load_bundle
41
+
39
42
  # Inform the developer that we're ensuring gen assets are ready.
40
43
  puts_start_compile_check_message(stale_gen_files)
41
44
 
@@ -43,17 +46,15 @@ module ReactOnRails
43
46
  end
44
47
 
45
48
  def puts_start_compile_check_message(stale_files)
46
- # rubocop:disable Layout/IndentHeredoc
47
- puts <<-MSG
49
+ puts <<~MSG
48
50
 
49
- Detected the following stale generated files:
50
- #{stale_files.join("\n ")}
51
+ Detected the following stale generated files:
52
+ #{stale_files.join("\n ")}
51
53
 
52
- React on Rails will ensure your JavaScript generated files are up to date, using your
53
- `#{ReactOnRails::Utils.prepend_cd_node_modules_directory(ReactOnRails.configuration.build_test_command)}` command.
54
+ React on Rails will ensure your JavaScript generated files are up to date, using your
55
+ `#{ReactOnRails::Utils.prepend_cd_node_modules_directory(ReactOnRails.configuration.build_test_command)}` command.
54
56
 
55
57
  MSG
56
- # rubocop:enable Layout/IndentHeredoc
57
58
  end
58
59
  end
59
60
  end
@@ -6,6 +6,23 @@ module ReactOnRails
6
6
  module TestHelper
7
7
  class WebpackAssetsCompiler
8
8
  def compile_assets
9
+ if ReactOnRails.configuration.build_test_command.blank?
10
+ msg = <<~MSG
11
+ You are using the React on Rails test helper.
12
+ Either you used:
13
+ ReactOnRails::TestHelper.configure_rspec_to_compile_assets or
14
+ ReactOnRails::TestHelper.ensure_assets_compiled
15
+ but you did not specify the config.build_test_command
16
+
17
+ React on Rails is aborting your test run
18
+
19
+ If you wish to use the config/webpacker.yml compile option for tests
20
+ them remove your call to the ReactOnRails test helper.
21
+ MSG
22
+ puts Rainbow(msg).red
23
+ exit!(1)
24
+ end
25
+
9
26
  puts "\nBuilding Webpack assets..."
10
27
 
11
28
  cmd = ReactOnRails::Utils.prepend_cd_node_modules_directory(
@@ -26,12 +26,16 @@ module ReactOnRails
26
26
  end
27
27
 
28
28
  def stale_generated_webpack_files
29
+ stale_generated_files(client_files)
30
+ end
31
+
32
+ def stale_generated_files(files)
29
33
  manifest_needed = ReactOnRails::WebpackerUtils.using_webpacker? &&
30
34
  !ReactOnRails::WebpackerUtils.manifest_exists?
31
35
 
32
36
  return ["manifest.json"] if manifest_needed
33
37
 
34
- most_recent_mtime = find_most_recent_mtime
38
+ most_recent_mtime = Utils.find_most_recent_mtime(files)
35
39
  all_compiled_assets.each_with_object([]) do |webpack_generated_file, stale_gen_list|
36
40
  if !File.exist?(webpack_generated_file) ||
37
41
  File.mtime(webpack_generated_file) < most_recent_mtime
@@ -43,17 +47,14 @@ module ReactOnRails
43
47
 
44
48
  private
45
49
 
46
- def find_most_recent_mtime
47
- client_files.reduce(1.year.ago) do |newest_time, file|
48
- mt = File.mtime(file)
49
- mt > newest_time ? mt : newest_time
50
- end
51
- end
52
-
53
50
  def all_compiled_assets
54
51
  @all_compiled_assets ||= begin
55
52
  webpack_generated_files = @webpack_generated_files.map do |bundle_name|
56
- ReactOnRails::Utils.bundle_js_file_path(bundle_name)
53
+ if bundle_name == ReactOnRails.configuration.server_bundle_js_file
54
+ ReactOnRails::Utils.server_bundle_js_file_path
55
+ else
56
+ ReactOnRails::Utils.bundle_js_file_path(bundle_name)
57
+ end
57
58
  end
58
59
 
59
60
  if webpack_generated_files.present?
@@ -62,10 +63,10 @@ module ReactOnRails
62
63
  file_list = make_file_list(make_globs(generated_assets_full_path)).to_ary
63
64
  puts "V" * 80
64
65
  puts "Please define config.webpack_generated_files (array) so the test helper knows "\
65
- "which files are required. If you are using webpacker, you typically need to only "\
66
- "include 'manifest.json'."
66
+ "which files are required. If you are using webpacker, you typically need to only "\
67
+ "include 'manifest.json'."
67
68
  puts "Detected the possible following files to check for webpack compilation in "\
68
- "#{generated_assets_full_path}"
69
+ "#{generated_assets_full_path}"
69
70
  puts file_list.join("\n")
70
71
  puts "^" * 80
71
72
  file_list
@@ -35,8 +35,17 @@ module ReactOnRails
35
35
  def self.configure_rspec_to_compile_assets(config, *metatags)
36
36
  metatags = %i[js server_rendering controller] if metatags.empty?
37
37
 
38
+ # Supported since RSpec 3.5.0
39
+ supports_first_matching_example = config.respond_to?(:when_first_matching_example_defined)
40
+
38
41
  metatags.each do |metatag|
39
- config.before(:example, metatag) { ReactOnRails::TestHelper.ensure_assets_compiled }
42
+ if supports_first_matching_example
43
+ config.when_first_matching_example_defined(metatag) do
44
+ ReactOnRails::TestHelper.ensure_assets_compiled
45
+ end
46
+ else
47
+ config.before(:example, metatag) { ReactOnRails::TestHelper.ensure_assets_compiled }
48
+ end
40
49
  end
41
50
  end
42
51
 
@@ -52,6 +61,7 @@ module ReactOnRails
52
61
  # don't provide one.
53
62
  # webpack_generated_files List of files to check for up-to-date-status, defaulting to
54
63
  # webpack_generated_files in your configuration
64
+ # rubocop:disable Metrics/CyclomaticComplexity
55
65
  def self.ensure_assets_compiled(webpack_assets_status_checker: nil,
56
66
  webpack_assets_compiler: nil,
57
67
  source_path: nil,
@@ -71,10 +81,20 @@ module ReactOnRails
71
81
  unless @printed_once
72
82
  puts
73
83
  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}"
84
+ "#{webpack_assets_status_checker.generated_assets_full_path} for "\
85
+ "outdated/missing bundles based on source_path #{source_path}"
76
86
  puts
77
87
  @printed_once = true
88
+
89
+ if ReactOnRails::WebpackerUtils.using_webpacker? &&
90
+ ReactOnRails::Utils.using_webpacker_source_path_is_not_defined_and_custom_node_modules?
91
+ msg = <<-MSG.strip_heredoc
92
+ WARNING: Define config.webpacker.yml to include sourcePath to configure
93
+ the location of your JavaScript source for React on Rails.
94
+ Default location of #{source_path} is used.
95
+ MSG
96
+ puts ReactOnRails::Utils.wrap_message(msg, :orange)
97
+ end
78
98
  end
79
99
  end
80
100
 
@@ -86,4 +106,5 @@ module ReactOnRails
86
106
  ).call
87
107
  end
88
108
  end
109
+ # rubocop:enable Metrics/CyclomaticComplexity
89
110
  end