diff --git a/README.md b/README.md
index f243dc81..83841cc4 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,8 @@
# react-rails
-`react-rails` makes it easy to use [React](http://facebook.github.io/react/) and [JSX](http://facebook.github.io/react/docs/jsx-in-depth.html) in your Ruby on Rails (3.2+) application. `react-rails` can:
+`react-rails` makes it easy to use [React](http://facebook.github.io/react/) and [JSX](http://facebook.github.io/react/docs/jsx-in-depth.html)
+in your Ruby on Rails (3.2+) application. `react-rails` can:
- Provide [various `react` builds](#reactjs-builds) to your asset bundle
- Transform [`.jsx` in the asset pipeline](#jsx)
@@ -32,7 +33,8 @@ rails g react:install
```
This will:
-- create a `components.js` manifest file and a `app/assets/javascripts/components/` directory, where you will put your components
+- create a `components.js` manifest file and a `app/assets/javascripts/components/` directory,
+where you will put your components
- place the following in your `application.js`:
```js
@@ -45,7 +47,8 @@ This will:
### React.js builds
-You can pick which React.js build (development, production, with or without [add-ons]((http://facebook.github.io/react/docs/addons.html))) to serve in each environment by adding a config. Here are the defaults:
+You can pick which React.js build (development, production, with or without [add-ons]((http://facebook.github.io/react/docs/addons.html)))
+to serve in each environment by adding a config. Here are the defaults:
```ruby
# config/environments/development.rb
@@ -67,14 +70,40 @@ MyApp::Application.configure do
end
```
-After restarting your Rails server, `//= require react` will provide the build of React.js which was specified by the configurations.
+After restarting your Rails server, `//= require react` will provide the build of React.js which
+was specified by the configurations.
-`react-rails` offers a few other options for versions & builds of React.js. See [VERSIONS.md](https://github.com/reactjs/react-rails/blob/master/VERSIONS.md) for more info about using the `react-source` gem or dropping in your own copies of React.js.
+`react-rails` offers a few other options for versions & builds of React.js.
+See [VERSIONS.md](https://github.com/reactjs/react-rails/blob/master/VERSIONS.md) for more info about
+ using the `react-source` gem or dropping in your own copies of React.js.
### JSX
After installing `react-rails`, restart your server. Now, `.js.jsx` files will be transformed in the asset pipeline.
+`react-rails` currently ships with two transformers, to convert jsx code -
+
+* `BabelTransformer` using [Babel](http://babeljs.io), which is the default transformer.
+* `JSXTransformer` using `JSXTransformer.js`
+
+#### BabelTransformer options
+
+You can use babel's [transformers](http://babeljs.io/docs/advanced/transformers/) and [custom plugins](http://babeljs.io/docs/advanced/plugins/),
+and pass [options](http://babeljs.io/docs/usage/options/) to the babel transpiler adding following configurations:
+
+```ruby
+config.react.jsx_transform_options = {
+ blacklist: ['spec.functionName', 'validation.react'], // default options
+ optional: ["transformerName"], // pass extra babel options
+ whitelist: ["useStrict"] // even more options
+}
+```
+Under the hood, `react-rails` users [ruby-babel-transpiler](https://github.com/babel/ruby-babel-transpiler), for transformation.
+
+#### JSXTransformer options
+
+To use old JSXTransformer you can use `React::JSX.transformer_class = React::JSX::JSXTransformer`
+
You can use JSX `--harmony` or `--strip-types` options by adding a configuration:
```ruby
@@ -85,17 +114,11 @@ config.react.jsx_transform_options = {
}
```
-To use CoffeeScript, create `.js.jsx.coffee` files and embed JSX inside backticks, for example:
-
-```coffee
-Component = React.createClass
- render: ->
- ``
-```
-
### Rendering & mounting
-`react-rails` includes a view helper (`react_component`) and an unobtrusive JavaScript driver (`react_ujs`) which work together to put React components on the page. You should require the UJS driver in your manifest after `react` (and after `turbolinks` if you use [Turbolinks](https://github.com/rails/turbolinks)).
+`react-rails` includes a view helper (`react_component`) and an unobtrusive JavaScript driver (`react_ujs`)
+which work together to put React components on the page. You should require the UJS driver
+ in your manifest after `react` (and after `turbolinks` if you use [Turbolinks](https://github.com/rails/turbolinks)).
The __view helper__ puts a `div` on the page with the requested component class & props. For example:
@@ -105,9 +128,12 @@ The __view helper__ puts a `div` on the page with the requested component class
```
-On page load, the __`react_ujs` driver__ will scan the page and mount components using `data-react-class` and `data-react-props`. Before page unload, it will unmount components (if you want to disable this behavior, remove `data-react-class` attribute in `componentDidMount`).
+On page load, the __`react_ujs` driver__ will scan the page and mount components using `data-react-class`
+and `data-react-props`. Before page unload, it will unmount components (if you want to disable this behavior,
+remove `data-react-class` attribute in `componentDidMount`).
-`react_ujs` uses Turbolinks events if they're available, otherwise, it uses native events. __Turbolinks >= 2.4.0__ is recommended because it exposes better events.
+`react_ujs` uses Turbolinks events if they're available, otherwise, it uses native events.
+ __Turbolinks >= 2.4.0__ is recommended because it exposes better events.
The view helper's signature is:
@@ -141,8 +167,10 @@ _(It will be also be mounted by the UJS on page load.)_
There are some requirements for this to work:
-- `react-rails` must load your code. By convention it uses `components.js`, which was created by the install task. This file must include your components _and_ their dependencies (eg, Underscore.js).
-- Your components must be accessible in the global scope. If you are using `.js.jsx.coffee` files then the wrapper function needs to be taken into account:
+- `react-rails` must load your code. By convention it uses `components.js`, which was created
+by the install task. This file must include your components _and_ their dependencies (eg, Underscore.js).
+- Your components must be accessible in the global scope.
+If you are using `.js.jsx.coffee` files then the wrapper function needs to be taken into account:
```coffee
# @ is `window`:
@@ -150,7 +178,8 @@ There are some requirements for this to work:
render: ->
``
```
-- Your code can't reference `document`. Prerender processes don't have access to `document`, so jQuery and some other libs won't work in this environment :(
+- Your code can't reference `document`. Prerender processes don't have access to `document`,
+so jQuery and some other libs won't work in this environment :(
You can configure your pool of JS virtual machines and specify where it should load code:
@@ -171,7 +200,11 @@ end
### Component generator
-`react-rails` ships with a Rails generator to help you get started with a simple component scaffold. You can run it using `rails generate react:component ComponentName`. The generator takes an optional list of arguments for default propTypes, which follow the conventions set in the [Reusable Components](http://facebook.github.io/react/docs/reusable-components.html) section of the React documentation.
+`react-rails` ships with a Rails generator to help you get started with a simple component scaffold.
+You can run it using `rails generate react:component ComponentName`.
+The generator takes an optional list of arguments for default propTypes,
+which follow the conventions set in the [Reusable Components](http://facebook.github.io/react/docs/reusable-components.html)
+section of the React documentation.
For example:
@@ -222,11 +255,13 @@ The following additional arguments have special behavior:
* `oneOf` behaves like an enum, and takes an optional list of strings in the form of `'name:oneOf{one,two,three}'`.
* `oneOfType` takes an optional list of react and custom types in the form of `'model:oneOfType{string,number,OtherType}'`.
-Note that the arguments for `oneOf` and `oneOfType` must be enclosed in single quotes to prevent your terminal from expanding them into an argument list.
+Note that the arguments for `oneOf` and `oneOfType` must be enclosed in single quotes
+ to prevent your terminal from expanding them into an argument list.
### Jbuilder & react-rails
-If you use Jbuilder to pass a JSON string to `react_component`, make sure your JSON is a stringified hash, not an array. This is not the Rails default -- you should add the root node yourself. For example:
+If you use Jbuilder to pass a JSON string to `react_component`, make sure your JSON is a stringified hash,
+not an array. This is not the Rails default -- you should add the root node yourself. For example:
```ruby
# BAD: returns a stringified array
@@ -244,7 +279,9 @@ end
## CoffeeScript
-It is possible to use JSX with CoffeeScript. We need to embed JSX inside backticks so CoffeeScript ignores the syntax it doesn't understand. Here's an example:
+It is possible to use JSX with CoffeeScript. To use CoffeeScript, create files with an extension `.js.jsx.coffee`.
+We also need to embed JSX code inside backticks so that CoffeeScript ignores the syntax it doesn't understand.
+Here's an example:
```coffee
Component = React.createClass
diff --git a/lib/react/jsx.rb b/lib/react/jsx.rb
index 72fa6af6..692e4e2d 100644
--- a/lib/react/jsx.rb
+++ b/lib/react/jsx.rb
@@ -1,6 +1,7 @@
require 'execjs'
require 'react/jsx/template'
-require 'react/jsx/transformer'
+require 'react/jsx/jsx_transformer'
+require 'react/jsx/babel_transformer'
require 'rails'
module React
@@ -11,7 +12,7 @@ module JSX
# to provide your own transformer. It must implement:
# - #initialize(options)
# - #transform(code) => new code
- self.transformer_class = Transformer
+ self.transformer_class = BabelTransformer
def self.transform(code)
transformer.transform(code)
diff --git a/lib/react/jsx/babel_transformer.rb b/lib/react/jsx/babel_transformer.rb
new file mode 100644
index 00000000..f6fc37dc
--- /dev/null
+++ b/lib/react/jsx/babel_transformer.rb
@@ -0,0 +1,21 @@
+require 'babel/transpiler'
+module React
+ module JSX
+ class BabelTransformer
+ DEPRECATED_OPTIONS = [:harmony, :strip_types, :asset_path]
+ DEFAULT_TRANSFORM_OPTIONS = { blacklist: ['spec.functionName', 'validation.react', 'strict'] }
+ def initialize(options)
+ if (options.keys & DEPRECATED_OPTIONS).any?
+ 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."+
+ "Please use new Babel Transformer options :whitelist, :plugin instead.")
+ end
+
+ @transform_options = DEFAULT_TRANSFORM_OPTIONS.merge(options)
+ end
+
+ def transform(code)
+ Babel::Transpiler.transform(code, @transform_options)['code']
+ end
+ end
+ end
+end
diff --git a/lib/react/jsx/transformer.rb b/lib/react/jsx/jsx_transformer.rb
similarity index 97%
rename from lib/react/jsx/transformer.rb
rename to lib/react/jsx/jsx_transformer.rb
index 49a79e16..c4230332 100644
--- a/lib/react/jsx/transformer.rb
+++ b/lib/react/jsx/jsx_transformer.rb
@@ -1,6 +1,6 @@
module React
module JSX
- class Transformer
+ class JSXTransformer
DEFAULT_ASSET_PATH = 'JSXTransformer.js'
def initialize(options)
@@ -30,4 +30,4 @@ def jsx_transform_code
end
end
end
-end
\ No newline at end of file
+end
diff --git a/react-rails.gemspec b/react-rails.gemspec
index 4839a978..52da5103 100644
--- a/react-rails.gemspec
+++ b/react-rails.gemspec
@@ -31,6 +31,7 @@ Gem::Specification.new do |s|
s.add_dependency 'execjs'
s.add_dependency 'rails', '>= 3.2'
s.add_dependency 'tilt'
+ s.add_dependency 'babel-transpiler', '>=0.7.0'
s.files = Dir[
'lib/**/*',
diff --git a/test/dummy/config/environments/test.rb b/test/dummy/config/environments/test.rb
index cff2ea78..f6b42357 100644
--- a/test/dummy/config/environments/test.rb
+++ b/test/dummy/config/environments/test.rb
@@ -17,7 +17,8 @@
config.eager_load = false
# Configure static asset server for tests with Cache-Control for performance.
- config.serve_static_assets = true
+ # Disabled since we dont use it and this option is deprecated from Rails 4.2 onwards
+ # config.serve_static_assets = true
config.static_cache_control = "public, max-age=3600"
# Show full error reports and disable caching.
diff --git a/test/generators/component_generator_test.rb b/test/generators/component_generator_test.rb
index e64bf13e..173ce34d 100644
--- a/test/generators/component_generator_test.rb
+++ b/test/generators/component_generator_test.rb
@@ -47,8 +47,8 @@ def filename
end
test "generates working jsx" do
- expected_name_div = Regexp.escape('React.createElement("div", null, "Name: ", this.props.name)')
- expected_shape_div = Regexp.escape('React.createElement("div", null, "Address: ", this.props.address)')
+ expected_name_div = /React\.createElement\(\s*"div",\s*null,\s*\"Name:\s*\",\s*this\.props\.name\s*\)/x
+ expected_shape_div = /React\.createElement\(\s*"div",\s*null,\s*\"Address:\s*\",\s*this\.props\.address\s*\)/x
run_generator %w(GeneratedComponent name:string address:shape)
jsx = React::JSX.transform(File.read(File.join(destination_root, filename)))
diff --git a/test/react/jsx_test.rb b/test/react/jsx_test.rb
index ce71a20c..7032b43e 100644
--- a/test/react/jsx_test.rb
+++ b/test/react/jsx_test.rb
@@ -29,19 +29,24 @@ def transform(code)
class JSXTransformTest < ActionDispatch::IntegrationTest
setup do
- clear_sprockets_cache
+ reset_transformer
end
teardown do
+ reset_transformer
+ end
+
+ def reset_transformer
clear_sprockets_cache
- React::JSX.transformer_class = React::JSX::Transformer
+ React::JSX.transformer_class = React::JSX::BabelTransformer
React::JSX.transform_options = {}
end
test 'asset pipeline should transform JSX' do
get '/assets/example.js'
assert_response :success
- assert_equal EXPECTED_JS, @response.body
+
+ assert_equal EXPECTED_JS.gsub(/\s/, ''), @response.body.gsub(/\s/, '')
end
test 'asset pipeline should transform JSX + Coffeescript' do
@@ -54,6 +59,39 @@ class JSXTransformTest < ActionDispatch::IntegrationTest
assert_equal EXPECTED_JS_2.gsub(/\s/, ''), @response.body.gsub(/\s/, '')
end
+ test 'use a custom transformer' do
+ React::JSX.transformer_class = NullTransformer
+ manually_expire_asset('example2.js')
+ get '/assets/example2.js'
+ assert_equal "TRANSFORMED CODE!;\n", @response.body
+ end
+
+ def test_babel_transformer_accepts_babel_transformation_options
+ React::JSX.transform_options = {blacklist: ['spec.functionName', 'validation.react', "strict"]}
+ get '/assets/example.js'
+ assert_response :success
+
+ assert !@response.body.include?('strict')
+ end
+
+end
+
+class JSXTransformerTest < ActionDispatch::IntegrationTest
+
+ setup do
+ reset_transformer
+ end
+
+ teardown do
+ reset_transformer
+ end
+
+ def reset_transformer
+ clear_sprockets_cache
+ React::JSX.transformer_class = React::JSX::JSXTransformer
+ React::JSX.transform_options = {}
+ end
+
test 'can use dropped-in version of JSX transformer' do
hidden_path = Rails.root.join("vendor/assets/react/JSXTransformer__.js")
replacing_path = Rails.root.join("vendor/assets/react/JSXTransformer.js")
@@ -99,10 +137,4 @@ class JSXTransformTest < ActionDispatch::IntegrationTest
assert_equal 'test_confirmation_token_jsx_transformed;', @response.body
end
- test 'use a custom transformer' do
- React::JSX.transformer_class = NullTransformer
- manually_expire_asset('example2.js')
- get '/assets/example2.js'
- assert_equal "TRANSFORMED CODE!;\n", @response.body
- end
end
diff --git a/test/test_helper.rb b/test/test_helper.rb
index 1e03862e..0c6b4129 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -6,13 +6,13 @@
# Configure Rails Environment
ENV["RAILS_ENV"] = "test"
-require File.expand_path("../dummy/config/environment.rb", __FILE__)
+require File.expand_path("../dummy/config/environment.rb", __FILE__)
require "rails/test_help"
require "rails/generators"
require "pathname"
require 'minitest/mock'
-CACHE_PATH = Pathname.new File.expand_path("../dummy/tmp/cache", __FILE__)
+CACHE_PATH = Pathname.new File.expand_path("../dummy/tmp/cache", __FILE__)
Rails.backtrace_cleaner.remove_silencers!
@@ -40,6 +40,10 @@ def asset.fresh?(env); false; end
ActiveSupport::TestCase.fixture_path = File.expand_path("../fixtures", __FILE__)
end
+if ActiveSupport::TestCase.respond_to?(:test_order=)
+ ActiveSupport::TestCase.test_order = :random
+end
+
def wait_for_turbolinks_to_be_available
sleep(1)
end