Skip to content

Commit 259e299

Browse files
author
Robert Mosolgo
committed
Merge pull request #302 from rmosolgo/transformer-fixes
Config & initialization fixes
2 parents 9a4f6ab + a40a435 commit 259e299

File tree

9 files changed

+138
-96
lines changed

9 files changed

+138
-96
lines changed

README.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ You can configure your pool of JS virtual machines and specify where it should l
188188
# These are the defaults if you dont specify any yourself
189189
MyApp::Application.configure do
190190
# Settings for the pool of renderers:
191-
config.react.server_renderer_pool_size ||= 10
191+
config.react.server_renderer_pool_size ||= 1 # ExecJS doesn't allow more than one on MRI
192192
config.react.server_renderer_timeout ||= 20 # seconds
193193
config.react.server_renderer = React::ServerRendering::SprocketsRenderer
194194
config.react.server_renderer_options = {
@@ -198,6 +198,10 @@ MyApp::Application.configure do
198198
end
199199
```
200200

201+
- On MRI, use `therubyracer` for the best performance (see [discussion](https://github.com/reactjs/react-rails/pull/290))
202+
- On MRI, you'll get a deadlock with `pool_size` > 1
203+
- If you're using JRuby, you can increase `pool_size` to have real multi-threaded rendering.
204+
201205
### Component generator
202206

203207
`react-rails` ships with a Rails generator to help you get started with a simple component scaffold.

lib/react/jsx.rb

+5-11
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,18 @@
66

77
module React
88
module JSX
9-
mattr_accessor :transform_options, :transformer_class
9+
DEFAULT_TRANSFORMER = BabelTransformer
10+
mattr_accessor :transform_options, :transformer_class, :transformer
1011

1112
# You can assign `React::JSX.transformer_class = `
1213
# to provide your own transformer. It must implement:
1314
# - #initialize(options)
1415
# - #transform(code) => new code
15-
self.transformer_class = BabelTransformer
16+
self.transformer_class = DEFAULT_TRANSFORMER
1617

1718
def self.transform(code)
18-
transformer.transform(code)
19-
end
20-
21-
def self.transformer
22-
# lazily loaded during first request and reloaded every time when in dev or test
23-
if @transformer.nil? || !::Rails.env.production?
24-
@transformer = transformer_class.new(transform_options)
25-
end
26-
@transformer
19+
self.transformer ||= transformer_class.new(transform_options)
20+
self.transformer.transform(code)
2721
end
2822
end
2923
end

lib/react/rails.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1-
require 'react/rails/railtie'
1+
require 'react/rails/asset_variant'
22
require 'react/rails/engine'
3+
require 'react/rails/railtie'
34
require 'react/rails/view_helper'

lib/react/rails/asset_variant.rb

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
module React
2+
module Rails
3+
class AssetVariant
4+
GEM_ROOT = Pathname.new('../../../../').expand_path(__FILE__)
5+
attr_reader :react_build, :react_directory, :jsx_directory
6+
7+
def initialize(options={})
8+
# We want to include different files in dev/prod. The development builds
9+
# contain console logging for invariants and logging to help catch
10+
# common mistakes. These are all stripped out in the production build.
11+
@react_build = options[:variant] == :production ? 'production' : 'development'
12+
options[:addons] && @react_build += '-with-addons'
13+
14+
@react_directory = GEM_ROOT.join('lib/assets/react-source/').join(@react_build).to_s
15+
@jsx_directory = GEM_ROOT.join('lib/assets/javascripts/').to_s
16+
end
17+
end
18+
end
19+
end

lib/react/rails/railtie.rb

+17-13
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ class Railtie < ::Rails::Railtie
99
config.react.variant = (::Rails.env.production? ? :production : :development)
1010
config.react.addons = false
1111
config.react.jsx_transform_options = {}
12+
config.react.jsx_transformer_class = nil # defaults to BabelTransformer
1213
# Server rendering:
13-
config.react.server_renderer_pool_size = 10
14-
config.react.server_renderer_timeout = 20 # seconds
14+
config.react.server_renderer_pool_size = 1 # increase if you're on JRuby
15+
config.react.server_renderer_timeout = 20 # seconds
1516
config.react.server_renderer = nil # defaults to SprocketsRenderer
1617
config.react.server_renderer_options = {} # SprocketsRenderer provides defaults
1718

@@ -22,32 +23,35 @@ class Railtie < ::Rails::Railtie
2223

2324
# Include the react-rails view helper lazily
2425
initializer "react_rails.setup_view_helpers", group: :all do |app|
26+
app.config.react.jsx_transformer_class ||= React::JSX::DEFAULT_TRANSFORMER
27+
React::JSX.transformer_class = app.config.react.jsx_transformer_class
2528
React::JSX.transform_options = app.config.react.jsx_transform_options
29+
2630
ActiveSupport.on_load(:action_view) do
2731
include ::React::Rails::ViewHelper
2832
end
2933
end
3034

3135
initializer "react_rails.bust_cache", group: :all do |app|
32-
variant = app.config.react.variant == :production ? 'production' : 'development'
33-
variant += '-with-addons' if app.config.react.addons
36+
asset_variant = React::Rails::AssetVariant.new({
37+
variant: app.config.react.variant,
38+
addons: app.config.react.addons,
39+
})
3440

3541
app.assets.version = [
3642
app.assets.version,
37-
"react-#{variant}",
43+
"react-#{asset_variant.react_build}",
3844
].compact.join('-')
3945
end
4046

4147
config.before_initialize do |app|
42-
# We want to include different files in dev/prod. The development builds
43-
# contain console logging for invariants and logging to help catch
44-
# common mistakes. These are all stripped out in the production build.
45-
root_path = Pathname.new('../../../../').expand_path(__FILE__)
46-
directory = app.config.react.variant == :production ? 'production' : 'development'
47-
directory += '-with-addons' if app.config.react.addons
48+
asset_variant = React::Rails::AssetVariant.new({
49+
variant: app.config.react.variant,
50+
addons: app.config.react.addons,
51+
})
4852

49-
app.config.assets.paths << root_path.join('lib/assets/react-source/').join(directory).to_s
50-
app.config.assets.paths << root_path.join('lib/assets/javascripts/').to_s
53+
app.config.assets.paths << asset_variant.react_directory
54+
app.config.assets.paths << asset_variant.jsx_directory
5155
end
5256

5357
config.after_initialize do |app|
+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
require 'test_helper'
2+
3+
class JSXTransformerTest < ActionDispatch::IntegrationTest
4+
setup do
5+
reset_transformer
6+
React::JSX.transformer_class = React::JSX::JSXTransformer
7+
end
8+
9+
teardown do
10+
reset_transformer
11+
end
12+
13+
test 'can use dropped-in version of JSX transformer' do
14+
hidden_path = Rails.root.join("vendor/assets/react/JSXTransformer__.js")
15+
replacing_path = Rails.root.join("vendor/assets/react/JSXTransformer.js")
16+
17+
FileUtils.cp hidden_path, replacing_path
18+
get '/assets/example3.js'
19+
FileUtils.rm replacing_path
20+
21+
assert_response :success
22+
assert_equal 'test_confirmation_token_jsx_transformed;', @response.body
23+
end
24+
25+
test 'accepts harmony: true option' do
26+
React::JSX.transform_options = {harmony: true}
27+
get '/assets/harmony_example.js'
28+
assert_response :success
29+
assert_match(/generateGreeting:\s*function\(\)/, @response.body, "object literal methods")
30+
assert_match(/React.__spread/, @response.body, "spreading props")
31+
assert_match(/Your greeting is: '" \+ insertedGreeting \+ "'/, @response.body, "string interpolation")
32+
assert_match(/active=\$__0\.active/, @response.body, "destructuring assignment")
33+
end
34+
35+
test 'accepts strip_types: true option' do
36+
React::JSX.transform_options = {strip_types: true, harmony: true}
37+
get '/assets/flow_types_example.js'
38+
assert_response :success
39+
assert_match(/\(i\s*,\s*name\s*\)\s*\{/, @response.body, "type annotations are removed")
40+
end
41+
42+
test 'accepts asset_path: option' do
43+
hidden_path = Rails.root.join("vendor/assets/react/JSXTransformer__.js")
44+
custom_path = Rails.root.join("vendor/assets/react/custom")
45+
replacing_path = custom_path.join("CustomTransformer.js")
46+
47+
React::JSX.transform_options = {asset_path: "custom/CustomTransformer.js"}
48+
49+
FileUtils.mkdir_p(custom_path)
50+
FileUtils.cp(hidden_path, replacing_path)
51+
get '/assets/example3.js'
52+
53+
FileUtils.rm_rf custom_path
54+
assert_response :success
55+
assert_equal 'test_confirmation_token_jsx_transformed;', @response.body
56+
end
57+
58+
end

test/react/jsx_test.rb

-70
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,6 @@ class JSXTransformTest < ActionDispatch::IntegrationTest
3636
reset_transformer
3737
end
3838

39-
def reset_transformer
40-
clear_sprockets_cache
41-
React::JSX.transformer_class = React::JSX::BabelTransformer
42-
React::JSX.transform_options = {}
43-
end
44-
4539
test 'asset pipeline should transform JSX' do
4640
get '/assets/example.js'
4741
assert_response :success
@@ -73,68 +67,4 @@ def test_babel_transformer_accepts_babel_transformation_options
7367

7468
assert !@response.body.include?('strict')
7569
end
76-
77-
end
78-
79-
class JSXTransformerTest < ActionDispatch::IntegrationTest
80-
81-
setup do
82-
reset_transformer
83-
end
84-
85-
teardown do
86-
reset_transformer
87-
end
88-
89-
def reset_transformer
90-
clear_sprockets_cache
91-
React::JSX.transformer_class = React::JSX::JSXTransformer
92-
React::JSX.transform_options = {}
93-
end
94-
95-
test 'can use dropped-in version of JSX transformer' do
96-
hidden_path = Rails.root.join("vendor/assets/react/JSXTransformer__.js")
97-
replacing_path = Rails.root.join("vendor/assets/react/JSXTransformer.js")
98-
99-
FileUtils.cp hidden_path, replacing_path
100-
get '/assets/example3.js'
101-
FileUtils.rm replacing_path
102-
103-
assert_response :success
104-
assert_equal 'test_confirmation_token_jsx_transformed;', @response.body
105-
end
106-
107-
test 'accepts harmony: true option' do
108-
React::JSX.transform_options = {harmony: true}
109-
get '/assets/harmony_example.js'
110-
assert_response :success
111-
assert_match(/generateGreeting:\s*function\(\)/, @response.body, "object literal methods")
112-
assert_match(/React.__spread/, @response.body, "spreading props")
113-
assert_match(/Your greeting is: '" \+ insertedGreeting \+ "'/, @response.body, "string interpolation")
114-
assert_match(/active=\$__0\.active/, @response.body, "destructuring assignment")
115-
end
116-
117-
test 'accepts strip_types: true option' do
118-
React::JSX.transform_options = {strip_types: true, harmony: true}
119-
get '/assets/flow_types_example.js'
120-
assert_response :success
121-
assert_match(/\(i\s*,\s*name\s*\)\s*\{/, @response.body, "type annotations are removed")
122-
end
123-
124-
test 'accepts asset_path: option' do
125-
hidden_path = Rails.root.join("vendor/assets/react/JSXTransformer__.js")
126-
custom_path = Rails.root.join("vendor/assets/react/custom")
127-
replacing_path = custom_path.join("CustomTransformer.js")
128-
129-
React::JSX.transform_options = {asset_path: "custom/CustomTransformer.js"}
130-
131-
FileUtils.mkdir_p(custom_path)
132-
FileUtils.cp(hidden_path, replacing_path)
133-
get '/assets/example3.js'
134-
135-
FileUtils.rm_rf custom_path
136-
assert_response :success
137-
assert_equal 'test_confirmation_token_jsx_transformed;', @response.body
138-
end
139-
14070
end
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
require 'test_helper'
2+
3+
class AssetVariantTest < ActiveSupport::TestCase
4+
def build_variant(options)
5+
React::Rails::AssetVariant.new(options)
6+
end
7+
8+
test 'it points to different directories for react' do
9+
production_variant = build_variant(variant: :production)
10+
assert_match(%r{/lib/assets/react-source/production}, production_variant.react_directory)
11+
12+
development_variant = build_variant(variant: nil)
13+
assert_match(%r{/lib/assets/react-source/development}, development_variant.react_directory)
14+
end
15+
16+
test 'points to jsx transformer' do
17+
variant = build_variant({})
18+
assert_match(%r{/lib/assets/javascripts/}, variant.jsx_directory)
19+
end
20+
21+
test 'it includes addons if requested' do
22+
asset_variant = build_variant(addons: true)
23+
assert_equal "development-with-addons", asset_variant.react_build
24+
end
25+
end

test/test_helper.rb

+7
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ def clear_sprockets_cache
2424
end
2525
end
2626

27+
def reset_transformer
28+
clear_sprockets_cache
29+
React::JSX.transformer_class = React::JSX::DEFAULT_TRANSFORMER
30+
React::JSX.transform_options = {}
31+
React::JSX.transformer = nil
32+
end
33+
2734
# Sprockets 2 doesn't expire this assets well in
2835
# this kind of setting,
2936
# so override `fresh?` to mark it as expired.

0 commit comments

Comments
 (0)