Skip to content

Commit ce68d57

Browse files
authored
Merge pull request #684 from reactjs/generate-webpacker
Support webpacker in generators
2 parents 79101ff + e33848f commit ce68d57

22 files changed

+385
-202
lines changed

lib/assets/javascripts/react_ujs.js

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
(function webpackUniversalModuleDefinition(root, factory) {
22
if(typeof exports === 'object' && typeof module === 'object')
3-
module.exports = factory(require("react"), require("react-dom"));
3+
module.exports = factory(require("react"), require("react-dom"), require("react-dom/server"));
44
else if(typeof define === 'function' && define.amd)
5-
define(["react", "react-dom"], factory);
5+
define(["react", "react-dom", "react-dom/server"], factory);
66
else if(typeof exports === 'object')
7-
exports["ReactRailsUJS"] = factory(require("react"), require("react-dom"));
7+
exports["ReactRailsUJS"] = factory(require("react"), require("react-dom"), require("react-dom/server"));
88
else
9-
root["ReactRailsUJS"] = factory(root["React"], root["ReactDOM"]);
10-
})(this, function(__WEBPACK_EXTERNAL_MODULE_3__, __WEBPACK_EXTERNAL_MODULE_4__) {
9+
root["ReactRailsUJS"] = factory(root["React"], root["ReactDOM"], root["ReactDOMServer"]);
10+
})(this, function(__WEBPACK_EXTERNAL_MODULE_3__, __WEBPACK_EXTERNAL_MODULE_4__, __WEBPACK_EXTERNAL_MODULE_5__) {
1111
return /******/ (function(modules) { // webpackBootstrap
1212
/******/ // The module cache
1313
/******/ var installedModules = {};
@@ -73,18 +73,18 @@ return /******/ (function(modules) { // webpackBootstrap
7373
/******/ __webpack_require__.p = "";
7474
/******/
7575
/******/ // Load entry module and return exports
76-
/******/ return __webpack_require__(__webpack_require__.s = 5);
76+
/******/ return __webpack_require__(__webpack_require__.s = 6);
7777
/******/ })
7878
/************************************************************************/
7979
/******/ ([
8080
/* 0 */
8181
/***/ (function(module, exports, __webpack_require__) {
8282

83-
var nativeEvents = __webpack_require__(6)
84-
var pjaxEvents = __webpack_require__(7)
85-
var turbolinksEvents = __webpack_require__(8)
86-
var turbolinksClassicDeprecatedEvents = __webpack_require__(10)
87-
var turbolinksClassicEvents = __webpack_require__(9)
83+
var nativeEvents = __webpack_require__(7)
84+
var pjaxEvents = __webpack_require__(8)
85+
var turbolinksEvents = __webpack_require__(9)
86+
var turbolinksClassicDeprecatedEvents = __webpack_require__(11)
87+
var turbolinksClassicEvents = __webpack_require__(10)
8888

8989
// see what things are globally available
9090
// and setup event handlers to those things
@@ -127,13 +127,13 @@ module.exports = function(ujs) {
127127
// Also, try to gracefully import Babel 6 style default exports
128128
module.exports = function(className) {
129129
var constructor;
130-
130+
var topLevel = typeof window === "undefined" ? this : window;
131131
// Try to access the class globally first
132-
constructor = window[className];
132+
constructor = topLevel[className];
133133

134134
// If that didn't work, try eval
135135
if (!constructor) {
136-
constructor = eval.call(window, className);
136+
constructor = eval.call(topLevel, className);
137137
}
138138

139139
// Lastly, if there is a default attribute try that
@@ -160,7 +160,6 @@ module.exports = function(reqctx) {
160160
var parts = className.split(".")
161161
var filename = parts.shift()
162162
var keys = parts
163-
console.log(filename, keys)
164163
// Load the module:
165164
var component = reqctx("./" + filename)
166165
// Then access each key:
@@ -190,10 +189,17 @@ module.exports = __WEBPACK_EXTERNAL_MODULE_4__;
190189

191190
/***/ }),
192191
/* 5 */
192+
/***/ (function(module, exports) {
193+
194+
module.exports = __WEBPACK_EXTERNAL_MODULE_5__;
195+
196+
/***/ }),
197+
/* 6 */
193198
/***/ (function(module, exports, __webpack_require__) {
194199

195200
var React = __webpack_require__(3)
196201
var ReactDOM = __webpack_require__(4)
202+
var ReactDOMServer = __webpack_require__(5)
197203

198204
var detectEvents = __webpack_require__(0)
199205
var constructorFromGlobal = __webpack_require__(1)
@@ -209,7 +215,7 @@ var ReactRailsUJS = {
209215
PROPS_ATTR: 'data-react-props',
210216

211217
// If jQuery is detected, save a reference to it for event handlers
212-
jQuery: (typeof window.jQuery !== 'undefined') && window.jQuery,
218+
jQuery: (typeof window !== 'undefined') && (typeof window.jQuery !== 'undefined') && window.jQuery,
213219

214220
// helper method for the mount and unmount methods to find the
215221
// `data-react-class` DOM elements
@@ -252,6 +258,14 @@ var ReactRailsUJS = {
252258
this.getConstructor = constructorFromRequireContext(req)
253259
},
254260

261+
// Render `componentName` with `props` to a string,
262+
// using the specified `renderFunction` from `react-dom/server`.
263+
serverRender: function(renderFunction, componentName, props) {
264+
var componentClass = this.getConstructor(componentName)
265+
var element = React.createElement(componentClass, props)
266+
return ReactDOMServer[renderFunction](element)
267+
},
268+
255269
// Within `searchSelector`, find nodes which should have React components
256270
// inside them, and mount them with their props.
257271
mountComponents: function(searchSelector) {
@@ -289,13 +303,16 @@ var ReactRailsUJS = {
289303
},
290304
}
291305

292-
detectEvents(ReactRailsUJS)
306+
if (typeof window !== "undefined") {
307+
// Only setup events for browser (not server-rendering)
308+
detectEvents(ReactRailsUJS)
309+
}
293310

294311
module.exports = ReactRailsUJS
295312

296313

297314
/***/ }),
298-
/* 6 */
315+
/* 7 */
299316
/***/ (function(module, exports) {
300317

301318
module.exports = {
@@ -316,7 +333,7 @@ module.exports = {
316333

317334

318335
/***/ }),
319-
/* 7 */
336+
/* 8 */
320337
/***/ (function(module, exports) {
321338

322339
module.exports = {
@@ -330,7 +347,7 @@ module.exports = {
330347

331348

332349
/***/ }),
333-
/* 8 */
350+
/* 9 */
334351
/***/ (function(module, exports) {
335352

336353
module.exports = {
@@ -343,7 +360,7 @@ module.exports = {
343360

344361

345362
/***/ }),
346-
/* 9 */
363+
/* 10 */
347364
/***/ (function(module, exports) {
348365

349366
module.exports = {
@@ -357,7 +374,7 @@ module.exports = {
357374

358375

359376
/***/ }),
360-
/* 10 */
377+
/* 11 */
361378
/***/ (function(module, exports) {
362379

363380
module.exports = {

lib/generators/react/component_generator.rb

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ class ComponentGenerator < ::Rails::Generators::NamedBase
44
source_root File.expand_path '../../templates', __FILE__
55
desc <<-DESC.strip_heredoc
66
Description:
7-
Scaffold a react component into app/assets/javascripts/components.
7+
Scaffold a React component into `components/` of your Webpacker source or asset pipeline.
88
The generated component will include a basic render function and a PropTypes
99
hash to help with development.
1010
@@ -90,17 +90,29 @@ class ComponentGenerator < ::Rails::Generators::NamedBase
9090
}
9191

9292
def create_component_file
93-
extension = case
94-
when options[:es6]
95-
'es6.jsx'
96-
when options[:coffee]
97-
'js.jsx.coffee'
98-
else
99-
'js.jsx'
100-
end
101-
102-
file_path = File.join('app/assets/javascripts/components', "#{file_name}.#{extension}")
103-
template("component.#{extension}", file_path)
93+
template_extension = case
94+
when options[:es6]
95+
'es6.jsx'
96+
when options[:coffee]
97+
'js.jsx.coffee'
98+
else
99+
'js.jsx'
100+
end
101+
102+
# Prefer webpacker to sprockets:
103+
if defined?(Webpacker)
104+
extension = options[:coffee] ? "coffee" : "js"
105+
target_dir = Webpacker::Configuration.source_path
106+
.join("components")
107+
.relative_path_from(::Rails.root)
108+
.to_s
109+
else
110+
extension = template_extension
111+
target_dir = 'app/assets/javascripts/components'
112+
end
113+
114+
file_path = File.join(target_dir, "#{file_name}.#{extension}")
115+
template("component.#{template_extension}", file_path)
104116
end
105117

106118
private

lib/generators/react/install_generator.rb

Lines changed: 64 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,60 @@ class InstallGenerator < ::Rails::Generators::Base
1616
default: false,
1717
desc: "Don't generate server_rendering.js or config/initializers/react_server_rendering.rb"
1818

19+
# Make an empty `components/` directory in the right place:
1920
def create_directory
20-
empty_directory 'app/assets/javascripts/components'
21+
empty_directory File.join(javascript_dir, 'components')
2122
if !options[:skip_git]
22-
create_file 'app/assets/javascripts/components/.gitkeep'
23+
create_file File.join(javascript_dir, 'components/.gitkeep')
2324
end
2425
end
2526

26-
def inject_react
27-
require_react = "//= require react\n"
27+
# Add requires, setup UJS
28+
def setup_react
29+
if webpacker?
30+
setup_react_webpacker
31+
else
32+
setup_react_sprockets
33+
end
34+
end
35+
36+
def create_server_rendering
37+
if options[:skip_server_rendering]
38+
return
39+
elsif webpacker?
40+
ssr_manifest_path = File.join(javascript_dir, "server_rendering.js")
41+
template("server_rendering_pack.js", ssr_manifest_path)
42+
else
43+
ssr_manifest_path = File.join(javascript_dir, "server_rendering.js")
44+
template("server_rendering.js", ssr_manifest_path)
45+
initializer_path = "config/initializers/react_server_rendering.rb"
46+
template("react_server_rendering.rb", initializer_path)
47+
end
48+
end
49+
50+
private
51+
52+
def webpacker?
53+
!!defined?(Webpacker)
54+
end
55+
56+
def javascript_dir
57+
if webpacker?
58+
Webpacker::Configuration.source_path
59+
.join(Webpacker::Configuration.entry_path)
60+
.relative_path_from(::Rails.root)
61+
.to_s
62+
else
63+
'app/assets/javascripts'
64+
end
65+
end
66+
67+
def manifest
68+
Pathname.new(destination_root).join(javascript_dir, 'application.js')
69+
end
70+
71+
def setup_react_sprockets
72+
require_react = "//= require react\n//= require react_ujs\n//= require components\n"
2873

2974
if manifest.exist?
3075
manifest_contents = File.read(manifest)
@@ -39,34 +84,27 @@ def inject_react
3984
else
4085
create_file manifest, require_react
4186
end
42-
end
43-
44-
def inject_components
45-
inject_into_file manifest, "//= require components\n", {after: "//= require react\n"}
46-
end
47-
48-
def inject_react_ujs
49-
inject_into_file manifest, "//= require react_ujs\n", {after: "//= require react\n"}
50-
end
5187

52-
def create_components
5388
components_js = "//= require_tree ./components\n"
54-
components_file = File.join(*%w(app assets javascripts components.js))
89+
components_file = File.join(javascript_dir, "components.js")
5590
create_file components_file, components_js
5691
end
5792

58-
def create_server_rendering
59-
return if options[:skip_server_rendering]
60-
manifest_path = "app/assets/javascripts/server_rendering.js"
61-
template("server_rendering.js", manifest_path)
62-
initializer_path = "config/initializers/react_server_rendering.rb"
63-
template("react_server_rendering.rb", initializer_path)
64-
end
65-
66-
private
93+
WEBPACKER_SETUP_UJS = <<-JS
94+
// Support component names relative to this directory:
95+
var componentRequireContext = require.context("components", true)
96+
var ReactRailsUJS = require("react_ujs")
97+
ReactRailsUJS.loadContext(componentRequireContext)
98+
JS
6799

68-
def manifest
69-
Pathname.new(destination_root).join('app/assets/javascripts', 'application.js')
100+
def setup_react_webpacker
101+
yarn_binstub = File.expand_path("./bin/yarn", ::Rails.root)
102+
`#{yarn_binstub} add react_ujs`
103+
if manifest.exist?
104+
append_file(manifest, WEBPACKER_SETUP_UJS)
105+
else
106+
create_file(manifest, WEBPACKER_SETUP_UJS)
107+
end
70108
end
71109
end
72110
end

lib/generators/templates/server_rendering.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//= require react-server
2+
//= require react_ujs
23
//= require ./components
34
//
45
// By default, this file is loaded for server-side rendering.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// By default, this pack is loaded for server-side rendering.
2+
// It must expose react_ujs as `ReactRailsUJS` and prepare a require context.
3+
var componentRequireContext = require.context("components", true)
4+
var ReactRailsUJS = require("react_ujs")
5+
ReactRailsUJS.loadContext(componentRequireContext)

lib/react/server_rendering/exec_js_renderer.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def render_from_parts(before, main, after)
4141

4242
def main_render(component_name, props, prerender_options)
4343
render_function = prerender_options.fetch(:render_function, "renderToString")
44-
"ReactDOMServer.#{render_function}(React.createElement(#{component_name}, #{props}))"
44+
"ReactRailsUJS.serverRender('#{render_function}', #{component_name}, #{props})"
4545
end
4646

4747
def compose_js(before, main, after)

0 commit comments

Comments
 (0)