Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/react/jsx.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require 'react/jsx/template'
require 'react/jsx/jsx_transformer'
require 'react/jsx/babel_transformer'
require 'react/jsx/sprockets_strategy'
require 'rails'

module React
Expand Down
53 changes: 53 additions & 0 deletions lib/react/jsx/sprockets_strategy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
module React
module JSX
# Depending on the Sprockets version,
# attach JSX transformation the the Sprockets environment.
#
# You can override it with `config.sprockets_strategy`
# @example Specifying a Sprockets strategy
# app.config.react.sprockets_strategy = :register_engine
#
# @example Opting out of any Sprockets strategy
# app.config.react.sprockets_strategy = false
#
module SprocketsStrategy
module_function

# @param [Sprockets::Environment] the environment to attach JSX to
# @param [Symbol, Nil] A strategy name, or `nil` to detect a strategy
def attach_with_strategy(sprockets_env, strategy_or_nil)
strategy = strategy_or_nil || detect_strategy
self.public_send(strategy, sprockets_env)
end

# @return [Symbol] based on the environment, return a method name to call with the sprockets environment
def detect_strategy
sprockets_version = Gem::Version.new(Sprockets::VERSION)
if sprockets_version >= Gem::Version.new("4.x")
:register_processors
elsif sprockets_version >= Gem::Version.new("3.0.0")
:register_engine_with_mime_type
else
:register_engine
end
end

def register_engine(sprockets_env)
sprockets_env.register_engine(".jsx", React::JSX::Template)
end

def register_engine_with_mime_type(sprockets_env)
sprockets_env.register_engine(".jsx", React::JSX::Processor, mime_type: "application/javascript", silence_deprecation: true)
end

def register_processors(sprockets_env)
sprockets_env.register_mime_type("application/jsx", extensions: [".jsx", ".js.jsx", ".es.jsx", ".es6.jsx"])
sprockets_env.register_mime_type("application/jsx+coffee", extensions: [".jsx.coffee", ".js.jsx.coffee"])
sprockets_env.register_transformer("application/jsx", "application/javascript", React::JSX::Processor)
sprockets_env.register_transformer("application/jsx+coffee", "application/jsx", Sprockets::CoffeeScriptProcessor)
sprockets_env.register_preprocessor("application/jsx", Sprockets::DirectiveProcessor.new(comments: ["//", ["/*", "*/"]]))
sprockets_env.register_preprocessor("application/jsx+coffee", Sprockets::DirectiveProcessor.new(comments: ["#", ["###", "###"]]))
end
end
end
end
20 changes: 7 additions & 13 deletions lib/react/rails/railtie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class Railtie < ::Rails::Railtie
config.react.jsx_transform_options = {}
config.react.jsx_transformer_class = nil # defaults to BabelTransformer
config.react.camelize_props = false # pass in an underscored hash but get a camelized hash
config.react.sprockets_strategy = nil # how to attach JSX to the asset pipeline (or `false` for none)

# Server rendering:
config.react.server_renderer_pool_size = 1 # increase if you're on JRuby
Expand Down Expand Up @@ -100,19 +101,12 @@ class Railtie < ::Rails::Railtie
# Sprockets 3.x expects this in a different place
sprockets_env = app.assets || defined?(Sprockets) && Sprockets

if !sprockets_env.nil?
if Gem::Version.new(Sprockets::VERSION) >= Gem::Version.new("3.7.0")
sprockets_env.register_mime_type("application/jsx", extensions: [".jsx", ".js.jsx", ".es.jsx", ".es6.jsx"])
sprockets_env.register_mime_type("application/jsx+coffee", extensions: [".jsx.coffee", ".js.jsx.coffee"])
sprockets_env.register_transformer("application/jsx", "application/javascript", React::JSX::Processor)
sprockets_env.register_transformer("application/jsx+coffee", "application/jsx", Sprockets::CoffeeScriptProcessor)
sprockets_env.register_preprocessor("application/jsx", Sprockets::DirectiveProcessor.new(comments: ["//", ["/*", "*/"]]))
sprockets_env.register_preprocessor("application/jsx+coffee", Sprockets::DirectiveProcessor.new(comments: ["#", ["###", "###"]]))
elsif Gem::Version.new(Sprockets::VERSION) >= Gem::Version.new("3.0.0")
sprockets_env.register_engine(".jsx", React::JSX::Processor, mime_type: "application/javascript")
else
sprockets_env.register_engine(".jsx", React::JSX::Template)
end
if app.config.react.sprockets_strategy == false
# pass, Sprockets opt-out
elsif sprockets_env.present?
React::JSX::SprocketsStrategy.attach_with_strategy(sprockets_env, app.config.react.sprockets_strategy)
else
# pass, Sprockets is not preset
end
end
end
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//= require ./jsx_require_child_jsx
//= require ./jsx_require_child_js
//= require ./jsx_require_child_coffee
<div className="le-javascript" />

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
requireCoffee = true
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
var requirePlainJavascript = true;

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<div className="require-jsx" />
20 changes: 11 additions & 9 deletions test/react/jsx/jsx_prepocessor_test.rb
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
require 'test_helper'

when_sprockets_available do
class JSXPreprocessorTest < ActionDispatch::IntegrationTest
EXPECTED_JS = <<javascript
React.createElement(\"div\", { className: \"le-javascript-child\" });
React.createElement(\"div\", { className: \"le-javascript\" });
javascript

class JSXPreprocessorTest < ActiveSupport::TestCase
REQUIRED_JAVASCRIPT = "var requirePlainJavascript = true;"
REQUIRED_COFFEESCRIPT = "var requireCoffee; requireCoffee = true;"
REQUIRED_JSX = "React.createElement(\"div\", { className: \"require-jsx\" });"
OWN_JSX = "React.createElement(\"div\", { className: \"le-javascript\" });"
test 'executes //= require directives' do
get '/assets/require_test/jsx_preprocessor_test.js'
assert_response :success
assert_compiled_javascript_matches(@response.body, EXPECTED_JS)
require_parent = fetch_asset_body("require_test/jsx_preprocessor_test.js")

assert_compiled_javascript_includes(require_parent, REQUIRED_JAVASCRIPT)
assert_compiled_javascript_includes(require_parent, REQUIRED_COFFEESCRIPT)
assert_compiled_javascript_includes(require_parent, REQUIRED_JSX)
assert_compiled_javascript_includes(require_parent, OWN_JSX)
end
end
end
15 changes: 10 additions & 5 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,18 @@ def when_sprockets_available
end
end

class ActionDispatch::IntegrationTest
def fetch_asset_body(asset_logical_path)
Rails.application.assets[asset_logical_path].to_s
end

# Different processors may generate slightly different outputs,
# as some version inserts an extra "\n" at the beginning.
# Because appraisal is used, multiple versions of coffee-script are treated
# together. Remove all spaces to make test pass.
def assert_compiled_javascript_matches(javascript, expectation)
assert_equal expectation.gsub(/\s/, ''), javascript.gsub(/\s/, '')
end
end
def assert_compiled_javascript_matches(javascript, expectation)
assert_equal expectation.gsub(/\s/, ''), javascript.gsub(/\s/, '')
end

def assert_compiled_javascript_includes(javascript, expected_part)
assert_includes javascript.gsub(/\s/, ''), expected_part.gsub(/\s/, '')
end