react_on_rails 10.1.4 → 11.0.0.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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/CHANGELOG.md +14 -2
  4. data/CONTRIBUTING.md +0 -7
  5. data/Gemfile +8 -13
  6. data/README.md +5 -4
  7. data/app/helpers/react_on_rails_helper.rb +1 -534
  8. data/docs/additional-reading/caching-and-performance.md +2 -29
  9. data/docs/additional-reading/server-rendering-tips.md +4 -4
  10. data/docs/basics/configuration.md +23 -9
  11. data/docs/basics/i18n.md +4 -0
  12. data/lib/generators/react_on_rails/base_generator.rb +2 -3
  13. data/lib/generators/react_on_rails/dev_tests_generator.rb +1 -1
  14. data/lib/generators/react_on_rails/install_generator.rb +1 -1
  15. data/lib/generators/react_on_rails/react_no_redux_generator.rb +1 -1
  16. data/lib/generators/react_on_rails/react_with_redux_generator.rb +1 -1
  17. data/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb +2 -1
  18. data/lib/generators/react_on_rails/templates/dev_tests/spec/simplecov_helper.rb +2 -2
  19. data/lib/react_on_rails.rb +3 -2
  20. data/lib/react_on_rails/configuration.rb +27 -8
  21. data/lib/react_on_rails/error.rb +4 -0
  22. data/lib/react_on_rails/prerender_error.rb +1 -1
  23. data/lib/react_on_rails/react_on_rails_helper.rb +546 -0
  24. data/lib/react_on_rails/server_rendering_pool.rb +21 -11
  25. data/lib/react_on_rails/server_rendering_pool/{exec.rb → ruby_embedded_java_script.rb} +35 -38
  26. data/lib/react_on_rails/test_helper.rb +1 -1
  27. data/lib/react_on_rails/test_helper/webpack_assets_status_checker.rb +2 -2
  28. data/lib/react_on_rails/utils.rb +12 -73
  29. data/lib/react_on_rails/version.rb +1 -1
  30. data/lib/react_on_rails/version_checker.rb +4 -2
  31. data/lib/react_on_rails/webpacker_utils.rb +42 -0
  32. data/package.json +1 -1
  33. data/rakelib/example_type.rb +1 -1
  34. data/rakelib/examples.rake +1 -1
  35. data/rakelib/release.rake +0 -5
  36. data/rakelib/task_helpers.rb +1 -1
  37. data/react_on_rails.gemspec +12 -9
  38. metadata +30 -13
  39. data/lib/react_on_rails/server_rendering_pool/node.rb +0 -81
  40. data/lib/react_on_rails/test_helper/node_process_launcher.rb +0 -14
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "connection_pool"
4
- require_relative "server_rendering_pool/exec"
5
- require_relative "server_rendering_pool/node"
4
+ require_relative "server_rendering_pool/ruby_embedded_java_script"
6
5
 
7
6
  # Based on the react-rails gem.
8
7
  # None of these methods should be called directly.
@@ -10,19 +9,30 @@ require_relative "server_rendering_pool/node"
10
9
  module ReactOnRails
11
10
  module ServerRenderingPool
12
11
  class << self
12
+ def react_on_rails_pro?
13
+ @react_on_rails_pro ||= gem_available?("react_on_rails_pro")
14
+ end
15
+
13
16
  def pool
14
- if ReactOnRails.configuration.server_render_method == "NodeJS"
15
- ServerRenderingPool::Node
16
- else
17
- ServerRenderingPool::Exec
18
- end
17
+ @pool ||= if react_on_rails_pro?
18
+ ReactOnRailsPro::ServerRenderingPool::ProRendering
19
+ else
20
+ ReactOnRails::ServerRenderingPool::RubyEmbeddedJavaScript
21
+ end
19
22
  end
20
23
 
21
- # rubocop:disable Style/MethodMissing
22
- def method_missing(sym, *args, &block)
23
- pool.send sym, *args, &block
24
+ delegate :server_render_js_with_console_logging, :reset_pool_if_server_bundle_was_modified,
25
+ :reset_pool, to: :pool
26
+
27
+ private
28
+
29
+ def gem_available?(name)
30
+ Gem::Specification.find_by_name(name)
31
+ rescue Gem::LoadError
32
+ false
33
+ rescue StandardError
34
+ Gem.available?(name)
24
35
  end
25
- # rubocop:enable Style/MethodMissing
26
36
  end
27
37
  end
28
38
  end
@@ -4,8 +4,7 @@ require "open-uri"
4
4
 
5
5
  module ReactOnRails
6
6
  module ServerRenderingPool
7
- # This implementation of the rendering pool uses ExecJS to execute javasript code
8
- class Exec
7
+ class RubyEmbeddedJavaScript
9
8
  def self.reset_pool
10
9
  options = {
11
10
  size: ReactOnRails.configuration.server_renderer_pool_size,
@@ -17,18 +16,11 @@ module ReactOnRails
17
16
  def self.reset_pool_if_server_bundle_was_modified
18
17
  return unless ReactOnRails.configuration.development_mode
19
18
 
20
- server_bundle_js_file_path = ReactOnRails::Utils.server_bundle_js_file_path
21
-
22
- if Utils.using_webpacker? && Webpacker.dev_server.running?
23
- return if @last_loaded_server_bundle == server_bundle_js_file_path
24
- @last_loaded_server_bundle = server_bundle_js_file_path
25
- else
26
- # we're not hashing the server name and we can use the mtime
27
- file_mtime = File.mtime(server_bundle_js_file_path)
28
- @server_bundle_timestamp ||= file_mtime
29
- return if @server_bundle_timestamp == file_mtime
30
- @server_bundle_timestamp = file_mtime
31
- end
19
+ file_mtime = File.mtime(ReactOnRails::Utils.server_bundle_js_file_path)
20
+ @server_bundle_timestamp ||= file_mtime
21
+ return if @server_bundle_timestamp == file_mtime
22
+
23
+ @server_bundle_timestamp = file_mtime
32
24
 
33
25
  ReactOnRails::ServerRenderingPool.reset_pool
34
26
  end
@@ -42,9 +34,10 @@ module ReactOnRails
42
34
  # js_code MUST RETURN json stringify Object
43
35
  # Calling code will probably call 'html_safe' on return value before rendering to the view.
44
36
  def self.server_render_js_with_console_logging(js_code)
45
- if trace_react_on_rails?
37
+ if ReactOnRails.configuration.trace
46
38
  @file_index ||= 1
47
- trace_messsage(js_code, "tmp/server-generated-#{@file_index % 10}.js")
39
+ trace_js_code_used("Evaluating code to server render.", js_code,
40
+ "tmp/server-generated-#{@file_index % 10}.js")
48
41
  @file_index += 1
49
42
  end
50
43
  json_string = eval_js(js_code)
@@ -68,18 +61,21 @@ module ReactOnRails
68
61
  class << self
69
62
  private
70
63
 
71
- def trace_messsage(js_code, file_name = "tmp/server-generated.js", force = false)
72
- return unless trace_react_on_rails? || force
64
+ def trace_js_code_used(msg, js_code, file_name = "tmp/server-generated.js", force: false)
65
+ return unless ReactOnRails.configuration.trace || force
73
66
  # Set to anything to print generated code.
74
- puts "Z" * 80
75
- puts "react_renderer.rb: 92"
76
- puts "wrote file #{file_name}"
77
67
  File.write(file_name, js_code)
78
- puts "Z" * 80
79
- end
80
-
81
- def trace_react_on_rails?
82
- ENV["TRACE_REACT_ON_RAILS"].present?
68
+ msg = <<-MSG.strip_heredoc
69
+ #{'Z' * 80}
70
+ [react_on_rails] #{msg}
71
+ JavaScript code used: #{file_name}
72
+ #{'Z' * 80}
73
+ MSG
74
+ if force
75
+ Rails.logger.error(msg)
76
+ else
77
+ Rails.logger.info(msg)
78
+ end
83
79
  end
84
80
 
85
81
  def eval_js(js_code)
@@ -95,34 +91,35 @@ module ReactOnRails
95
91
 
96
92
  server_js_file = ReactOnRails::Utils.server_bundle_js_file_path
97
93
 
98
- # bundle_js_code = File.read(server_js_file)
99
94
  begin
100
- bundle_js_code = open(server_js_file, &:read)
95
+ bundle_js_code = File.read(server_js_file)
101
96
  rescue StandardError => e
102
97
  msg = "You specified server rendering JS file: #{server_js_file}, but it cannot be "\
103
98
  "read. You may set the server_bundle_js_file in your configuration to be \"\" to "\
104
99
  "avoid this warning.\nError is: #{e}"
105
- raise msg
100
+ raise ReactOnRails::Error, msg
106
101
  end
107
102
  # rubocop:disable Layout/IndentHeredoc
108
103
  base_js_code = <<-JS
109
104
  #{console_polyfill}
110
- #{execjs_timer_polyfills}
111
- #{bundle_js_code};
105
+ #{execjs_timer_polyfills}
106
+ #{bundle_js_code};
112
107
  JS
113
108
  # rubocop:enable Layout/IndentHeredoc
114
109
  file_name = "tmp/base_js_code.js"
115
110
  begin
116
- trace_messsage(base_js_code, file_name)
111
+ if ReactOnRails.configuration.trace
112
+ Rails.logger.info { "[react_on_rails] Created JavaScript context with file #{server_js_file}" }
113
+ end
117
114
  ExecJS.compile(base_js_code)
118
115
  rescue StandardError => e
119
116
  msg = "ERROR when compiling base_js_code! "\
120
117
  "See file #{file_name} to "\
121
118
  "correlate line numbers of error. Error is\n\n#{e.message}"\
122
119
  "\n\n#{e.backtrace.join("\n")}"
123
- puts msg
124
120
  Rails.logger.error(msg)
125
- trace_messsage(base_js_code, file_name, true)
121
+ trace_js_code_used("Error when compiling JavaScript code for the context.", base_js_code,
122
+ file_name, force: true)
126
123
  raise e
127
124
  end
128
125
  end
@@ -158,10 +155,10 @@ function clearTimeout() {
158
155
  end
159
156
 
160
157
  def undefined_for_exec_js_logging(function_name)
161
- if trace_react_on_rails?
162
- "console.error('#{function_name} is not defined for execJS. See "\
163
- "https://github.com/sstephenson/execjs#faq. Note babel-polyfill may call this.');\n"\
164
- " console.error(getStackTrace().join('\\n'));"
158
+ if ReactOnRails.configuration.trace
159
+ "console.error('[React on Rails Rendering] #{function_name} is not defined for execJS. See "\
160
+ "https://github.com/sstephenson/execjs#faq. Note babel-polyfill may call this.');\n"\
161
+ " console.error(getStackTrace().join('\\n'));"
165
162
  else
166
163
  ""
167
164
  end
@@ -57,7 +57,7 @@ module ReactOnRails
57
57
  source_path: nil,
58
58
  generated_assets_dir: nil,
59
59
  webpack_generated_files: nil)
60
- ReactOnRails::Utils.check_manifest_not_cached
60
+ ReactOnRails::WebpackerUtils.check_manifest_not_cached
61
61
  if webpack_assets_status_checker.nil?
62
62
  source_path ||= ReactOnRails::Utils.source_path
63
63
  generated_assets_dir ||= ReactOnRails::Utils.generated_assets_dir
@@ -26,8 +26,8 @@ module ReactOnRails
26
26
  end
27
27
 
28
28
  def stale_generated_webpack_files
29
- manifest_needed = ReactOnRails::Utils.using_webpacker? &&
30
- !ReactOnRails::Utils.manifest_exists?
29
+ manifest_needed = ReactOnRails::WebpackerUtils.using_webpacker? &&
30
+ !ReactOnRails::WebpackerUtils.manifest_exists?
31
31
 
32
32
  return ["manifest.json"] if manifest_needed
33
33
 
@@ -8,33 +8,6 @@ require "active_support/core_ext/string"
8
8
 
9
9
  module ReactOnRails
10
10
  module Utils
11
- ###########################################################
12
- # PUBLIC API
13
- ###########################################################
14
-
15
- # Returns the hashed file name when using webpacker. Useful for creating cache keys.
16
- def self.bundle_file_name(bundle_name)
17
- raise "Only call bundle_file_name if using webpacker" unless using_webpacker?
18
- full_path = bundle_js_file_path_from_webpacker(bundle_name)
19
- pathname = Pathname.new(full_path)
20
- pathname.basename.to_s
21
- end
22
-
23
- # Returns the hashed file name of the server bundle when using webpacker.
24
- # Nececessary fragment-caching keys.
25
- def self.server_bundle_file_name
26
- return @server_bundle_hash if @server_bundle_hash && !Rails.env.development?
27
-
28
- @server_bundle_hash = begin
29
- server_bundle_name = ReactOnRails.configuration.server_bundle_js_file
30
- bundle_file_name(server_bundle_name)
31
- end
32
- end
33
-
34
- ###########################################################
35
- # PRIVATE API -- Subject to change
36
- ###########################################################
37
-
38
11
  # https://forum.shakacode.com/t/yak-of-the-week-ruby-2-4-pathname-empty-changed-to-look-at-file-size/901
39
12
  # return object if truthy, else return nil
40
13
  def self.truthy_presence(obj)
@@ -99,7 +72,7 @@ exitstatus: #{status.exitstatus}#{stdout_msg}#{stderr_msg}
99
72
  return @server_bundle_path if @server_bundle_path && !Rails.env.development?
100
73
 
101
74
  bundle_name = ReactOnRails.configuration.server_bundle_js_file
102
- @server_bundle_path = if using_webpacker?
75
+ @server_bundle_path = if ReactOnRails::WebpackerUtils.using_webpacker?
103
76
  begin
104
77
  bundle_js_file_path(bundle_name)
105
78
  rescue Webpacker::Manifest::MissingEntryError
@@ -112,8 +85,8 @@ exitstatus: #{status.exitstatus}#{stdout_msg}#{stderr_msg}
112
85
  end
113
86
 
114
87
  def self.bundle_js_file_path(bundle_name)
115
- if using_webpacker? && bundle_name != "manifest.json"
116
- bundle_js_file_path_from_webpacker(bundle_name)
88
+ if ReactOnRails::WebpackerUtils.using_webpacker? && bundle_name != "manifest.json"
89
+ ReactOnRails::WebpackerUtils.bundle_js_file_path_from_webpacker(bundle_name)
117
90
  else
118
91
  # Default to the non-hashed name in the specified output directory, which, for legacy
119
92
  # React on Rails, this is the output directory picked up by the asset pipeline.
@@ -141,7 +114,7 @@ exitstatus: #{status.exitstatus}#{stdout_msg}#{stderr_msg}
141
114
 
142
115
  module Required
143
116
  def required(arg_name)
144
- raise ArgumentError, "#{arg_name} is required"
117
+ raise ReactOnRailsPro::Error, "#{arg_name} is required"
145
118
  end
146
119
  end
147
120
 
@@ -150,53 +123,19 @@ exitstatus: #{status.exitstatus}#{stdout_msg}#{stderr_msg}
150
123
  end
151
124
 
152
125
  def self.source_path
153
- using_webpacker? ? webpacker_source_path : ReactOnRails.configuration.node_modules_location
126
+ if ReactOnRails::WebpackerUtils.using_webpacker?
127
+ ReactOnRails::WebpackerUtils.webpacker_source_path
128
+ else
129
+ ReactOnRails.configuration.node_modules_location
130
+ end
154
131
  end
155
132
 
156
133
  def self.generated_assets_dir
157
- using_webpacker? ? webpacker_public_output_path : ReactOnRails.configuration.generated_assets_dir
158
- end
159
-
160
- ###########################################################################
161
- # WEBPACKER WRAPPERS
162
- ###########################################################################
163
- def self.using_webpacker?
164
- ActionController::Base.helpers.respond_to?(:asset_pack_path)
165
- end
166
-
167
- def self.bundle_js_file_path_from_webpacker(bundle_name)
168
- possible_result = Webpacker.manifest.lookup(bundle_name)
169
- hashed_bundle_name = possible_result.nil? ? Webpacker.manifest.lookup!(bundle_name) : possible_result
170
- if Webpacker.dev_server.running?
171
- result = "#{Webpacker.dev_server.protocol}://#{Webpacker.dev_server.host_with_port}#{hashed_bundle_name}"
172
- result
134
+ if ReactOnRails::WebpackerUtils.using_webpacker?
135
+ ReactOnRails::WebpackerUtils.webpacker_public_output_path
173
136
  else
174
- # Next line will throw if the file or manifest does not exist
175
- Rails.root.join(File.join("public", hashed_bundle_name)).to_s
137
+ ReactOnRails.configuration.generated_assets_dir
176
138
  end
177
139
  end
178
-
179
- def self.webpacker_source_path
180
- Webpacker.config.source_path
181
- end
182
-
183
- def self.webpacker_public_output_path
184
- Webpacker.config.public_output_path
185
- end
186
-
187
- def self.manifest_exists?
188
- Webpacker.config.public_manifest_path.exist?
189
- end
190
-
191
- def self.check_manifest_not_cached
192
- return unless using_webpacker? && Webpacker.config.cache_manifest?
193
- msg = <<-MSG.strip_heredoc
194
- ERROR: you have enabled cache_manifest in the #{Rails.env} env when using the
195
- ReactOnRails::TestHelper.configure_rspec_to_compile_assets helper
196
- To fix this: edit your config/webpacker.yml file and set cache_manifest to false for test.
197
- MSG
198
- puts wrap_message(msg)
199
- exit!
200
- end
201
140
  end
202
141
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ReactOnRails
4
- VERSION = "10.1.4".freeze
4
+ VERSION = "11.0.0.beta.1".freeze
5
5
  end
@@ -71,7 +71,7 @@ module ReactOnRails
71
71
  parsed_package_contents["dependencies"].key?("react-on-rails")
72
72
  parsed_package_contents["dependencies"]["react-on-rails"]
73
73
  else
74
- raise "no 'react-on-rails' entry in package.json dependencies"
74
+ raise ReactOnRails::Error, "No 'react-on-rails' entry in package.json dependencies"
75
75
  end
76
76
  end
77
77
 
@@ -82,7 +82,9 @@ module ReactOnRails
82
82
  def major_minor_patch
83
83
  return if relative_path?
84
84
  match = raw.match(MAJOR_MINOR_PATCH_VERSION_REGEX)
85
- raise "Cannot parse version number '#{raw}' (wildcard versions are not supported)" unless match
85
+ unless match
86
+ raise ReactOnRails::Error, "Cannot parse version number '#{raw}' (wildcard versions are not supported)"
87
+ end
86
88
  [match[1], match[2], match[3]]
87
89
  end
88
90
 
@@ -0,0 +1,42 @@
1
+ module ReactOnRails
2
+ module WebpackerUtils
3
+ def self.using_webpacker?
4
+ ActionController::Base.helpers.respond_to?(:asset_pack_path)
5
+ end
6
+
7
+ def self.bundle_js_file_path_from_webpacker(bundle_name)
8
+ possible_result = Webpacker.manifest.lookup(bundle_name)
9
+ hashed_bundle_name = possible_result.nil? ? Webpacker.manifest.lookup!(bundle_name) : possible_result
10
+ if Webpacker.dev_server.running?
11
+ result = "#{Webpacker.dev_server.protocol}://#{Webpacker.dev_server.host_with_port}#{hashed_bundle_name}"
12
+ result
13
+ else
14
+ # Next line will throw if the file or manifest does not exist
15
+ Rails.root.join(File.join("public", hashed_bundle_name)).to_s
16
+ end
17
+ end
18
+
19
+ def self.webpacker_source_path
20
+ Webpacker.config.source_path
21
+ end
22
+
23
+ def self.webpacker_public_output_path
24
+ Webpacker.config.public_output_path
25
+ end
26
+
27
+ def self.manifest_exists?
28
+ Webpacker.config.public_manifest_path.exist?
29
+ end
30
+
31
+ def self.check_manifest_not_cached
32
+ return unless using_webpacker? && Webpacker.config.cache_manifest?
33
+ msg = <<-MSG.strip_heredoc
34
+ ERROR: you have enabled cache_manifest in the #{Rails.env} env when using the
35
+ ReactOnRails::TestHelper.configure_rspec_to_compile_assets helper
36
+ To fix this: edit your config/webpacker.yml file and set cache_manifest to false for test.
37
+ MSG
38
+ puts wrap_message(msg)
39
+ exit!
40
+ end
41
+ end
42
+ end
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-on-rails",
3
- "version": "10.1.4",
3
+ "version": "11.0.0-beta.1",
4
4
  "description": "react-on-rails JavaScript for react_on_rails Ruby gem",
5
5
  "main": "node_package/lib/ReactOnRails.js",
6
6
  "directories": {
@@ -11,7 +11,7 @@ module ReactOnRails
11
11
  module TaskHelpers
12
12
  class ExampleType
13
13
  def self.all
14
- @example_types ||= []
14
+ @all ||= []
15
15
  end
16
16
 
17
17
  def self.namespace_name
@@ -12,7 +12,7 @@ require_relative "task_helpers"
12
12
  namespace :examples do # rubocop:disable Metrics/BlockLength
13
13
  include ReactOnRails::TaskHelpers
14
14
  # Loads data from examples_config.yml and instantiates corresponding ExampleType objects
15
- examples_config_file = File.expand_path("../examples_config.yml", __FILE__)
15
+ examples_config_file = File.expand_path("examples_config.yml", __dir__)
16
16
  examples_config = symbolize_keys(YAML.safe_load(File.read(examples_config_file)))
17
17
  examples_config[:example_type_data].each { |example_type_data| ExampleType.new(symbolize_keys(example_type_data)) }
18
18
 
data/rakelib/release.rake CHANGED
@@ -4,9 +4,6 @@ require_relative "task_helpers"
4
4
  require_relative File.join(gem_root, "lib", "react_on_rails", "version_syntax_converter")
5
5
  require_relative File.join(gem_root, "lib", "react_on_rails", "git_utils")
6
6
  require_relative File.join(gem_root, "lib", "react_on_rails", "utils")
7
-
8
- # rubocop:disable Lint/UnneededDisable
9
- # rubocop:disable Layout/EmptyLinesAroundArguments
10
7
  desc("Releases both the gem and node package using the given version.
11
8
 
12
9
  IMPORTANT: the gem version must be in valid rubygem format (no dashes).
@@ -22,8 +19,6 @@ which are installed via `bundle install` and `yarn`
22
19
  2nd argument: Perform a dry run by passing 'true' as a second argument.
23
20
 
24
21
  Example: `rake release[2.1.0,false]`")
25
- # rubocop:enable Layout/EmptyLinesAroundArguments
26
- # rubocop:enable Lint/UnneededDisable
27
22
 
28
23
  # rubocop:disable Metrics/BlockLength
29
24
  task :release, %i[gem_version dry_run tools_install] do |_t, args|