react-rails 1.11.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -0
  3. data/README.md +294 -214
  4. data/lib/assets/javascripts/react_ujs.js +429 -7
  5. data/lib/generators/react/component_generator.rb +24 -12
  6. data/lib/generators/react/install_generator.rb +76 -18
  7. data/lib/generators/templates/react_server_rendering.rb +2 -0
  8. data/lib/generators/templates/server_rendering.js +6 -0
  9. data/lib/generators/templates/server_rendering_pack.js +5 -0
  10. data/lib/react/jsx.rb +2 -0
  11. data/lib/react/rails/component_mount.rb +23 -5
  12. data/lib/react/rails/controller_lifecycle.rb +35 -7
  13. data/lib/react/rails/railtie.rb +17 -11
  14. data/lib/react/rails/version.rb +1 -1
  15. data/lib/react/server_rendering.rb +16 -4
  16. data/lib/react/server_rendering/{sprockets_renderer.rb → bundle_renderer.rb} +40 -20
  17. data/lib/react/server_rendering/{sprockets_renderer → bundle_renderer}/console_polyfill.js +0 -0
  18. data/lib/react/server_rendering/{sprockets_renderer → bundle_renderer}/console_replay.js +1 -1
  19. data/lib/react/server_rendering/bundle_renderer/console_reset.js +3 -0
  20. data/lib/react/server_rendering/{sprockets_renderer → bundle_renderer}/timeout_polyfill.js +0 -0
  21. data/lib/react/server_rendering/exec_js_renderer.rb +4 -1
  22. data/lib/react/server_rendering/webpacker_manifest_container.rb +34 -0
  23. data/lib/react/server_rendering/yaml_manifest_container.rb +1 -1
  24. metadata +16 -16
  25. data/lib/assets/javascripts/react_ujs_event_setup.js +0 -29
  26. data/lib/assets/javascripts/react_ujs_mount.js +0 -104
  27. data/lib/assets/javascripts/react_ujs_native.js +0 -18
  28. data/lib/assets/javascripts/react_ujs_pjax.js +0 -10
  29. data/lib/assets/javascripts/react_ujs_turbolinks.js +0 -9
  30. data/lib/assets/javascripts/react_ujs_turbolinks_classic.js +0 -10
  31. data/lib/assets/javascripts/react_ujs_turbolinks_classic_deprecated.js +0 -13
  32. data/lib/generators/react/ujs_generator.rb +0 -44
@@ -1,7 +1,429 @@
1
- //= require react_ujs_mount
2
- //= require react_ujs_turbolinks
3
- //= require react_ujs_turbolinks_classic
4
- //= require react_ujs_turbolinks_classic_deprecated
5
- //= require react_ujs_pjax
6
- //= require react_ujs_native
7
- //= require react_ujs_event_setup
1
+ (function webpackUniversalModuleDefinition(root, factory) {
2
+ if(typeof exports === 'object' && typeof module === 'object')
3
+ module.exports = factory(require("react"), require("react-dom"), require("react-dom/server"));
4
+ else if(typeof define === 'function' && define.amd)
5
+ define(["react", "react-dom", "react-dom/server"], factory);
6
+ else if(typeof exports === 'object')
7
+ exports["ReactRailsUJS"] = factory(require("react"), require("react-dom"), require("react-dom/server"));
8
+ else
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__) {
11
+ return /******/ (function(modules) { // webpackBootstrap
12
+ /******/ // The module cache
13
+ /******/ var installedModules = {};
14
+ /******/
15
+ /******/ // The require function
16
+ /******/ function __webpack_require__(moduleId) {
17
+ /******/
18
+ /******/ // Check if module is in cache
19
+ /******/ if(installedModules[moduleId])
20
+ /******/ return installedModules[moduleId].exports;
21
+ /******/
22
+ /******/ // Create a new module (and put it into the cache)
23
+ /******/ var module = installedModules[moduleId] = {
24
+ /******/ i: moduleId,
25
+ /******/ l: false,
26
+ /******/ exports: {}
27
+ /******/ };
28
+ /******/
29
+ /******/ // Execute the module function
30
+ /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
31
+ /******/
32
+ /******/ // Flag the module as loaded
33
+ /******/ module.l = true;
34
+ /******/
35
+ /******/ // Return the exports of the module
36
+ /******/ return module.exports;
37
+ /******/ }
38
+ /******/
39
+ /******/
40
+ /******/ // expose the modules object (__webpack_modules__)
41
+ /******/ __webpack_require__.m = modules;
42
+ /******/
43
+ /******/ // expose the module cache
44
+ /******/ __webpack_require__.c = installedModules;
45
+ /******/
46
+ /******/ // identity function for calling harmony imports with the correct context
47
+ /******/ __webpack_require__.i = function(value) { return value; };
48
+ /******/
49
+ /******/ // define getter function for harmony exports
50
+ /******/ __webpack_require__.d = function(exports, name, getter) {
51
+ /******/ if(!__webpack_require__.o(exports, name)) {
52
+ /******/ Object.defineProperty(exports, name, {
53
+ /******/ configurable: false,
54
+ /******/ enumerable: true,
55
+ /******/ get: getter
56
+ /******/ });
57
+ /******/ }
58
+ /******/ };
59
+ /******/
60
+ /******/ // getDefaultExport function for compatibility with non-harmony modules
61
+ /******/ __webpack_require__.n = function(module) {
62
+ /******/ var getter = module && module.__esModule ?
63
+ /******/ function getDefault() { return module['default']; } :
64
+ /******/ function getModuleExports() { return module; };
65
+ /******/ __webpack_require__.d(getter, 'a', getter);
66
+ /******/ return getter;
67
+ /******/ };
68
+ /******/
69
+ /******/ // Object.prototype.hasOwnProperty.call
70
+ /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
71
+ /******/
72
+ /******/ // __webpack_public_path__
73
+ /******/ __webpack_require__.p = "";
74
+ /******/
75
+ /******/ // Load entry module and return exports
76
+ /******/ return __webpack_require__(__webpack_require__.s = 6);
77
+ /******/ })
78
+ /************************************************************************/
79
+ /******/ ([
80
+ /* 0 */
81
+ /***/ (function(module, exports) {
82
+
83
+ // Assume className is simple and can be found at top-level (window).
84
+ // Fallback to eval to handle cases like 'My.React.ComponentName'.
85
+ // Also, try to gracefully import Babel 6 style default exports
86
+ var topLevel = typeof window === "undefined" ? this : window;
87
+
88
+ module.exports = function(className) {
89
+ var constructor;
90
+ // Try to access the class globally first
91
+ constructor = topLevel[className];
92
+
93
+ // If that didn't work, try eval
94
+ if (!constructor) {
95
+ constructor = eval(className);
96
+ }
97
+
98
+ // Lastly, if there is a default attribute try that
99
+ if (constructor && constructor['default']) {
100
+ constructor = constructor['default'];
101
+ }
102
+
103
+ return constructor;
104
+ }
105
+
106
+
107
+ /***/ }),
108
+ /* 1 */
109
+ /***/ (function(module, exports, __webpack_require__) {
110
+
111
+ var nativeEvents = __webpack_require__(7)
112
+ var pjaxEvents = __webpack_require__(8)
113
+ var turbolinksEvents = __webpack_require__(9)
114
+ var turbolinksClassicDeprecatedEvents = __webpack_require__(11)
115
+ var turbolinksClassicEvents = __webpack_require__(10)
116
+
117
+ // see what things are globally available
118
+ // and setup event handlers to those things
119
+ module.exports = function(ujs) {
120
+ if (ujs.jQuery) {
121
+ ujs.handleEvent = function(eventName, callback) {
122
+ ujs.jQuery(document).on(eventName, callback);
123
+ };
124
+ } else {
125
+ ujs.handleEvent = function(eventName, callback) {
126
+ document.addEventListener(eventName, callback);
127
+ };
128
+ }
129
+
130
+ // Detect which kind of events to set up:
131
+ if (typeof Turbolinks !== 'undefined' && Turbolinks.supported) {
132
+ if (typeof Turbolinks.EVENTS !== 'undefined') {
133
+ // Turbolinks.EVENTS is in classic version 2.4.0+
134
+ turbolinksClassicEvents.setup(ujs)
135
+ } else if (typeof Turbolinks.controller !== "undefined") {
136
+ // Turbolinks.controller is in version 5+
137
+ turbolinksEvents.setup(ujs);
138
+ } else {
139
+ turbolinksClassicDeprecatedEvents.setup(ujs);
140
+ }
141
+ } else if (typeof $ !== "undefined" && typeof $.pjax === 'function') {
142
+ pjaxEvents.setup(ujs);
143
+ } else {
144
+ nativeEvents.setup(ujs);
145
+ }
146
+ }
147
+
148
+
149
+ /***/ }),
150
+ /* 2 */
151
+ /***/ (function(module, exports, __webpack_require__) {
152
+
153
+ // Make a function which:
154
+ // - First tries to require the name
155
+ // - Then falls back to global lookup
156
+ var fromGlobal = __webpack_require__(0)
157
+ var fromRequireContext = __webpack_require__(12)
158
+
159
+ module.exports = function(reqctx) {
160
+ var fromCtx = fromRequireContext(reqctx)
161
+ return function(className) {
162
+ var component;
163
+ try {
164
+ // `require` will raise an error if this className isn't found:
165
+ component = fromCtx(className)
166
+ } catch (err) {
167
+ // fallback to global:
168
+ component = fromGlobal(className)
169
+ }
170
+ return component
171
+ }
172
+ }
173
+
174
+
175
+ /***/ }),
176
+ /* 3 */
177
+ /***/ (function(module, exports) {
178
+
179
+ module.exports = __WEBPACK_EXTERNAL_MODULE_3__;
180
+
181
+ /***/ }),
182
+ /* 4 */
183
+ /***/ (function(module, exports) {
184
+
185
+ module.exports = __WEBPACK_EXTERNAL_MODULE_4__;
186
+
187
+ /***/ }),
188
+ /* 5 */
189
+ /***/ (function(module, exports) {
190
+
191
+ module.exports = __WEBPACK_EXTERNAL_MODULE_5__;
192
+
193
+ /***/ }),
194
+ /* 6 */
195
+ /***/ (function(module, exports, __webpack_require__) {
196
+
197
+ var React = __webpack_require__(3)
198
+ var ReactDOM = __webpack_require__(4)
199
+ var ReactDOMServer = __webpack_require__(5)
200
+
201
+ var detectEvents = __webpack_require__(1)
202
+ var constructorFromGlobal = __webpack_require__(0)
203
+ var constructorFromRequireContextWithGlobalFallback = __webpack_require__(2)
204
+
205
+ var ReactRailsUJS = {
206
+ // This attribute holds the name of component which should be mounted
207
+ // example: `data-react-class="MyApp.Items.EditForm"`
208
+ CLASS_NAME_ATTR: 'data-react-class',
209
+
210
+ // This attribute holds JSON stringified props for initializing the component
211
+ // example: `data-react-props="{\"item\": { \"id\": 1, \"name\": \"My Item\"} }"`
212
+ PROPS_ATTR: 'data-react-props',
213
+
214
+ // If jQuery is detected, save a reference to it for event handlers
215
+ jQuery: (typeof window !== 'undefined') && (typeof window.jQuery !== 'undefined') && window.jQuery,
216
+
217
+ // helper method for the mount and unmount methods to find the
218
+ // `data-react-class` DOM elements
219
+ findDOMNodes: function(selector) {
220
+ var classNameAttr = ReactRailsUJS.CLASS_NAME_ATTR
221
+ // we will use fully qualified paths as we do not bind the callbacks
222
+ var selector, parent;
223
+
224
+ switch (typeof searchSelector) {
225
+ case 'undefined':
226
+ selector = '[' + classNameAttr + ']';
227
+ parent = document;
228
+ break;
229
+ case 'object':
230
+ selector = '[' + classNameAttr + ']';
231
+ parent = searchSelector;
232
+ break;
233
+ case 'string':
234
+ selector = searchSelector + '[' + classNameAttr + '], ' +
235
+ searchSelector + ' [' + classNameAttr + ']';
236
+ parent = document;
237
+ break
238
+ default:
239
+ break;
240
+ }
241
+
242
+ if (ReactRailsUJS.jQuery) {
243
+ return ReactRailsUJS.jQuery(selector, parent);
244
+ } else {
245
+ return parent.querySelectorAll(selector);
246
+ }
247
+ },
248
+
249
+ // Get the constructor for a className (returns a React class)
250
+ // Override this function to lookup classes in a custom way,
251
+ // the default is ReactRailsUJS.ComponentGlobal
252
+ getConstructor: constructorFromGlobal,
253
+
254
+ useContext: function(req) {
255
+ this.getConstructor = constructorFromRequireContextWithGlobalFallback(req)
256
+ },
257
+
258
+ // Render `componentName` with `props` to a string,
259
+ // using the specified `renderFunction` from `react-dom/server`.
260
+ serverRender: function(renderFunction, componentName, props) {
261
+ var componentClass = this.getConstructor(componentName)
262
+ var element = React.createElement(componentClass, props)
263
+ return ReactDOMServer[renderFunction](element)
264
+ },
265
+
266
+ // Within `searchSelector`, find nodes which should have React components
267
+ // inside them, and mount them with their props.
268
+ mountComponents: function(searchSelector) {
269
+ var ujs = ReactRailsUJS
270
+ var nodes = ujs.findDOMNodes(searchSelector);
271
+
272
+ for (var i = 0; i < nodes.length; ++i) {
273
+ var node = nodes[i];
274
+ var className = node.getAttribute(ujs.CLASS_NAME_ATTR);
275
+ var constructor = ujs.getConstructor(className);
276
+ var propsJson = node.getAttribute(ujs.PROPS_ATTR);
277
+ var props = propsJson && JSON.parse(propsJson);
278
+
279
+ if (!constructor) {
280
+ var message = "Cannot find component: '" + className + "'"
281
+ if (console && console.log) {
282
+ console.log("%c[react-rails] %c" + message + " for element", "font-weight: bold", "", node)
283
+ }
284
+ throw new Error(message + ". Make sure your component is available to render.")
285
+ } else {
286
+ ReactDOM.render(React.createElement(constructor, props), node);
287
+ }
288
+ }
289
+ },
290
+
291
+ // Within `searchSelector`, find nodes which have React components
292
+ // inside them, and unmount those components.
293
+ unmountComponents: function(searchSelector) {
294
+ var nodes = ReactRailsUJS.findDOMNodes(searchSelector);
295
+
296
+ for (var i = 0; i < nodes.length; ++i) {
297
+ var node = nodes[i];
298
+ ReactDOM.unmountComponentAtNode(node);
299
+ }
300
+ },
301
+ }
302
+
303
+ if (typeof window !== "undefined") {
304
+ // Only setup events for browser (not server-rendering)
305
+ detectEvents(ReactRailsUJS)
306
+ }
307
+
308
+ // It's a bit of a no-no to populate the global namespace,
309
+ // but we really need it!
310
+ // We need access to this object for server rendering, and
311
+ // we can't do a dynamic `require`, so we'll grab it from here:
312
+ self.ReactRailsUJS = ReactRailsUJS
313
+
314
+ module.exports = ReactRailsUJS
315
+
316
+
317
+ /***/ }),
318
+ /* 7 */
319
+ /***/ (function(module, exports) {
320
+
321
+ module.exports = {
322
+ // Attach handlers to browser events to mount
323
+ // (There are no unmount handlers since the page is destroyed on navigation)
324
+ setup: function(ujs) {
325
+ if (ujs.jQuery) {
326
+ // Use jQuery if it's present:
327
+ ujs.jQuery(function() { ujs.mountComponents() });
328
+ } else if ('addEventListener' in window) {
329
+ document.addEventListener('DOMContentLoaded', function() { ujs.mountComponents() });
330
+ } else {
331
+ // add support to IE8 without jQuery
332
+ window.attachEvent('onload', function() { ujs.mountComponents() });
333
+ }
334
+ }
335
+ }
336
+
337
+
338
+ /***/ }),
339
+ /* 8 */
340
+ /***/ (function(module, exports) {
341
+
342
+ module.exports = {
343
+ // pjax support
344
+ setup: function(ujs) {
345
+ ujs.handleEvent('ready', function() { ujs.mountComponents() });
346
+ ujs.handleEvent('pjax:end', function(e) { ujs.mountComponents(e.target) });
347
+ ujs.handleEvent('pjax:beforeReplace', function(e) { ujs.unmountComponents(e.target) });
348
+ }
349
+ }
350
+
351
+
352
+ /***/ }),
353
+ /* 9 */
354
+ /***/ (function(module, exports) {
355
+
356
+ module.exports = {
357
+ // Turbolinks 5+ got rid of named events (?!)
358
+ setup: function(ujs) {
359
+ ujs.handleEvent('DOMContentLoaded', function() { ujs.mountComponents() })
360
+ ujs.handleEvent('turbolinks:render', function() { ujs.mountComponents() })
361
+ ujs.handleEvent('turbolinks:before-render', function() { ujs.unmountComponents() })
362
+ },
363
+ }
364
+
365
+
366
+ /***/ }),
367
+ /* 10 */
368
+ /***/ (function(module, exports) {
369
+
370
+ module.exports = {
371
+ // Attach handlers to Turbolinks-Classic events
372
+ // for mounting and unmounting components
373
+ setup: function(ujs) {
374
+ ujs.handleEvent(Turbolinks.EVENTS.CHANGE, function() { ujs.mountComponents() });
375
+ ujs.handleEvent(Turbolinks.EVENTS.BEFORE_UNLOAD, function() { ujs.unmountComponents() });
376
+ }
377
+ }
378
+
379
+
380
+ /***/ }),
381
+ /* 11 */
382
+ /***/ (function(module, exports) {
383
+
384
+ module.exports = {
385
+ // Before Turbolinks 2.4.0, Turbolinks didn't
386
+ // have named events and didn't have a before-unload event.
387
+ // Also, it didn't work with the Turbolinks cache, see
388
+ // https://github.com/reactjs/react-rails/issues/87
389
+ setup: function(ujs) {
390
+ Turbolinks.pagesCached(0)
391
+ ujs.handleEvent('page:change', function() { ujs.mountComponents() });
392
+ ujs.handleEvent('page:receive', function() { ujs.unmountComponents() });
393
+ }
394
+ }
395
+
396
+
397
+ /***/ }),
398
+ /* 12 */
399
+ /***/ (function(module, exports) {
400
+
401
+ // Load React components by requiring them from "components/", for example:
402
+ //
403
+ // - "pages/index" -> `require("components/pages/index")`
404
+ // - "pages/show.Header" -> `require("components/pages/show").Header`
405
+ // - "pages/show.Body.Content" -> `require("components/pages/show").Body.Content`
406
+ //
407
+ module.exports = function(reqctx) {
408
+ return function(className) {
409
+ var parts = className.split(".")
410
+ var filename = parts.shift()
411
+ var keys = parts
412
+ // Load the module:
413
+ var component = reqctx("./" + filename)
414
+ // Then access each key:
415
+ keys.forEach(function(k) {
416
+ component = component[k]
417
+ })
418
+ // support `export default`
419
+ if (component.__esModule) {
420
+ component = component["default"]
421
+ }
422
+ return component
423
+ }
424
+ }
425
+
426
+
427
+ /***/ })
428
+ /******/ ]);
429
+ });
@@ -4,7 +4,7 @@ module React
4
4
  source_root File.expand_path '../../templates', __FILE__
5
5
  desc <<-DESC.strip_heredoc
6
6
  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.
8
8
  The generated component will include a basic render function and a PropTypes
9
9
  hash to help with development.
10
10
 
@@ -90,17 +90,29 @@ module React
90
90
  }
91
91
 
92
92
  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)
104
116
  end
105
117
 
106
118
  private