react_on_rails 11.1.8 → 12.0.0.pre.beta.1

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 (106) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +320 -0
  3. data/.eslintignore +2 -1
  4. data/.eslintrc +30 -2
  5. data/.github/FUNDING.yml +1 -0
  6. data/.gitignore +3 -1
  7. data/.prettierignore +10 -0
  8. data/.prettierrc +23 -0
  9. data/.release-it.json +3 -0
  10. data/.rubocop.yml +37 -11
  11. data/.travis.yml +10 -20
  12. data/CHANGELOG.md +89 -3
  13. data/CONTRIBUTING.md +61 -80
  14. data/Gemfile +3 -5
  15. data/{COMM-LICENSE → REACT-ON-RAILS-PRO-LICENSE} +6 -9
  16. data/README.md +121 -71
  17. data/Rakefile +0 -7
  18. data/SUMMARY.md +10 -12
  19. data/book.json +5 -5
  20. data/docs/additional-reading/asset-pipeline.md +8 -16
  21. data/docs/additional-reading/images.md +1 -1
  22. data/docs/additional-reading/rails_view_rendering_from_inline_javascript.md +2 -1
  23. data/docs/additional-reading/react-helmet.md +30 -10
  24. data/docs/additional-reading/react-router.md +52 -75
  25. data/docs/additional-reading/server-rendering-tips.md +12 -7
  26. data/docs/additional-reading/upgrade-webpacker-v3-to-v4.md +10 -0
  27. data/docs/api/javascript-api.md +3 -3
  28. data/docs/api/redux-store-api.md +4 -2
  29. data/docs/api/view-helpers-api.md +17 -14
  30. data/docs/basics/configuration.md +64 -61
  31. data/docs/basics/deployment.md +1 -2
  32. data/docs/basics/i18n.md +44 -22
  33. data/docs/basics/installation-into-an-existing-rails-app.md +2 -2
  34. data/docs/basics/minitest-configuration.md +31 -0
  35. data/docs/basics/react-server-rendering.md +1 -1
  36. data/docs/basics/recommended-project-structure.md +1 -1
  37. data/docs/basics/{generator-functions-and-railscontext.md → render-functions-and-railscontext.md} +59 -21
  38. data/docs/basics/rspec-configuration.md +2 -2
  39. data/docs/basics/upgrading-react-on-rails.md +61 -3
  40. data/docs/basics/webpack-configuration.md +15 -1
  41. data/docs/contributor-info/errors-with-hooks.md +45 -0
  42. data/docs/contributor-info/pull-requests.md +44 -0
  43. data/docs/misc/doctrine.md +1 -1
  44. data/docs/{misc-pending → outdated}/code-splitting.md +13 -9
  45. data/docs/{additional-reading → outdated}/heroku-deployment.md +0 -6
  46. data/docs/{basics → outdated}/how-react-on-rails-works.md +3 -3
  47. data/docs/{misc-pending → outdated}/manual-installation-overview.md +5 -5
  48. data/docs/{additional-reading → outdated}/rails-assets-relative-paths.md +3 -3
  49. data/docs/{misc-pending → outdated}/rails-assets.md +4 -7
  50. data/docs/{misc → outdated}/rails3.md +0 -0
  51. data/docs/testimonials/hvmn.md +3 -3
  52. data/docs/testimonials/resortpass.md +13 -0
  53. data/docs/testimonials/testimonials.md +11 -1
  54. data/docs/tutorial.md +69 -28
  55. data/jest.config.js +4 -0
  56. data/lib/generators/react_on_rails/base_generator.rb +2 -2
  57. data/lib/generators/react_on_rails/dev_tests_generator.rb +2 -1
  58. data/lib/generators/react_on_rails/generator_helper.rb +4 -6
  59. data/lib/generators/react_on_rails/install_generator.rb +2 -0
  60. data/lib/generators/react_on_rails/templates/base/base/Procfile.dev +3 -1
  61. data/lib/generators/react_on_rails/templates/base/base/Procfile.dev-hmr +18 -0
  62. data/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx +20 -40
  63. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx +4 -8
  64. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/store/helloWorldStore.js +1 -3
  65. data/lib/react_on_rails.rb +4 -1
  66. data/lib/react_on_rails/configuration.rb +15 -23
  67. data/lib/react_on_rails/error.rb +2 -0
  68. data/lib/react_on_rails/git_utils.rb +2 -0
  69. data/lib/react_on_rails/helper.rb +111 -162
  70. data/lib/react_on_rails/json_output.rb +1 -1
  71. data/lib/react_on_rails/json_parse_error.rb +2 -0
  72. data/lib/react_on_rails/locales/base.rb +142 -0
  73. data/lib/react_on_rails/locales/to_js.rb +37 -0
  74. data/lib/react_on_rails/locales/to_json.rb +27 -0
  75. data/lib/react_on_rails/prerender_error.rb +11 -15
  76. data/lib/react_on_rails/react_component/render_options.rb +4 -0
  77. data/lib/react_on_rails/server_rendering_js_code.rb +42 -0
  78. data/lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb +71 -51
  79. data/lib/react_on_rails/test_helper/ensure_assets_compiled.rb +7 -8
  80. data/lib/react_on_rails/utils.rb +15 -20
  81. data/lib/react_on_rails/version.rb +1 -1
  82. data/lib/react_on_rails/version_checker.rb +5 -1
  83. data/lib/react_on_rails/webpacker_utils.rb +16 -2
  84. data/lib/tasks/assets.rake +5 -45
  85. data/lib/tasks/locale.rake +8 -2
  86. data/package-scripts.yml +49 -0
  87. data/package.json +41 -31
  88. data/rakelib/dummy_apps.rake +1 -9
  89. data/rakelib/example_type.rb +3 -1
  90. data/rakelib/examples.rake +3 -0
  91. data/rakelib/lint.rake +2 -7
  92. data/rakelib/node_package.rake +2 -2
  93. data/rakelib/release.rake +3 -8
  94. data/rakelib/run_rspec.rake +5 -18
  95. data/react_on_rails.gemspec +3 -5
  96. data/tsconfig.json +14 -0
  97. data/webpackConfigLoader.js +5 -4
  98. data/yarn.lock +7042 -2327
  99. metadata +39 -57
  100. data/Gemfile.rails32 +0 -74
  101. data/docs/additional-reading/babel.md +0 -5
  102. data/docs/additional-reading/hot-reloading-rails-development-asset-pipeline.md +0 -47
  103. data/docs/api/ruby-api-hot-reload-view-helpers.md +0 -44
  104. data/lib/generators/react_on_rails/templates/base/base/Procfile.dev-server +0 -12
  105. data/lib/react_on_rails/assets_precompile.rb +0 -150
  106. data/lib/react_on_rails/locales_to_js.rb +0 -136
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+
5
+ module ReactOnRails
6
+ module Locales
7
+ class ToJs < Base
8
+ def initialize
9
+ super
10
+ end
11
+
12
+ private
13
+
14
+ def file_format
15
+ "js"
16
+ end
17
+
18
+ def template_translations
19
+ <<-JS.strip_heredoc
20
+ export const translations = #{@translations};
21
+ JS
22
+ end
23
+
24
+ def template_default
25
+ <<-JS.strip_heredoc
26
+ import { defineMessages } from 'react-intl';
27
+
28
+ const defaultLocale = \'#{default_locale}\';
29
+
30
+ const defaultMessages = defineMessages(#{@defaults});
31
+
32
+ export { defaultMessages, defaultLocale };
33
+ JS
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+
5
+ module ReactOnRails
6
+ module Locales
7
+ class ToJson < Base
8
+ def initialize
9
+ super
10
+ end
11
+
12
+ private
13
+
14
+ def file_format
15
+ "json"
16
+ end
17
+
18
+ def template_translations
19
+ @translations
20
+ end
21
+
22
+ def template_default
23
+ @defaults
24
+ end
25
+ end
26
+ end
27
+ end
@@ -48,33 +48,29 @@ module ReactOnRails
48
48
  def calc_message(component_name, console_messages, err, js_code, props)
49
49
  message = "ERROR in SERVER PRERENDERING\n".dup
50
50
  if err
51
- # rubocop:disable Layout/IndentHeredoc
52
- message << <<-MSG
53
- Encountered error: \"#{err}\"
51
+ message << <<~MSG
52
+ Encountered error: \"#{err}\"
54
53
  MSG
55
- # rubocop:enable Layout/IndentHeredoc
54
+
56
55
  backtrace = err.backtrace.join("\n")
57
56
  else
58
57
  backtrace = nil
59
58
  end
60
- # rubocop:disable Layout/IndentHeredoc
61
- message << <<-MSG
62
- when prerendering #{component_name} with props: #{Utils.smart_trim(props, MAX_ERROR_SNIPPET_TO_LOG)}
59
+ message << <<~MSG
60
+ when prerendering #{component_name} with props: #{Utils.smart_trim(props, MAX_ERROR_SNIPPET_TO_LOG)}
63
61
 
64
- code:
62
+ code:
65
63
 
66
- #{Utils.smart_trim(js_code, MAX_ERROR_SNIPPET_TO_LOG)}
64
+ #{Utils.smart_trim(js_code, MAX_ERROR_SNIPPET_TO_LOG)}
67
65
 
68
66
  MSG
69
- # rubocop:enable Layout/IndentHeredoc
70
67
 
71
68
  if console_messages
72
- # rubocop:disable Layout/IndentHeredoc
73
- message << <<-MSG
74
- console messages:
75
- #{console_messages}
69
+ message << <<~MSG
70
+ console messages:
71
+ #{console_messages}
76
72
  MSG
77
- # rubocop:enable Layout/IndentHeredoc
73
+
78
74
  end
79
75
  [backtrace, message]
80
76
  end
@@ -72,6 +72,10 @@ module ReactOnRails
72
72
  "{ react_component_name = #{react_component_name}, options = #{options}, request_digest = #{request_digest}"
73
73
  end
74
74
 
75
+ def internal_option(key)
76
+ options[key]
77
+ end
78
+
75
79
  private
76
80
 
77
81
  attr_reader :options
@@ -0,0 +1,42 @@
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
+ js_code_renderer.render(props_string, rails_context, redux_stores, react_component_name, render_options)
22
+ end
23
+
24
+ def render(props_string, rails_context, redux_stores, react_component_name, render_options)
25
+ <<-JS
26
+ (function() {
27
+ var railsContext = #{rails_context};
28
+ #{redux_stores}
29
+ var props = #{props_string};
30
+ return ReactOnRails.serverRenderReactComponent({
31
+ name: '#{react_component_name}',
32
+ domNodeId: '#{render_options.dom_id}',
33
+ props: props,
34
+ trace: #{render_options.trace},
35
+ railsContext: railsContext
36
+ });
37
+ })()
38
+ JS
39
+ end
40
+ end
41
+ end
42
+ 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
 
@@ -72,6 +80,7 @@ module ReactOnRails
72
80
 
73
81
  def trace_js_code_used(msg, js_code, file_name = "tmp/server-generated.js", force: false)
74
82
  return unless ReactOnRails.configuration.trace || force
83
+
75
84
  # Set to anything to print generated code.
76
85
  File.write(file_name, js_code)
77
86
  msg = <<-MSG.strip_heredoc
@@ -97,7 +106,11 @@ module ReactOnRails
97
106
 
98
107
  def read_bundle_js_code
99
108
  server_js_file = ReactOnRails::Utils.server_bundle_js_file_path
100
- File.read(server_js_file)
109
+ if ReactOnRails::Utils.server_bundle_path_is_http?
110
+ file_url_to_string(server_js_file)
111
+ else
112
+ File.read(server_js_file)
113
+ end
101
114
  rescue StandardError => e
102
115
  msg = "You specified server rendering JS file: #{server_js_file}, but it cannot be "\
103
116
  "read. You may set the server_bundle_js_file in your configuration to be \"\" to "\
@@ -109,14 +122,12 @@ module ReactOnRails
109
122
  return if ReactOnRails.configuration.server_bundle_js_file.blank?
110
123
 
111
124
  bundle_js_code = read_bundle_js_code
112
-
113
- # rubocop:disable Layout/IndentHeredoc
114
- base_js_code = <<-JS
115
- #{console_polyfill}
116
- #{execjs_timer_polyfills}
117
- #{bundle_js_code};
125
+ base_js_code = <<~JS
126
+ #{console_polyfill}
127
+ #{execjs_timer_polyfills}
128
+ #{bundle_js_code};
118
129
  JS
119
- # rubocop:enable Layout/IndentHeredoc
130
+
120
131
  file_name = "tmp/base_js_code.js"
121
132
  begin
122
133
  if ReactOnRails.configuration.trace
@@ -139,33 +150,31 @@ module ReactOnRails
139
150
  end
140
151
 
141
152
  def execjs_timer_polyfills
142
- # rubocop:disable Layout/IndentHeredoc
143
- <<-JS
144
- function getStackTrace () {
145
- var stack;
146
- try {
147
- throw new Error('');
148
- }
149
- catch (error) {
150
- stack = error.stack || '';
151
- }
152
- stack = stack.split('\\n').map(function (line) { return line.trim(); });
153
- return stack.splice(stack[0] == 'Error' ? 2 : 1);
154
- }
155
-
156
- function setInterval() {
157
- #{undefined_for_exec_js_logging('setInterval')}
158
- }
159
-
160
- function setTimeout() {
161
- #{undefined_for_exec_js_logging('setTimeout')}
162
- }
163
-
164
- function clearTimeout() {
165
- #{undefined_for_exec_js_logging('clearTimeout')}
166
- }
153
+ <<~JS
154
+ function getStackTrace () {
155
+ var stack;
156
+ try {
157
+ throw new Error('');
158
+ }
159
+ catch (error) {
160
+ stack = error.stack || '';
161
+ }
162
+ stack = stack.split('\\n').map(function (line) { return line.trim(); });
163
+ return stack.splice(stack[0] == 'Error' ? 2 : 1);
164
+ }
165
+
166
+ function setInterval() {
167
+ #{undefined_for_exec_js_logging('setInterval')}
168
+ }
169
+
170
+ function setTimeout() {
171
+ #{undefined_for_exec_js_logging('setTimeout')}
172
+ }
173
+
174
+ function clearTimeout() {
175
+ #{undefined_for_exec_js_logging('clearTimeout')}
176
+ }
167
177
  JS
168
- # rubocop:enable Layout/IndentHeredoc
169
178
  end
170
179
 
171
180
  def undefined_for_exec_js_logging(function_name)
@@ -179,20 +188,31 @@ function clearTimeout() {
179
188
 
180
189
  # Reimplement console methods for replaying on the client
181
190
  def console_polyfill
182
- # rubocop:disable Layout/IndentHeredoc
183
- <<-JS
184
- var console = { history: [] };
185
- ['error', 'log', 'info', 'warn'].forEach(function (level) {
186
- console[level] = function () {
187
- var argArray = Array.prototype.slice.call(arguments);
188
- if (argArray.length > 0) {
189
- argArray[0] = '[SERVER] ' + argArray[0];
190
- }
191
- console.history.push({level: level, arguments: argArray});
192
- };
193
- });
191
+ <<~JS
192
+ var console = { history: [] };
193
+ ['error', 'log', 'info', 'warn'].forEach(function (level) {
194
+ console[level] = function () {
195
+ var argArray = Array.prototype.slice.call(arguments);
196
+ if (argArray.length > 0) {
197
+ argArray[0] = '[SERVER] ' + argArray[0];
198
+ }
199
+ console.history.push({level: level, arguments: argArray});
200
+ };
201
+ });
194
202
  JS
195
- # rubocop:enable Layout/IndentHeredoc
203
+ end
204
+
205
+ private
206
+
207
+ def file_url_to_string(url)
208
+ response = Net::HTTP.get_response(URI.parse(url))
209
+ content_type_header = response["content-type"]
210
+ match = content_type_header.match(/\A.*; charset=(?<encoding>.*)\z/)
211
+ encoding_type = match[:encoding]
212
+ response.body.force_encoding(encoding_type)
213
+ rescue StandardError => e
214
+ msg = "file_url_to_string #{url} failed\nError is: #{e}"
215
+ raise ReactOnRails::Error, msg
196
216
  end
197
217
  end
198
218
  end
@@ -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,7 +30,7 @@ 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::ToJs.new
33
34
 
34
35
  stale_gen_files = webpack_assets_status_checker.stale_generated_webpack_files
35
36
 
@@ -43,17 +44,15 @@ module ReactOnRails
43
44
  end
44
45
 
45
46
  def puts_start_compile_check_message(stale_files)
46
- # rubocop:disable Layout/IndentHeredoc
47
- puts <<-MSG
47
+ puts <<~MSG
48
48
 
49
- Detected the following stale generated files:
50
- #{stale_files.join("\n ")}
49
+ Detected the following stale generated files:
50
+ #{stale_files.join("\n ")}
51
51
 
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.
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
54
 
55
55
  MSG
56
- # rubocop:enable Layout/IndentHeredoc
57
56
  end
58
57
  end
59
58
  end
@@ -8,7 +8,7 @@ require "active_support/core_ext/string"
8
8
 
9
9
  module ReactOnRails
10
10
  module Utils
11
- TRUNCATION_FILLER = "\n... TRUNCATED ...\n".freeze
11
+ TRUNCATION_FILLER = "\n... TRUNCATED ...\n"
12
12
 
13
13
  # https://forum.shakacode.com/t/yak-of-the-week-ruby-2-4-pathname-empty-changed-to-look-at-file-size/901
14
14
  # return object if truthy, else return nil
@@ -24,13 +24,12 @@ module ReactOnRails
24
24
  # Pass in the msg and color as a symbol.
25
25
  def self.wrap_message(msg, color = :red)
26
26
  wrapper_line = ("=" * 80).to_s
27
- # rubocop:disable Layout/IndentHeredoc
28
- fenced_msg = <<-MSG
29
- #{wrapper_line}
30
- #{msg.strip}
31
- #{wrapper_line}
27
+ fenced_msg = <<~MSG
28
+ #{wrapper_line}
29
+ #{msg.strip}
30
+ #{wrapper_line}
32
31
  MSG
33
- # rubocop:enable Layout/IndentHeredoc
32
+
34
33
  Rainbow(fenced_msg).color(color)
35
34
  end
36
35
 
@@ -48,14 +47,13 @@ module ReactOnRails
48
47
  unless status.success?
49
48
  stdout_msg = stdout.present? ? "\nstdout:\n#{stdout.strip}\n" : ""
50
49
  stderr_msg = stderr.present? ? "\nstderr:\n#{stderr.strip}\n" : ""
51
- # rubocop:disable Layout/IndentHeredoc
52
- msg = <<-MSG
53
- React on Rails FATAL ERROR!
54
- #{failure_message}
55
- cmd: #{cmd}
56
- exitstatus: #{status.exitstatus}#{stdout_msg}#{stderr_msg}
50
+ msg = <<~MSG
51
+ React on Rails FATAL ERROR!
52
+ #{failure_message}
53
+ cmd: #{cmd}
54
+ exitstatus: #{status.exitstatus}#{stdout_msg}#{stderr_msg}
57
55
  MSG
58
- # rubocop:enable Layout/IndentHeredoc
56
+
59
57
  puts wrap_message(msg)
60
58
 
61
59
  # Rspec catches exit without! in the exit callbacks
@@ -72,11 +70,8 @@ exitstatus: #{status.exitstatus}#{stdout_msg}#{stderr_msg}
72
70
  # Either:
73
71
  # 1. Using same bundle for both server and client, so server bundle will be hashed in manifest
74
72
  # 2. Using a different bundle (different Webpack config), so file is not hashed, and
75
- # bundle_js_path will throw.
76
- # 3. Not using webpacker, and bundle_js_path always returns
77
-
78
- # Note, server bundle should not be in the manifest
79
- # If using webpacker gem per https://github.com/rails/webpacker/issues/571
73
+ # bundle_js_path will throw so the default path is used without a hash.
74
+ # 3. Not using webpacker, and this method returns the bundle_js_file_path
80
75
  return @server_bundle_path if @server_bundle_path && !Rails.env.development?
81
76
 
82
77
  bundle_name = ReactOnRails.configuration.server_bundle_js_file
@@ -96,7 +91,7 @@ exitstatus: #{status.exitstatus}#{stdout_msg}#{stderr_msg}
96
91
 
97
92
  def self.bundle_js_file_path(bundle_name)
98
93
  if ReactOnRails::WebpackerUtils.using_webpacker? && bundle_name != "manifest.json"
99
- ReactOnRails::WebpackerUtils.bundle_js_file_path_from_webpacker(bundle_name)
94
+ ReactOnRails::WebpackerUtils.bundle_js_uri_from_webpacker(bundle_name)
100
95
  else
101
96
  # Default to the non-hashed name in the specified output directory, which, for legacy
102
97
  # React on Rails, this is the output directory picked up by the asset pipeline.