react-rails 1.11.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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