react-rails 1.9.0 → 3.2.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 (61) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +345 -9
  3. data/README.md +137 -389
  4. data/lib/assets/javascripts/JSXTransformer.js +6 -6
  5. data/lib/assets/javascripts/react_ujs.js +1 -7
  6. data/lib/assets/react-source/development/react-server.js +423 -21528
  7. data/lib/assets/react-source/development/react.js +191 -21409
  8. data/lib/assets/react-source/production/react-server.js +2 -19
  9. data/lib/assets/react-source/production/react.js +2 -19
  10. data/lib/generators/react/component_generator.rb +203 -73
  11. data/lib/generators/react/install_generator.rb +98 -25
  12. data/lib/generators/templates/component.es6.jsx +9 -12
  13. data/lib/generators/templates/component.es6.tsx +24 -0
  14. data/lib/generators/templates/component.js.jsx +15 -18
  15. data/lib/generators/templates/component.js.jsx.coffee +5 -8
  16. data/lib/generators/templates/component.js.jsx.tsx +36 -0
  17. data/lib/generators/templates/react_server_rendering.rb +4 -0
  18. data/lib/generators/templates/server_rendering.js +6 -0
  19. data/lib/generators/templates/server_rendering_pack.js +5 -0
  20. data/lib/react/jsx/babel_transformer.rb +12 -6
  21. data/lib/react/jsx/jsx_transformer.rb +7 -6
  22. data/lib/react/jsx/processor.rb +3 -1
  23. data/lib/react/jsx/sprockets_strategy.rb +12 -6
  24. data/lib/react/jsx/template.rb +7 -6
  25. data/lib/react/jsx.rb +11 -7
  26. data/lib/react/rails/asset_variant.rb +7 -8
  27. data/lib/react/rails/component_mount.rb +48 -14
  28. data/lib/react/rails/controller_lifecycle.rb +36 -7
  29. data/lib/react/rails/controller_renderer.rb +13 -4
  30. data/lib/react/rails/railtie.rb +34 -29
  31. data/lib/react/rails/test_helper.rb +25 -0
  32. data/lib/react/rails/version.rb +4 -2
  33. data/lib/react/rails/view_helper.rb +3 -1
  34. data/lib/react/rails.rb +9 -7
  35. data/lib/react/server_rendering/{sprockets_renderer → bundle_renderer}/console_replay.js +1 -1
  36. data/lib/react/server_rendering/bundle_renderer/console_reset.js +3 -0
  37. data/lib/react/server_rendering/bundle_renderer/timeout_polyfill.js +26 -0
  38. data/lib/react/server_rendering/bundle_renderer.rb +117 -0
  39. data/lib/react/server_rendering/environment_container.rb +2 -0
  40. data/lib/react/server_rendering/exec_js_renderer.rb +43 -16
  41. data/lib/react/server_rendering/manifest_container.rb +5 -1
  42. data/lib/react/server_rendering/separate_server_bundle_container.rb +19 -0
  43. data/lib/react/server_rendering/yaml_manifest_container.rb +12 -4
  44. data/lib/react/server_rendering.rb +26 -12
  45. data/lib/react-rails.rb +12 -4
  46. data/lib/react.rb +8 -4
  47. metadata +106 -41
  48. data/lib/assets/javascripts/react_ujs_event_setup.js +0 -29
  49. data/lib/assets/javascripts/react_ujs_mount.js +0 -104
  50. data/lib/assets/javascripts/react_ujs_native.js +0 -18
  51. data/lib/assets/javascripts/react_ujs_pjax.js +0 -10
  52. data/lib/assets/javascripts/react_ujs_turbolinks.js +0 -9
  53. data/lib/assets/javascripts/react_ujs_turbolinks_classic.js +0 -10
  54. data/lib/assets/javascripts/react_ujs_turbolinks_classic_deprecated.js +0 -13
  55. data/lib/assets/react-source/development-with-addons/react-server.js +0 -24053
  56. data/lib/assets/react-source/development-with-addons/react.js +0 -23890
  57. data/lib/assets/react-source/production-with-addons/react-server.js +0 -19
  58. data/lib/assets/react-source/production-with-addons/react.js +0 -19
  59. data/lib/generators/react/ujs_generator.rb +0 -44
  60. data/lib/react/server_rendering/sprockets_renderer.rb +0 -79
  61. /data/lib/react/server_rendering/{sprockets_renderer → bundle_renderer}/console_polyfill.js +0 -0
@@ -1,21 +1,27 @@
1
- require 'babel/transpiler'
1
+ # frozen_string_literal: true
2
+
3
+ require "babel/transpiler"
2
4
  module React
3
5
  module JSX
4
6
  # A {React::JSX}-compliant transformer which uses `Babel::Transpiler` to transform JSX.
5
7
  class BabelTransformer
6
- DEPRECATED_OPTIONS = [:harmony, :strip_types, :asset_path]
7
- DEFAULT_TRANSFORM_OPTIONS = { blacklist: ['spec.functionName', 'validation.react', 'strict'] }
8
+ DEPRECATED_OPTIONS = %i[harmony strip_types asset_path].freeze
9
+ DEFAULT_TRANSFORM_OPTIONS = { blacklist: ["spec.functionName", "validation.react", "strict"] }.freeze
8
10
  def initialize(options)
9
11
  if (options.keys & DEPRECATED_OPTIONS).any?
10
- ActiveSupport::Deprecation.warn("Setting config.react.jsx_transform_options for :harmony, :strip_types, and :asset_path keys is now deprecated and has no effect with the default Babel Transformer."+
11
- "Please use new Babel Transformer options :whitelist, :plugin instead.")
12
+ ActiveSupport::Deprecation.warn(
13
+ <<-MSG
14
+ Setting config.react.jsx_transform_options for :harmony, :strip_types, and :asset_path keys is now deprecated and has no effect with the default Babel Transformer.
15
+ Please use new Babel Transformer options :whitelist, :plugin instead.
16
+ MSG
17
+ )
12
18
  end
13
19
 
14
20
  @transform_options = DEFAULT_TRANSFORM_OPTIONS.merge(options)
15
21
  end
16
22
 
17
23
  def transform(code)
18
- Babel::Transpiler.transform(code, @transform_options)['code']
24
+ Babel::Transpiler.transform(code, @transform_options)["code"]
19
25
  end
20
26
  end
21
27
  end
@@ -1,31 +1,32 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module React
2
4
  module JSX
3
5
  # A {React::JSX}-compliant transformer which uses the deprecated `JSXTransformer.js` to transform JSX.
4
6
  class JSXTransformer
5
- DEFAULT_ASSET_PATH = 'JSXTransformer.js'
7
+ DEFAULT_ASSET_PATH = "JSXTransformer.js"
6
8
 
7
9
  def initialize(options)
8
10
  @transform_options = {
9
11
  stripTypes: options.fetch(:strip_types, false),
10
- harmony: options.fetch(:harmony, false),
12
+ harmony: options.fetch(:harmony, false)
11
13
  }
12
14
 
13
15
  @asset_path = options.fetch(:asset_path, DEFAULT_ASSET_PATH)
14
16
 
15
17
  # If execjs uses therubyracer, there is no 'global'. Make sure
16
18
  # we have it so JSX script can work properly.
17
- js_code = 'var global = global || this;' + jsx_transform_code
19
+ js_code = "var global = global || this;#{jsx_transform_code}"
18
20
  @context = ExecJS.compile(js_code)
19
21
  end
20
22
 
21
-
22
23
  def transform(code)
23
- result = @context.call('JSXTransformer.transform', code, @transform_options)
24
+ result = @context.call("JSXTransformer.transform", code, @transform_options)
24
25
  result["code"]
25
26
  end
26
27
 
27
28
  # search for transformer file using sprockets - allows user to override
28
- # this file in his own application
29
+ # this file in their own application
29
30
  def jsx_transform_code
30
31
  ::Rails.application.assets[@asset_path].to_s
31
32
  end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module React
2
4
  module JSX
3
5
  # A Sprockets 3+-compliant processor
4
6
  class Processor
5
7
  def self.call(input)
6
- JSX::transform(input[:data])
8
+ JSX.transform(input[:data])
7
9
  end
8
10
  end
9
11
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module React
2
4
  module JSX
3
5
  # Depending on the Sprockets version,
@@ -17,13 +19,13 @@ module React
17
19
  # @param [Symbol, Nil] A strategy name, or `nil` to detect a strategy
18
20
  def attach_with_strategy(sprockets_env, strategy_or_nil)
19
21
  strategy = strategy_or_nil || detect_strategy
20
- self.public_send(strategy, sprockets_env)
22
+ public_send(strategy, sprockets_env)
21
23
  end
22
24
 
23
25
  # @return [Symbol] based on the environment, return a method name to call with the sprockets environment
24
26
  def detect_strategy
25
27
  sprockets_version = Gem::Version.new(Sprockets::VERSION)
26
- if sprockets_version >= Gem::Version.new("4.x")
28
+ if sprockets_version >= Gem::Version.new("4.a")
27
29
  :register_processors
28
30
  elsif sprockets_version >= Gem::Version.new("3.0.0")
29
31
  :register_engine_with_mime_type
@@ -37,16 +39,20 @@ module React
37
39
  end
38
40
 
39
41
  def register_engine_with_mime_type(sprockets_env)
40
- sprockets_env.register_engine(".jsx", React::JSX::Processor, mime_type: "application/javascript", silence_deprecation: true)
42
+ sprockets_env.register_engine(".jsx", React::JSX::Processor, mime_type: "application/javascript",
43
+ silence_deprecation: true)
41
44
  end
42
45
 
43
46
  def register_processors(sprockets_env)
44
47
  sprockets_env.register_mime_type("application/jsx", extensions: [".jsx", ".js.jsx", ".es.jsx", ".es6.jsx"])
45
48
  sprockets_env.register_mime_type("application/jsx+coffee", extensions: [".jsx.coffee", ".js.jsx.coffee"])
46
49
  sprockets_env.register_transformer("application/jsx", "application/javascript", React::JSX::Processor)
47
- sprockets_env.register_transformer("application/jsx+coffee", "application/jsx", Sprockets::CoffeeScriptProcessor)
48
- sprockets_env.register_preprocessor("application/jsx", Sprockets::DirectiveProcessor.new(comments: ["//", ["/*", "*/"]]))
49
- sprockets_env.register_preprocessor("application/jsx+coffee", Sprockets::DirectiveProcessor.new(comments: ["#", ["###", "###"]]))
50
+ sprockets_env.register_transformer("application/jsx+coffee", "application/jsx",
51
+ Sprockets::CoffeeScriptProcessor)
52
+ sprockets_env.register_preprocessor("application/jsx",
53
+ Sprockets::DirectiveProcessor.new(comments: ["//", ["/*", "*/"]]))
54
+ sprockets_env.register_preprocessor("application/jsx+coffee",
55
+ Sprockets::DirectiveProcessor.new(comments: ["#", ["###", "###"]]))
50
56
  end
51
57
  end
52
58
  end
@@ -1,16 +1,17 @@
1
- require 'tilt'
1
+ # frozen_string_literal: true
2
+
3
+ require "tilt"
2
4
 
3
5
  module React
4
6
  module JSX
5
7
  # Sprockets 2-compliant processor
6
8
  class Template < Tilt::Template
7
- self.default_mime_type = 'application/javascript'
9
+ self.default_mime_type = "application/javascript"
8
10
 
9
- def prepare
10
- end
11
+ def prepare; end
11
12
 
12
- def evaluate(scope, locals, &block)
13
- @output ||= JSX::transform(data)
13
+ def evaluate(_scope, _locals)
14
+ @evaluate ||= JSX.transform(data)
14
15
  end
15
16
  end
16
17
  end
data/lib/react/jsx.rb CHANGED
@@ -1,10 +1,12 @@
1
- require 'execjs'
2
- require 'react/jsx/processor'
3
- require 'react/jsx/template'
4
- require 'react/jsx/jsx_transformer'
5
- require 'react/jsx/babel_transformer'
6
- require 'react/jsx/sprockets_strategy'
7
- require 'rails'
1
+ # frozen_string_literal: true
2
+
3
+ require "execjs"
4
+ require "react/jsx/processor"
5
+ require "react/jsx/template"
6
+ require "react/jsx/jsx_transformer"
7
+ require "react/jsx/babel_transformer"
8
+ require "react/jsx/sprockets_strategy"
9
+ require "rails"
8
10
 
9
11
  module React
10
12
  module JSX
@@ -17,6 +19,8 @@ module React
17
19
  # - #transform(code) => new code
18
20
  self.transformer_class = DEFAULT_TRANSFORMER
19
21
 
22
+ # @param code [String] JSX code to transform into JavaScript
23
+ # @return [String] plain, browser-ready JavaScript code
20
24
  def self.transform(code)
21
25
  self.transformer ||= transformer_class.new(transform_options)
22
26
  self.transformer.transform(code)
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module React
2
4
  module Rails
3
5
  # This class accepts some options for which build you want, then exposes where you can find
4
6
  # them. In general, these paths should be added to the sprockets environment.
5
7
  class AssetVariant
6
- GEM_ROOT = Pathname.new('../../../../').expand_path(__FILE__)
8
+ GEM_ROOT = Pathname.new("../../../../").expand_path(__FILE__)
7
9
  # @return [String] "production" or "development"
8
10
  attr_reader :react_build
9
11
 
@@ -15,14 +17,11 @@ module React
15
17
 
16
18
  # @param [Hash] Options for the asset variant
17
19
  # @option variant [Symbol] if `:production`, use the minified React.js build
18
- # @option addons [Boolean] if true, use a React.js build with all addons
19
- def initialize(options={})
20
-
21
- @react_build = options[:variant] == :production ? 'production' : 'development'
22
- options[:addons] && @react_build += '-with-addons'
20
+ def initialize(options = {})
21
+ @react_build = options[:variant] == :production ? "production" : "development"
23
22
 
24
- @react_directory = GEM_ROOT.join('lib/assets/react-source/').join(@react_build).to_s
25
- @jsx_directory = GEM_ROOT.join('lib/assets/javascripts/').to_s
23
+ @react_directory = GEM_ROOT.join("lib/assets/react-source/").join(@react_build).to_s
24
+ @jsx_directory = GEM_ROOT.join("lib/assets/javascripts/").to_s
26
25
  end
27
26
  end
28
27
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module React
2
4
  module Rails
3
5
  # This is the default view helper implementation.
@@ -9,43 +11,75 @@ module React
9
11
  include ActionView::Helpers::TagHelper
10
12
  include ActionView::Helpers::TextHelper
11
13
  attr_accessor :output_buffer
14
+
12
15
  mattr_accessor :camelize_props_switch
13
16
 
14
- # ControllerLifecycle calls these hooks
15
- # You can use them in custom helper implementations
16
- def setup(env)
17
+ def initialize
18
+ @cache_ids = []
17
19
  end
18
20
 
19
- def teardown(env)
21
+ # {ControllerLifecycle} calls these hooks
22
+ # You can use them in custom helper implementations
23
+ def setup(controller)
24
+ @controller = controller
20
25
  end
21
26
 
27
+ def teardown(controller); end
28
+
22
29
  # Render a UJS-type HTML tag annotated with data attributes, which
23
30
  # are used by react_ujs to actually instantiate the React component
24
31
  # on the client.
25
32
  def react_component(name, props = {}, options = {}, &block)
26
- options = {:tag => options} if options.is_a?(Symbol)
27
- if camelize_props_switch
28
- props = React.camelize_props(props)
29
- end
33
+ options = { tag: options } if options.is_a?(Symbol)
34
+ props = React.camelize_props(props) if options.fetch(:camelize_props, camelize_props_switch)
30
35
 
31
36
  prerender_options = options[:prerender]
32
- if prerender_options
33
- block = Proc.new{ concat React::ServerRendering.render(name, props, prerender_options) }
34
- end
37
+ block = proc { concat(prerender_component(name, props, prerender_options)) } if prerender_options
38
+
39
+ html_options = generate_html_options(name, options, props, prerender_options)
40
+
41
+ rendered_tag(html_options, &block)
42
+ end
43
+
44
+ private
45
+
46
+ # If this controller has checked out a renderer, use that one.
47
+ # Otherwise, use {React::ServerRendering} directly (which will check one out for this rendering).
48
+ def prerender_component(component_name, props, prerender_options)
49
+ renderer = @controller.try(:react_rails_prerenderer) || React::ServerRendering
50
+ renderer.render(component_name, props, prerender_options)
51
+ end
52
+
53
+ def generate_html_options(name, options, props, prerender_options)
54
+ html_options = options.reverse_merge(data: {})
35
55
 
36
- html_options = options.reverse_merge(:data => {})
37
56
  unless prerender_options == :static
38
57
  html_options[:data].tap do |data|
39
58
  data[:react_class] = name
40
59
  data[:react_props] = (props.is_a?(String) ? props : props.to_json)
60
+ data[:hydrate] = "t" if prerender_options
61
+
62
+ num_components = @cache_ids.count { |c| c.start_with? name }
63
+ data[:react_cache_id] = "#{name}-#{num_components}"
41
64
  end
42
65
  end
66
+
67
+ html_options
68
+ end
69
+
70
+ def rendered_tag(html_options, &block)
43
71
  html_tag = html_options[:tag] || :div
44
72
 
45
73
  # remove internally used properties so they aren't rendered to DOM
46
- html_options.except!(:tag, :prerender)
74
+ html_option_to_use = html_options.except(:tag, :prerender, :camelize_props)
75
+
76
+ tag = content_tag(html_tag, "", html_option_to_use, &block)
77
+ return tag unless React::ServerRendering.renderer_options[:replay_console]
47
78
 
48
- content_tag(html_tag, '', html_options, &block)
79
+ # Grab the server-rendered console replay script
80
+ # and move it _outside_ the container div
81
+ tag.sub!(%r{\n(<script class="react-rails-console-replay">.*</script>)</(\w+)>$}m, '</\2>\1')
82
+ tag.html_safe
49
83
  end
50
84
  end
51
85
  end
@@ -1,25 +1,54 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module React
2
4
  module Rails
5
+ # This module is included into ActionController so that
6
+ # per-request hooks can be called in the view helper.
3
7
  module ControllerLifecycle
4
8
  extend ActiveSupport::Concern
5
9
 
6
10
  included do
7
11
  # use both names to support Rails 3..5
8
- before_action_with_fallback = respond_to?(:before_action) ? :before_action : :before_filter
9
- after_action_with_fallback = respond_to?(:after_action) ? :after_action : :after_filter
10
- public_send(before_action_with_fallback, :setup_react_component_helper)
11
- public_send(after_action_with_fallback, :teardown_react_component_helper)
12
+ around_action_with_fallback = respond_to?(:around_action) ? :around_action : :around_filter
13
+ public_send(around_action_with_fallback, :use_react_component_helper)
12
14
  attr_reader :__react_component_helper
13
15
  end
14
16
 
15
- def setup_react_component_helper
17
+ module ClassMethods
18
+ # Call this in the controller to check out a prerender for the whole request.
19
+ # You can access the renderer with {#react_rails_prerenderer}.
20
+ def per_request_react_rails_prerenderer
21
+ around_action_with_fallback = respond_to?(:around_action) ? :around_action : :around_filter
22
+ public_send(around_action_with_fallback, :per_request_react_rails_prerenderer)
23
+ end
24
+ end
25
+
26
+ # Instantiate the ViewHelper implementation and call its #setup method
27
+ # then let the controller action run,
28
+ # then call the ViewHelper implementation's #teardown method
29
+ def use_react_component_helper
16
30
  new_helper = React::Rails::ViewHelper.helper_implementation_class.new
17
31
  new_helper.setup(self)
18
32
  @__react_component_helper = new_helper
33
+ yield
34
+ @__react_component_helper.teardown(self)
19
35
  end
20
36
 
21
- def teardown_react_component_helper
22
- @__react_component_helper.teardown(self)
37
+ # If you want a per-request renderer, add this method as an around-action
38
+ #
39
+ # (`.per_request_react_rails_prerenderer` does this for you)
40
+ # @example Having one renderer instance for each controller action
41
+ # around_action :per_request_react_rails_prerenderer
42
+ def per_request_react_rails_prerenderer
43
+ React::ServerRendering.with_renderer do |renderer|
44
+ @__react_rails_prerenderer = renderer
45
+ yield
46
+ end
47
+ end
48
+
49
+ # An instance of a server renderer, for use during this request
50
+ def react_rails_prerenderer
51
+ @__react_rails_prerenderer
23
52
  end
24
53
  end
25
54
  end
@@ -1,9 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module React
2
4
  module Rails
3
5
  # A renderer class suitable for `ActionController::Renderers`.
4
6
  # It is associated to `:component` in the Railtie.
5
7
  #
6
- # It is prerendered with {React::ServerRendering}.
8
+ # It is prerendered by default with {React::ServerRendering}.
9
+ # Set options[:prerender] to `false` to disable prerendering.
7
10
  #
8
11
  # @example Rendering a component from a controller
9
12
  # class TodosController < ApplicationController
@@ -19,17 +22,23 @@ module React
19
22
 
20
23
  attr_accessor :output_buffer
21
24
 
22
- def initialize(options={})
25
+ def initialize(options = {})
23
26
  controller = options[:controller]
24
27
  @__react_component_helper = controller.__react_component_helper
25
28
  end
26
29
 
27
- # @return [String] Prerendered HTML for `component_name` with `options[:props]`
30
+ # @return [String] HTML for `component_name` with `options[:props]`
28
31
  def call(component_name, options, &block)
29
32
  props = options.fetch(:props, {})
30
- options = options.slice(:data, :aria, :tag, :class, :id).merge(prerender: true)
33
+ options = default_options.merge(options.slice(:data, :aria, :tag, :class, :id, :prerender, :camelize_props))
31
34
  react_component(component_name, props, options, &block)
32
35
  end
36
+
37
+ private
38
+
39
+ def default_options
40
+ { prerender: true }
41
+ end
33
42
  end
34
43
  end
35
44
  end
@@ -1,4 +1,6 @@
1
- require 'rails'
1
+ # frozen_string_literal: true
2
+
3
+ require "rails"
2
4
 
3
5
  module React
4
6
  module Rails
@@ -6,7 +8,6 @@ module React
6
8
  config.react = ActiveSupport::OrderedOptions.new
7
9
  # Sensible defaults. Can be overridden in application.rb
8
10
  config.react.variant = (::Rails.env.production? ? :production : :development)
9
- config.react.addons = false
10
11
  config.react.jsx_transform_options = {}
11
12
  config.react.jsx_transformer_class = nil # defaults to BabelTransformer
12
13
  config.react.camelize_props = false # pass in an underscored hash but get a camelized hash
@@ -15,19 +16,34 @@ module React
15
16
  # Server rendering:
16
17
  config.react.server_renderer_pool_size = 1 # increase if you're on JRuby
17
18
  config.react.server_renderer_timeout = 20 # seconds
18
- config.react.server_renderer = nil # defaults to SprocketsRenderer
19
- config.react.server_renderer_options = {} # SprocketsRenderer provides defaults
19
+ config.react.server_renderer = nil # defaults to BundleRenderer
20
+ config.react.server_renderer_options = {} # BundleRenderer provides defaults
21
+ # Changing files with these extensions in these directories will cause the server renderer to reload:
22
+ config.react.server_renderer_directories = ["/app/assets/javascripts/", "app/javascript"]
23
+ config.react.server_renderer_extensions = %w[jsx js]
20
24
  # View helper implementation:
21
25
  config.react.view_helper_implementation = nil # Defaults to ComponentMount
22
26
 
23
27
  # Watch .jsx files for changes in dev, so we can reload the JS VMs with the new JS code.
24
- initializer "react_rails.add_watchable_files", group: :all do |app|
25
- app.config.watchable_files.concat Dir["#{app.root}/app/assets/javascripts/**/*.jsx*"]
28
+ initializer "react_rails.add_watchable_files", after: :load_config_initializers, group: :all do |app|
29
+ # Watch files ending in `server_renderer_extensions` in each of `server_renderer_directories`
30
+ reload_paths = config.react.server_renderer_directories.each_with_object({}) do |dir, memo|
31
+ app_dir = File.join(app.root, dir)
32
+ memo[app_dir] = config.react.server_renderer_extensions
33
+ end
34
+
35
+ # Rails checks these objects for changes:
36
+ react_reloader = app.config.file_watcher.new([], reload_paths) do
37
+ React::ServerRendering.reset_pool
38
+ end
39
+ app.reloaders << react_reloader
40
+
41
+ # Reload renderers in dev when files change
42
+ config.to_prepare { react_reloader.execute_if_updated }
26
43
  end
27
44
 
28
45
  # Include the react-rails view helper lazily
29
46
  initializer "react_rails.setup_view_helpers", after: :load_config_initializers, group: :all do |app|
30
-
31
47
  app.config.react.jsx_transformer_class ||= React::JSX::DEFAULT_TRANSFORMER
32
48
  React::JSX.transformer_class = app.config.react.jsx_transformer_class
33
49
  React::JSX.transform_options = app.config.react.jsx_transform_options
@@ -42,10 +58,11 @@ module React
42
58
 
43
59
  ActiveSupport.on_load(:action_view) do
44
60
  include ::React::Rails::ViewHelper
61
+ ActionDispatch::IntegrationTest.include React::Rails::TestHelper if ::Rails.env.test?
45
62
  end
46
63
  end
47
64
 
48
- initializer "react_rails.add_component_renderer", group: :all do |app|
65
+ initializer "react_rails.add_component_renderer", group: :all do |_app|
49
66
  ActionController::Renderers.add :component do |component_name, options|
50
67
  renderer = ::React::Rails::ControllerRenderer.new(controller: self)
51
68
  html = renderer.call(component_name, options)
@@ -56,22 +73,19 @@ module React
56
73
 
57
74
  initializer "react_rails.bust_cache", after: :load_config_initializers, group: :all do |app|
58
75
  asset_variant = React::Rails::AssetVariant.new({
59
- variant: app.config.react.variant,
60
- addons: app.config.react.addons,
61
- })
76
+ variant: app.config.react.variant
77
+ })
62
78
 
63
79
  sprockets_env = app.assets || app.config.try(:assets) # sprockets-rails 3.x attaches this at a different config
64
- if !sprockets_env.nil?
65
- sprockets_env.version = [sprockets_env.version, "react-#{asset_variant.react_build}",].compact.join('-')
80
+ unless sprockets_env.nil?
81
+ sprockets_env.version = [sprockets_env.version, "react-#{asset_variant.react_build}"].compact.join("-")
66
82
  end
67
-
68
83
  end
69
84
 
70
85
  initializer "react_rails.set_variant", after: :engines_blank_point, group: :all do |app|
71
86
  asset_variant = React::Rails::AssetVariant.new({
72
- variant: app.config.react.variant,
73
- addons: app.config.react.addons,
74
- })
87
+ variant: app.config.react.variant
88
+ })
75
89
 
76
90
  if app.config.respond_to?(:assets)
77
91
  app.config.assets.paths << asset_variant.react_directory
@@ -81,32 +95,23 @@ module React
81
95
 
82
96
  config.after_initialize do |app|
83
97
  # The class isn't accessible in the configure block, so assign it here if it wasn't overridden:
84
- app.config.react.server_renderer ||= React::ServerRendering::SprocketsRenderer
98
+ app.config.react.server_renderer ||= React::ServerRendering::BundleRenderer
85
99
 
86
100
  React::ServerRendering.pool_size = app.config.react.server_renderer_pool_size
87
101
  React::ServerRendering.pool_timeout = app.config.react.server_renderer_timeout
88
102
  React::ServerRendering.renderer_options = app.config.react.server_renderer_options
89
103
  React::ServerRendering.renderer = app.config.react.server_renderer
90
-
91
104
  React::ServerRendering.reset_pool
92
- # Reload renderers in dev when files change
93
- if Gem::Version.new(::Rails::VERSION::STRING) >= Gem::Version.new("5.x")
94
- ActiveSupport::Reloader.to_prepare { React::ServerRendering.reset_pool }
95
- else
96
- ActionDispatch::Reloader.to_prepare { React::ServerRendering.reset_pool }
97
- end
98
105
  end
99
106
 
100
- initializer "react_rails.setup_engine", :group => :all do |app|
107
+ initializer "react_rails.setup_engine", group: :all do |app|
101
108
  # Sprockets 3.x expects this in a different place
102
- sprockets_env = app.assets || defined?(Sprockets) && Sprockets
109
+ sprockets_env = app.assets || (defined?(Sprockets) && Sprockets)
103
110
 
104
111
  if app.config.react.sprockets_strategy == false
105
112
  # pass, Sprockets opt-out
106
113
  elsif sprockets_env.present?
107
114
  React::JSX::SprocketsStrategy.attach_with_strategy(sprockets_env, app.config.react.sprockets_strategy)
108
- else
109
- # pass, Sprockets is not preset
110
115
  end
111
116
  end
112
117
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module React
4
+ module Rails
5
+ module TestHelper
6
+ extend ActiveSupport::Concern
7
+
8
+ # assert react_component render
9
+ #
10
+ # assert_react_component("HelloWorld") do |props|
11
+ # assert_equal "Hello world", props[:message]
12
+ # end
13
+ def assert_react_component(name)
14
+ assert_select "div[data-react-class=?]", name do |dom|
15
+ if block_given?
16
+ props = JSON.parse(dom.attr("data-react-props"))
17
+ props.deep_symbolize_keys!
18
+
19
+ yield(props)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module React
2
4
  module Rails
3
5
  # If you change this, make sure to update VERSIONS.md
4
- # And the version hint in README.md, if needed
5
- VERSION = "1.9.0"
6
+ # and republish the UJS by updating package.json and `bundle exec rake ujs:publish`
7
+ VERSION = "3.2.1"
6
8
  end
7
9
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module React
2
4
  module Rails
3
5
  module ViewHelper
@@ -18,7 +20,7 @@ module React
18
20
  # Otherwise, make a new instance.
19
21
  def react_component(*args, &block)
20
22
  helper_obj = @__react_component_helper ||= helper_implementation_class.new
21
- helper_obj.react_component(*args, &block)
23
+ helper_obj.react_component(*args) { capture(&block) if block }
22
24
  end
23
25
  end
24
26
  end
data/lib/react/rails.rb CHANGED
@@ -1,7 +1,9 @@
1
- require 'react/rails/asset_variant'
2
- require 'react/rails/railtie'
3
- require 'react/rails/controller_lifecycle'
4
- require 'react/rails/version'
5
- require 'react/rails/component_mount'
6
- require 'react/rails/view_helper'
7
- require 'react/rails/controller_renderer'
1
+ # frozen_string_literal: true
2
+
3
+ require "react/rails/asset_variant"
4
+ require "react/rails/railtie"
5
+ require "react/rails/controller_lifecycle"
6
+ require "react/rails/version"
7
+ require "react/rails/component_mount"
8
+ require "react/rails/view_helper"
9
+ require "react/rails/controller_renderer"
@@ -1,6 +1,6 @@
1
1
  (function (history) {
2
2
  if (history && history.length > 0) {
3
- result += '\n<scr'+'ipt>';
3
+ result += '\n<scr'+'ipt class="react-rails-console-replay">';
4
4
  history.forEach(function (msg) {
5
5
  result += '\nconsole.' + msg.level + '.apply(console, ' + JSON.stringify(msg.arguments) + ');';
6
6
  });
@@ -0,0 +1,3 @@
1
+ if (typeof console !== "undefined" && console.history) {
2
+ console.history = [];
3
+ }