react_on_rails 11.3.0 → 12.0.0.pre.beta.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) 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 +9 -22
  11. data/CHANGELOG.md +46 -4
  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 +102 -69
  16. data/Rakefile +0 -7
  17. data/SUMMARY.md +7 -11
  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 +6 -7
  26. data/docs/basics/configuration.md +60 -57
  27. data/docs/basics/deployment.md +1 -2
  28. data/docs/basics/i18n.md +44 -22
  29. data/docs/basics/installation-into-an-existing-rails-app.md +2 -2
  30. data/docs/basics/react-server-rendering.md +1 -1
  31. data/docs/basics/{generator-functions-and-railscontext.md → render-functions-and-railscontext.md} +59 -21
  32. data/docs/basics/upgrading-react-on-rails.md +50 -2
  33. data/docs/basics/webpack-configuration.md +15 -1
  34. data/docs/contributor-info/errors-with-hooks.md +45 -0
  35. data/docs/contributor-info/pull-requests.md +44 -0
  36. data/docs/misc/doctrine.md +1 -1
  37. data/docs/{misc-pending → outdated}/code-splitting.md +12 -8
  38. data/docs/{additional-reading → outdated}/heroku-deployment.md +0 -6
  39. data/docs/{basics → outdated}/how-react-on-rails-works.md +2 -2
  40. data/docs/{misc-pending → outdated}/manual-installation-overview.md +5 -5
  41. data/docs/{additional-reading → outdated}/rails-assets-relative-paths.md +3 -3
  42. data/docs/{misc-pending → outdated}/rails-assets.md +4 -7
  43. data/docs/{misc → outdated}/rails3.md +0 -0
  44. data/docs/tutorial.md +54 -34
  45. data/jest.config.js +4 -0
  46. data/lib/generators/react_on_rails/dev_tests_generator.rb +1 -1
  47. data/lib/generators/react_on_rails/generator_helper.rb +4 -6
  48. data/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx +9 -8
  49. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx +4 -8
  50. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/store/helloWorldStore.js +1 -3
  51. data/lib/react_on_rails.rb +3 -1
  52. data/lib/react_on_rails/configuration.rb +13 -22
  53. data/lib/react_on_rails/error.rb +2 -0
  54. data/lib/react_on_rails/helper.rb +41 -91
  55. data/lib/react_on_rails/json_parse_error.rb +2 -0
  56. data/lib/react_on_rails/locales/base.rb +142 -0
  57. data/lib/react_on_rails/locales/to_js.rb +37 -0
  58. data/lib/react_on_rails/locales/to_json.rb +27 -0
  59. data/lib/react_on_rails/prerender_error.rb +11 -15
  60. data/lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb +41 -46
  61. data/lib/react_on_rails/test_helper/ensure_assets_compiled.rb +7 -8
  62. data/lib/react_on_rails/utils.rb +14 -19
  63. data/lib/react_on_rails/version.rb +1 -1
  64. data/lib/react_on_rails/version_checker.rb +1 -0
  65. data/lib/react_on_rails/webpacker_utils.rb +13 -2
  66. data/lib/tasks/assets.rake +5 -45
  67. data/lib/tasks/locale.rake +8 -2
  68. data/package-scripts.yml +11 -8
  69. data/package.json +29 -28
  70. data/rakelib/dummy_apps.rake +1 -9
  71. data/rakelib/example_type.rb +3 -1
  72. data/rakelib/examples.rake +3 -0
  73. data/rakelib/lint.rake +2 -7
  74. data/rakelib/node_package.rake +2 -2
  75. data/rakelib/run_rspec.rake +5 -18
  76. data/react_on_rails.gemspec +3 -5
  77. data/tsconfig.json +14 -0
  78. data/webpackConfigLoader.js +3 -2
  79. data/yarn.lock +4170 -2197
  80. metadata +30 -56
  81. data/Gemfile.rails32 +0 -73
  82. data/docs/additional-reading/babel.md +0 -5
  83. data/docs/additional-reading/hot-reloading-rails-development-asset-pipeline.md +0 -47
  84. data/docs/api/ruby-api-hot-reload-view-helpers.md +0 -44
  85. data/lib/react_on_rails/assets_precompile.rb +0 -153
  86. data/lib/react_on_rails/locales_to_js.rb +0 -138
@@ -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
@@ -1,6 +1,7 @@
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
@@ -121,14 +122,12 @@ module ReactOnRails
121
122
  return if ReactOnRails.configuration.server_bundle_js_file.blank?
122
123
 
123
124
  bundle_js_code = read_bundle_js_code
124
-
125
- # rubocop:disable Layout/IndentHeredoc
126
- base_js_code = <<-JS
127
- #{console_polyfill}
128
- #{execjs_timer_polyfills}
129
- #{bundle_js_code};
125
+ base_js_code = <<~JS
126
+ #{console_polyfill}
127
+ #{execjs_timer_polyfills}
128
+ #{bundle_js_code};
130
129
  JS
131
- # rubocop:enable Layout/IndentHeredoc
130
+
132
131
  file_name = "tmp/base_js_code.js"
133
132
  begin
134
133
  if ReactOnRails.configuration.trace
@@ -151,33 +150,31 @@ module ReactOnRails
151
150
  end
152
151
 
153
152
  def execjs_timer_polyfills
154
- # rubocop:disable Layout/IndentHeredoc
155
- <<-JS
156
- function getStackTrace () {
157
- var stack;
158
- try {
159
- throw new Error('');
160
- }
161
- catch (error) {
162
- stack = error.stack || '';
163
- }
164
- stack = stack.split('\\n').map(function (line) { return line.trim(); });
165
- return stack.splice(stack[0] == 'Error' ? 2 : 1);
166
- }
167
-
168
- function setInterval() {
169
- #{undefined_for_exec_js_logging('setInterval')}
170
- }
171
-
172
- function setTimeout() {
173
- #{undefined_for_exec_js_logging('setTimeout')}
174
- }
175
-
176
- function clearTimeout() {
177
- #{undefined_for_exec_js_logging('clearTimeout')}
178
- }
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
+ }
179
177
  JS
180
- # rubocop:enable Layout/IndentHeredoc
181
178
  end
182
179
 
183
180
  def undefined_for_exec_js_logging(function_name)
@@ -191,20 +188,18 @@ function clearTimeout() {
191
188
 
192
189
  # Reimplement console methods for replaying on the client
193
190
  def console_polyfill
194
- # rubocop:disable Layout/IndentHeredoc
195
- <<-JS
196
- var console = { history: [] };
197
- ['error', 'log', 'info', 'warn'].forEach(function (level) {
198
- console[level] = function () {
199
- var argArray = Array.prototype.slice.call(arguments);
200
- if (argArray.length > 0) {
201
- argArray[0] = '[SERVER] ' + argArray[0];
202
- }
203
- console.history.push({level: level, arguments: argArray});
204
- };
205
- });
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
+ });
206
202
  JS
207
- # rubocop:enable Layout/IndentHeredoc
208
203
  end
209
204
 
210
205
  private
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ReactOnRails
4
- VERSION = "11.3.0".freeze
4
+ VERSION = "12.0.0-beta.0"
5
5
  end
@@ -5,6 +5,7 @@ module ReactOnRails
5
5
  # against each otherat runtime.
6
6
  class VersionChecker
7
7
  attr_reader :node_package_version
8
+
8
9
  MAJOR_MINOR_PATCH_VERSION_REGEX = /(\d+)\.(\d+)\.(\d+)/.freeze
9
10
 
10
11
  def self.build
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ReactOnRails
2
4
  module WebpackerUtils
3
5
  def self.using_webpacker?
@@ -10,7 +12,9 @@ module ReactOnRails
10
12
  Webpacker.dev_server.running?
11
13
  end
12
14
 
13
- # This returns either a URL for the webpack-dev-server or a file path
15
+ # This returns either a URL for the webpack-dev-server, non-server bundle or
16
+ # the hashed server bundle if using the same bundle for the client.
17
+ # Otherwise returns a file path.
14
18
  def self.bundle_js_uri_from_webpacker(bundle_name)
15
19
  # Note Webpacker 3.4.3 manifest lookup is inside of the public_output_path
16
20
  # [2] (pry) ReactOnRails::WebpackerUtils: 0> Webpacker.manifest.lookup("app-bundle.js")
@@ -18,7 +22,14 @@ module ReactOnRails
18
22
  # Next line will throw if the file or manifest does not exist
19
23
  hashed_bundle_name = Webpacker.manifest.lookup!(bundle_name)
20
24
 
21
- if Webpacker.dev_server.running?
25
+ # support for hashing the server-bundle and having that built
26
+ # by a webpack watch process and not served by the webpack-dev-server, then we
27
+ # need an extra config value "same_bundle_for_client_and_server" where a value of false
28
+ # would mean that the bundle is created by a separate webpack watch process.
29
+ is_server_bundle = bundle_name == ReactOnRails.configuration.server_bundle_js_file
30
+
31
+ if Webpacker.dev_server.running? && (!is_server_bundle ||
32
+ ReactOnRails.configuration.same_bundle_for_client_and_server)
22
33
  "#{Webpacker.dev_server.protocol}://#{Webpacker.dev_server.host_with_port}#{hashed_bundle_name}"
23
34
  else
24
35
  File.expand_path(File.join("public", hashed_bundle_name)).to_s
@@ -1,48 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "react_on_rails/assets_precompile"
4
3
  require "active_support"
5
4
 
6
- if defined?(Sprockets)
7
- namespace :react_on_rails do
8
- namespace :assets do
9
- desc "Creates non-digested symlinks for the assets in the public asset dir"
10
- task symlink_non_digested_assets: :"assets:environment" do
11
- ReactOnRails::AssetsPrecompile.new.symlink_non_digested_assets
12
- end
13
-
14
- desc "Cleans all broken symlinks for the assets in the public asset dir"
15
- task delete_broken_symlinks: :"assets:environment" do
16
- ReactOnRails::AssetsPrecompile.new.delete_broken_symlinks
17
- end
18
-
19
- # In this task, set prerequisites for the assets:precompile task
20
- desc <<-DESC.strip_heredoc
21
- Create webpack assets before calling assets:environment
22
- The webpack task must run before assets:environment task.
23
- Otherwise Sprockets cannot find the files that webpack produces.
24
- This is the secret sauce for how a Heroku deployment knows to create the webpack generated JavaScript files.
25
- DESC
26
- task compile_environment: :webpack do
27
- Rake::Task["assets:environment"].invoke
28
- end
29
-
30
- desc "Delete assets created with webpack, in the generated assetst directory (/app/assets/webpack)"
31
- task clobber: :environment do
32
- ReactOnRails::AssetsPrecompile.new.clobber
33
- end
34
- end
5
+ if Rake::Task.task_defined?("assets:precompile")
6
+ Rake::Task["assets:precompile"].enhance do
7
+ Rake::Task["react_on_rails:assets:webpack"].invoke
35
8
  end
36
-
37
- # These tasks run as pre-requisites of assets:precompile.
38
- # Note, it's not possible to refer to ReactOnRails configuration values at this point.
39
- Rake::Task["assets:precompile"]
40
- .clear_prerequisites
41
- .enhance([:environment, "react_on_rails:assets:compile_environment"])
42
- .enhance do
43
- Rake::Task["react_on_rails:assets:symlink_non_digested_assets"].invoke
44
- Rake::Task["react_on_rails:assets:delete_broken_symlinks"].invoke
45
- end
9
+ else
10
+ Rake::Task.define_task("assets:precompile" => ["react_on_rails:assets:webpack"])
46
11
  end
47
12
 
48
13
  # Sprockets independent tasks
@@ -55,11 +20,6 @@ namespace :react_on_rails do
55
20
  sh "#{ReactOnRails::Utils.prepend_cd_node_modules_directory('<ReactOnRails.configuration.build_production_command>')}"
56
21
  DESC
57
22
  task webpack: :locale do
58
- if Rake::Task.task_defined?("webpacker:compile")
59
- # TODO: Eventually, this will need reconsideration if we use any of the Webpacker compilation
60
- Rake::Task["webpacker:compile"].clear
61
- end
62
-
63
23
  if ReactOnRails.configuration.build_production_command.present?
64
24
  sh ReactOnRails::Utils.prepend_cd_node_modules_directory(
65
25
  ReactOnRails.configuration.build_production_command
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "react_on_rails/locales_to_js"
3
+ require "react_on_rails/locales/base"
4
+ require "react_on_rails/locales/to_js"
5
+ require "react_on_rails/locales/to_json"
4
6
  require "active_support"
5
7
 
6
8
  namespace :react_on_rails do
@@ -10,6 +12,10 @@ namespace :react_on_rails do
10
12
  the "ReactOnRails.configuration.i18n_dir".
11
13
  DESC
12
14
  task locale: :environment do
13
- ReactOnRails::LocalesToJs.new
15
+ if ReactOnRails.configuration.i18n_output_format&.downcase == "js"
16
+ ReactOnRails::Locales::ToJs.new
17
+ else
18
+ ReactOnRails::Locales::ToJson.new
19
+ end
14
20
  end
15
21
  end