react-on-rails 13.0.2 → 13.2.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.
package/README.md CHANGED
@@ -9,7 +9,12 @@
9
9
 
10
10
  ---
11
11
 
12
- [![License](https://img.shields.io/badge/license-mit-green.svg)](LICENSE.md) [![Build Status](https://travis-ci.org/shakacode/react_on_rails.svg?branch=master)](https://travis-ci.org/shakacode/react_on_rails) [![Gem Version](https://badge.fury.io/rb/react_on_rails.svg)](https://badge.fury.io/rb/react_on_rails) [![npm version](https://badge.fury.io/js/react-on-rails.svg)](https://badge.fury.io/js/react-on-rails) [![Code Climate](https://codeclimate.com/github/shakacode/react_on_rails/badges/gpa.svg)](https://codeclimate.com/github/shakacode/react_on_rails) [![Coverage Status](https://coveralls.io/repos/shakacode/react_on_rails/badge.svg?branch=master&service=github)](https://coveralls.io/github/shakacode/react_on_rails?branch=master) [![](https://ruby-gem-downloads-badge.herokuapp.com/react_on_rails?type=total)](https://rubygems.org/gems/react_on_rails)
12
+ [![License](https://img.shields.io/badge/license-mit-green.svg)](LICENSE.md)[![Gem Version](https://badge.fury.io/rb/react_on_rails.svg)](https://badge.fury.io/rb/react_on_rails) [![npm version](https://badge.fury.io/js/react-on-rails.svg)](https://badge.fury.io/js/react-on-rails) [![Code Climate](https://codeclimate.com/github/shakacode/react_on_rails/badges/gpa.svg)](https://codeclimate.com/github/shakacode/react_on_rails) [![Coverage Status](https://coveralls.io/repos/shakacode/react_on_rails/badge.svg?branch=master&service=github)](https://coveralls.io/github/shakacode/react_on_rails?branch=master) [![](https://ruby-gem-downloads-badge.herokuapp.com/react_on_rails?type=total)](https://rubygems.org/gems/react_on_rails)
13
+
14
+ [![Build Main](https://github.com/shakacode/react_on_rails/actions/workflows/main.yml/badge.svg)](https://github.com/shakacode/react_on_rails/actions/workflows/main.yml)
15
+ [![Build JS Tests](https://github.com/shakacode/react_on_rails/actions/workflows/package-js-tests.yml/badge.svg)](https://github.com/shakacode/react_on_rails/actions/workflows/package-js-tests.yml)
16
+ [![Build Rspec Tests](https://github.com/shakacode/react_on_rails/actions/workflows/rspec-package-specs.yml/badge.svg)](https://github.com/shakacode/react_on_rails/actions/workflows/rspec-package-specs.yml)
17
+ [![Linting](https://github.com/shakacode/react_on_rails/actions/workflows/lint-js-and-ruby.yml/badge.svg)](https://github.com/shakacode/react_on_rails/actions/workflows/lint-js-and-ruby.yml)
13
18
 
14
19
  # News
15
20
  * ShakaCode now maintains the official successor to `rails/webpacker`, [`shakapacker`](https://github.com/shakacode/shakapacker).
@@ -27,7 +32,7 @@ This project is maintained by the software consulting firm [ShakaCode](https://w
27
32
  Are you interested in optimizing your webpack setup for React on Rails including code
28
33
  splitting with [react-router](https://github.com/ReactTraining/react-router#readme) and
29
34
  [loadable-components](https://loadable-components.com/) with server-side rendering for SEO and hot-reloading for developers?
30
- We did this for Popmenu, [lowering Heroku costs 20-25% while getting a 73% decrease in average response times](https://www.shakacode.com/recent-work/popmenu/). Several years later, Popmenu is serving millions of SSR requests per day React on Rails.
35
+ We did this for Popmenu, [lowering Heroku costs 20-25% while getting a 73% decrease in average response times](https://www.shakacode.com/recent-work/popmenu/). Several years later, Popmenu is serving millions of SSR requests per day with React on Rails.
31
36
 
32
37
  Check out [React on Rails Pro](https://www.shakacode.com/react-on-rails-pro/). For more information, feel free to contact Justin Gordon, [justin@shakacode.com](mailto:justin@shakacode.com), maintainer of React on Rails.
33
38
 
@@ -43,9 +48,12 @@ To provide a high performance framework for integrating Ruby on Rails with React
43
48
 
44
49
  Given that `rails/webpacker` gem already provides basic React integration, why would you use "React on Rails"?
45
50
 
51
+ 1. Automatic configuration of what bundles are added to the page based on what React components are on the page. This results in faster browser loading time via smaller bundle sizes.
52
+ 1. Keep up with the latest changes of different versions of React. React 18 is supported.
46
53
  1. Easy passing of props directly from your Rails view to your React components rather than having your Rails view load and then make a separate request to your API.
47
- 1. Tight integration with [shakapacker](https://github.com/shakacode/shakapacker) (or it's predecessor [rails/webpacker](https://github.com/rails/webpacker)).
54
+ Tight integration with [shakapacker](https://github.com/shakacode/shakapacker) (or it's predecessor [rails/webpacker](https://github.com/rails/webpacker)).
48
55
  1. Server-Side Rendering (SSR), often used for SEO crawler indexing and UX performance.
56
+ 1. [Automated optimized entry-point creation and bundle inclusion when placing a component on a page. With this feature, you no longer need to configure `javascript_pack_tags` and `stylesheet_pack_tags` on your layouts based on what’s shown. “It just works!”](https://www.shakacode.com/react-on-rails/docs/guides/file-system-based-automated-bundle-generation.md)
49
57
  1. [Redux](https://github.com/reactjs/redux) and [React Router](https://github.com/ReactTraining/react-router#readme) integration with server-side-rendering.
50
58
  1. [Internationalization (I18n) and (localization)](https://www.shakacode.com/react-on-rails/docs/guides/i18n)
51
59
  1. A supportive community. This [web search shows how live public sites are using React on Rails](https://publicwww.com/websites/%22react-on-rails%22++-undeveloped.com+depth%3Aall/).
@@ -65,7 +73,7 @@ _Requires creating a free account._
65
73
 
66
74
  ## Prerequisites
67
75
 
68
- Ruby on Rails >=5, rails/webpacker >= 4.2, Ruby >= 2.7
76
+ Ruby on Rails >=5, rails/webpacker >= 4.2 or shakapacker > 6, Ruby >= 2.7
69
77
 
70
78
  # Support
71
79
 
@@ -81,24 +89,41 @@ Ruby on Rails >=5, rails/webpacker >= 4.2, Ruby >= 2.7
81
89
 
82
90
  Bug reports and pull requests are welcome. See [Contributing](https://github.com/shakacode/react_on_rails/tree/master/CONTRIBUTING.md) to get started, and the [list of help wanted issues](https://github.com/shakacode/react_on_rails/labels/contributions%3A%20up%20for%20grabs%21).
83
91
 
84
- # Supporters
85
-
86
- The following companies support this open source project, and ShakaCode uses their products! Justin writes React on Rails on [RubyMine](https://www.jetbrains.com/ruby/). We use [Scout](https://scoutapp.com/) to monitor the live performance of [HiChee.com](https://HiChee.com), [Rails AutoScale](https://railsautoscale.com) to scale the dynos of HiChee, and [HoneyBadger](https://www.honeybadger.io/) to monitor application errors. We love [BrowserStack](https://www.browserstack.com) to solve problems with oddball browsers. [Status Hero](https://statushero.com/) keeps the team posted on daily progress; it's so much better than live standups.
87
-
88
- [![RubyMine](https://user-images.githubusercontent.com/1118459/114100597-3b0e3000-9860-11eb-9b12-73beb1a184b2.png)](https://www.jetbrains.com/ruby/)
89
- [![Scout](https://user-images.githubusercontent.com/1118459/41828269-106b40f8-77d0-11e8-8d19-9c4b167ef9d8.png)](https://scoutapp.com/)
90
- [![Rails AutoScale](https://user-images.githubusercontent.com/1118459/103197530-48dc0e80-488a-11eb-8b1b-a16664b30274.png)](https://railsautoscale.com/)
91
- [![BrowserStack](https://cloud.githubusercontent.com/assets/1118459/23203304/1261e468-f886-11e6-819e-93b1a3f17da4.png)](https://www.browserstack.com)
92
- [![HoneyBadger](https://user-images.githubusercontent.com/1118459/114100696-63962a00-9860-11eb-8ac1-75ca02856d8e.png)](https://www.honeybadger.io/)
93
- [![StatusHero](https://user-images.githubusercontent.com/1118459/126868048-3fe64e54-4d6d-4066-9df2-8cf6fbaeb314.png)](https://statushero.com/)
94
-
95
- ShakaCode's favorite project tracking tool is [Shortcut](https://shortcut.com/). If you want to **try Shortcut and get 2 months free beyond the 14-day trial period**, click [here to use ShakaCode's referral code](http://r.clbh.se/mvfoNeH). We're participating in their awesome triple-sided referral program, which you can read about [here](https://clubhouse.io/referral/). By using our [referral code](http://r.clbh.se/mvfoNeH) you'll be supporting ShakaCode and, thus, React on Rails!
96
-
97
- Aloha and best wishes from Justin and the ShakaCode team!
98
-
99
92
  # Work with Us
100
93
  ShakaCode is **[hiring passionate software engineers](http://www.shakacode.com/career)** to work on our projects, including [HiChee](https://hichee.com)!
101
94
 
102
95
  # License
103
96
 
104
97
  The gem is available as open source under the terms of the [MIT License](https://github.com/shakacode/react_on_rails/tree/master/LICENSE.md).
98
+
99
+ # Supporters
100
+
101
+ <a href="https://www.jetbrains.com">
102
+ <img src="https://user-images.githubusercontent.com/4244251/184881139-42e4076b-024b-4b30-8c60-c3cd0e758c0a.png" alt="JetBrains" height="120px">
103
+ </a>
104
+ <a href="https://scoutapp.com">
105
+ <picture>
106
+ <source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/4244251/184881147-0d077438-3978-40da-ace9-4f650d2efe2e.png">
107
+ <source media="(prefers-color-scheme: light)" srcset="https://user-images.githubusercontent.com/4244251/184881152-9f2d8fba-88ac-4ba6-873b-22387f8711c5.png">
108
+ <img alt="ScoutAPM" src="https://user-images.githubusercontent.com/4244251/184881152-9f2d8fba-88ac-4ba6-873b-22387f8711c5.png" height="120px">
109
+ </picture>
110
+ </a>
111
+ <br />
112
+ <a href="https://www.browserstack.com">
113
+ <picture>
114
+ <source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/4244251/184881122-407dcc29-df78-4b20-a9ad-f597b56f6cdb.png">
115
+ <source media="(prefers-color-scheme: light)" srcset="https://user-images.githubusercontent.com/4244251/184881129-e1edf4b7-3ae1-4ea8-9e6d-3595cf01609e.png">
116
+ <img alt="BrowserStack" src="https://user-images.githubusercontent.com/4244251/184881129-e1edf4b7-3ae1-4ea8-9e6d-3595cf01609e.png" height="55px">
117
+ </picture>
118
+ </a>
119
+ <a href="https://railsautoscale.com">
120
+ <img src="https://user-images.githubusercontent.com/4244251/184881144-95c2c25c-9879-4069-864d-4e67d6ed39d2.png" alt="Rails Autoscale" height="55px">
121
+ </a>
122
+ <a href="https://www.honeybadger.io">
123
+ <img src="https://user-images.githubusercontent.com/4244251/184881133-79ee9c3c-8165-4852-958e-31687b9536f4.png" alt="Honeybadger" height="55px">
124
+ </a>
125
+
126
+ <br />
127
+ <br />
128
+
129
+ The following companies support our open source projects, and ShakaCode uses their products!
@@ -1,2 +1,2 @@
1
- declare const _default: import("./types/index").ReactOnRails;
1
+ declare const _default: import("./types").ReactOnRails;
2
2
  export default _default;
@@ -31,8 +31,7 @@ var buildConsoleReplay_1 = __importDefault(require("./buildConsoleReplay"));
31
31
  var createReactOutput_1 = __importDefault(require("./createReactOutput"));
32
32
  var Authenticity_1 = __importDefault(require("./Authenticity"));
33
33
  var context_1 = __importDefault(require("./context"));
34
- var reactHydrate_1 = __importDefault(require("./reactHydrate"));
35
- var reactRender_1 = __importDefault(require("./reactRender"));
34
+ var reactHydrateOrRender_1 = __importDefault(require("./reactHydrateOrRender"));
36
35
  var ctx = context_1.default();
37
36
  if (ctx === undefined) {
38
37
  throw new Error("The context (usually Window or NodeJS's Global) is undefined.");
@@ -77,6 +76,16 @@ ctx.ReactOnRails = {
77
76
  if (throwIfMissing === void 0) { throwIfMissing = true; }
78
77
  return StoreRegistry_1.default.getStore(name, throwIfMissing);
79
78
  },
79
+ /**
80
+ * Renders or hydrates the react element passed. In case react version is >=18 will use the new api.
81
+ * @param domNode
82
+ * @param reactElement
83
+ * @param hydrate if true will perform hydration, if false will render
84
+ * @returns {Root|ReactComponent|ReactElement|null}
85
+ */
86
+ reactHydrateOrRender: function (domNode, reactElement, hydrate) {
87
+ return reactHydrateOrRender_1.default(domNode, reactElement, hydrate);
88
+ },
80
89
  /**
81
90
  * Set options for ReactOnRails, typically before you call ReactOnRails.register
82
91
  * Available Options:
@@ -159,24 +168,34 @@ ctx.ReactOnRails = {
159
168
  StoreRegistry_1.default.clearHydratedStores();
160
169
  },
161
170
  /**
171
+ * @example
162
172
  * ReactOnRails.render("HelloWorldApp", {name: "Stranger"}, 'app');
163
173
  *
164
174
  * Does this:
165
- * ReactDOM.render(React.createElement(HelloWorldApp, {name: "Stranger"}),
166
- * document.getElementById('app'))
175
+ * ```js
176
+ * ReactDOM.render(React.createElement(HelloWorldApp, {name: "Stranger"}),
177
+ * document.getElementById('app'))
178
+ * ```
179
+ * under React 16/17 and
180
+ * ```js
181
+ * const root = ReactDOMClient.createRoot(document.getElementById('app'))
182
+ * root.render(React.createElement(HelloWorldApp, {name: "Stranger"}))
183
+ * return root
184
+ * ```
185
+ * under React 18+.
167
186
  *
168
187
  * @param name Name of your registered component
169
188
  * @param props Props to pass to your component
170
189
  * @param domNodeId
171
190
  * @param hydrate Pass truthy to update server rendered html. Default is falsy
172
- * @returns {virtualDomElement} Reference to your component's backing instance
191
+ * @returns {Root|ReactComponent|ReactElement} Under React 18+: the created React root
192
+ * (see "What is a root?" in https://github.com/reactwg/react-18/discussions/5).
193
+ * Under React 16/17: Reference to your component's backing instance or `null` for stateless components.
173
194
  */
174
195
  render: function (name, props, domNodeId, hydrate) {
175
196
  var componentObj = ComponentRegistry_1.default.get(name);
176
197
  var reactElement = createReactOutput_1.default({ componentObj: componentObj, props: props, domNodeId: domNodeId });
177
- var render = hydrate ? reactHydrate_1.default : reactRender_1.default;
178
- // eslint-disable-next-line react/no-render-return-value
179
- return render(document.getElementById(domNodeId), reactElement);
198
+ return reactHydrateOrRender_1.default(document.getElementById(domNodeId), reactElement, hydrate);
180
199
  },
181
200
  /**
182
201
  * Get the component that you registered
@@ -1,12 +1,14 @@
1
- import type { ReactOnRails as ReactOnRailsType } from './types/index';
1
+ import type { ReactOnRails as ReactOnRailsType, Root } from './types';
2
2
  declare global {
3
3
  interface Window {
4
4
  ReactOnRails: ReactOnRailsType;
5
5
  __REACT_ON_RAILS_EVENT_HANDLERS_RAN_ONCE__?: boolean;
6
+ roots: Root[];
6
7
  }
7
8
  namespace NodeJS {
8
9
  interface Global {
9
10
  ReactOnRails: ReactOnRailsType;
11
+ roots: Root[];
10
12
  }
11
13
  }
12
14
  namespace Turbolinks {
@@ -15,5 +17,7 @@ declare global {
15
17
  }
16
18
  }
17
19
  }
20
+ declare type Context = Window | NodeJS.Global;
18
21
  export declare function reactOnRailsPageLoaded(): void;
19
- export declare function clientStartup(context: Window | NodeJS.Global): void;
22
+ export declare function clientStartup(context: Context): void;
23
+ export {};
@@ -12,8 +12,8 @@ exports.clientStartup = exports.reactOnRailsPageLoaded = void 0;
12
12
  var react_dom_1 = __importDefault(require("react-dom"));
13
13
  var createReactOutput_1 = __importDefault(require("./createReactOutput"));
14
14
  var isServerRenderResult_1 = require("./isServerRenderResult");
15
- var reactHydrate_1 = __importDefault(require("./reactHydrate"));
16
- var reactRender_1 = __importDefault(require("./reactRender"));
15
+ var reactHydrateOrRender_1 = __importDefault(require("./reactHydrateOrRender"));
16
+ var reactApis_1 = require("./reactApis");
17
17
  var REACT_ON_RAILS_STORE_ATTRIBUTE = 'data-js-react-on-rails-store';
18
18
  function findContext() {
19
19
  if (typeof window.ReactOnRails !== 'undefined') {
@@ -50,24 +50,17 @@ function turboInstalled() {
50
50
  function reactOnRailsHtmlElements() {
51
51
  return document.getElementsByClassName('js-react-on-rails-component');
52
52
  }
53
- function forEachReactOnRailsComponentInitialize(fn, railsContext) {
54
- var els = reactOnRailsHtmlElements();
55
- for (var i = 0; i < els.length; i += 1) {
56
- fn(els[i], railsContext);
57
- }
58
- }
59
- function initializeStore(el, railsContext) {
60
- var context = findContext();
61
- var name = el.getAttribute(REACT_ON_RAILS_STORE_ATTRIBUTE) || "";
53
+ function initializeStore(el, context, railsContext) {
54
+ var name = el.getAttribute(REACT_ON_RAILS_STORE_ATTRIBUTE) || '';
62
55
  var props = (el.textContent !== null) ? JSON.parse(el.textContent) : {};
63
56
  var storeGenerator = context.ReactOnRails.getStoreGenerator(name);
64
57
  var store = storeGenerator(props, railsContext);
65
58
  context.ReactOnRails.setStore(name, store);
66
59
  }
67
- function forEachStore(railsContext) {
60
+ function forEachStore(context, railsContext) {
68
61
  var els = document.querySelectorAll("[" + REACT_ON_RAILS_STORE_ATTRIBUTE + "]");
69
62
  for (var i = 0; i < els.length; i += 1) {
70
- initializeStore(els[i], railsContext);
63
+ initializeStore(els[i], context, railsContext);
71
64
  }
72
65
  }
73
66
  function turbolinksVersion5() {
@@ -88,20 +81,18 @@ function delegateToRenderer(componentObj, props, railsContext, domNodeId, trace)
88
81
  return false;
89
82
  }
90
83
  function domNodeIdForEl(el) {
91
- return el.getAttribute('data-dom-id') || "";
84
+ return el.getAttribute('data-dom-id') || '';
92
85
  }
93
86
  /**
94
87
  * Used for client rendering by ReactOnRails. Either calls ReactDOM.hydrate, ReactDOM.render, or
95
88
  * delegates to a renderer registered by the user.
96
- * @param el
97
89
  */
98
- function render(el, railsContext) {
99
- var context = findContext();
90
+ function render(el, context, railsContext) {
100
91
  // This must match lib/react_on_rails/helper.rb
101
- var name = el.getAttribute('data-component-name') || "";
92
+ var name = el.getAttribute('data-component-name') || '';
102
93
  var domNodeId = domNodeIdForEl(el);
103
94
  var props = (el.textContent !== null) ? JSON.parse(el.textContent) : {};
104
- var trace = el.getAttribute('data-trace') === "true";
95
+ var trace = el.getAttribute('data-trace') === 'true';
105
96
  try {
106
97
  var domNode = document.getElementById(domNodeId);
107
98
  if (domNode) {
@@ -123,11 +114,11 @@ function render(el, railsContext) {
123
114
  if (isServerRenderResult_1.isServerRenderHash(reactElementOrRouterResult)) {
124
115
  throw new Error("You returned a server side type of react-router error: " + JSON.stringify(reactElementOrRouterResult) + "\nYou should return a React.Component always for the client side entry point.");
125
116
  }
126
- else if (shouldHydrate) {
127
- reactHydrate_1.default(domNode, reactElementOrRouterResult);
128
- }
129
117
  else {
130
- reactRender_1.default(domNode, reactElementOrRouterResult);
118
+ var rootOrElement = reactHydrateOrRender_1.default(domNode, reactElementOrRouterResult, shouldHydrate);
119
+ if (reactApis_1.supportsRootApi) {
120
+ context.roots.push(rootOrElement);
121
+ }
131
122
  }
132
123
  }
133
124
  }
@@ -137,6 +128,12 @@ function render(el, railsContext) {
137
128
  throw e;
138
129
  }
139
130
  }
131
+ function forEachReactOnRailsComponentRender(context, railsContext) {
132
+ var els = reactOnRailsHtmlElements();
133
+ for (var i = 0; i < els.length; i += 1) {
134
+ render(els[i], context, railsContext);
135
+ }
136
+ }
140
137
  function parseRailsContext() {
141
138
  var el = document.getElementById('js-react-on-rails-context');
142
139
  if (!el) {
@@ -145,7 +142,7 @@ function parseRailsContext() {
145
142
  return null;
146
143
  }
147
144
  if (!el.textContent) {
148
- throw new Error("The HTML element with ID 'js-react-on-rails-context' has no textContent");
145
+ throw new Error('The HTML element with ID \'js-react-on-rails-context\' has no textContent');
149
146
  }
150
147
  return JSON.parse(el.textContent);
151
148
  }
@@ -155,8 +152,12 @@ function reactOnRailsPageLoaded() {
155
152
  // If no react on rails components
156
153
  if (!railsContext)
157
154
  return;
158
- forEachStore(railsContext);
159
- forEachReactOnRailsComponentInitialize(render, railsContext);
155
+ var context = findContext();
156
+ if (reactApis_1.supportsRootApi) {
157
+ context.roots = [];
158
+ }
159
+ forEachStore(context, railsContext);
160
+ forEachReactOnRailsComponentRender(context, railsContext);
160
161
  }
161
162
  exports.reactOnRailsPageLoaded = reactOnRailsPageLoaded;
162
163
  function unmount(el) {
@@ -174,9 +175,21 @@ function unmount(el) {
174
175
  }
175
176
  function reactOnRailsPageUnloaded() {
176
177
  debugTurbolinks('reactOnRailsPageUnloaded');
177
- var els = reactOnRailsHtmlElements();
178
- for (var i = 0; i < els.length; i += 1) {
179
- unmount(els[i]);
178
+ if (reactApis_1.supportsRootApi) {
179
+ var roots = findContext().roots;
180
+ // If no react on rails components
181
+ if (!roots)
182
+ return;
183
+ for (var _i = 0, roots_1 = roots; _i < roots_1.length; _i++) {
184
+ var root = roots_1[_i];
185
+ root.unmount();
186
+ }
187
+ }
188
+ else {
189
+ var els = reactOnRailsHtmlElements();
190
+ for (var i = 0; i < els.length; i += 1) {
191
+ unmount(els[i]);
192
+ }
180
193
  }
181
194
  }
182
195
  function renderInit() {
@@ -0,0 +1 @@
1
+ export declare const supportsRootApi: boolean;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ var _a;
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.supportsRootApi = void 0;
8
+ var react_dom_1 = __importDefault(require("react-dom"));
9
+ var reactMajorVersion = ((_a = react_dom_1.default.version) === null || _a === void 0 ? void 0 : _a.split('.')[0]) || 16;
10
+ // TODO: once we require React 18, we can remove this and inline everything guarded by it.
11
+ // Not the default export because others may be added for future React versions.
12
+ // eslint-disable-next-line import/prefer-default-export
13
+ exports.supportsRootApi = reactMajorVersion >= 18;
@@ -0,0 +1,7 @@
1
+ import type { ReactElement } from 'react';
2
+ import type { RenderReturnType } from './types';
3
+ declare type HydrateOrRenderType = (domNode: Element, reactElement: ReactElement) => RenderReturnType;
4
+ export declare const reactHydrate: HydrateOrRenderType;
5
+ export declare function reactRender(domNode: Element, reactElement: ReactElement): RenderReturnType;
6
+ export default function reactHydrateOrRender(domNode: Element, reactElement: ReactElement, hydrate: boolean): RenderReturnType;
7
+ export {};
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.reactRender = exports.reactHydrate = void 0;
7
+ var react_dom_1 = __importDefault(require("react-dom"));
8
+ var reactApis_1 = require("./reactApis");
9
+ // TODO: once React dependency is updated to >= 18, we can remove this and just
10
+ // import ReactDOM from 'react-dom/client';
11
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
12
+ var reactDomClient;
13
+ if (reactApis_1.supportsRootApi) {
14
+ // This will never throw an exception, but it's the way to tell Webpack the dependency is optional
15
+ // https://github.com/webpack/webpack/issues/339#issuecomment-47739112
16
+ // Unfortunately, it only converts the error to a warning.
17
+ try {
18
+ // eslint-disable-next-line global-require,import/no-unresolved
19
+ reactDomClient = require('react-dom/client');
20
+ }
21
+ catch (e) {
22
+ // We should never get here, but if we do, we'll just use the default ReactDOM
23
+ // and live with the warning.
24
+ reactDomClient = react_dom_1.default;
25
+ }
26
+ }
27
+ exports.reactHydrate = reactApis_1.supportsRootApi ?
28
+ reactDomClient.hydrateRoot :
29
+ function (domNode, reactElement) { return react_dom_1.default.hydrate(reactElement, domNode); };
30
+ function reactRender(domNode, reactElement) {
31
+ if (reactApis_1.supportsRootApi) {
32
+ var root = reactDomClient.createRoot(domNode);
33
+ root.render(reactElement);
34
+ return root;
35
+ }
36
+ // eslint-disable-next-line react/no-render-return-value
37
+ return react_dom_1.default.render(reactElement, domNode);
38
+ }
39
+ exports.reactRender = reactRender;
40
+ function reactHydrateOrRender(domNode, reactElement, hydrate) {
41
+ return hydrate ? exports.reactHydrate(domNode, reactElement) : reactRender(domNode, reactElement);
42
+ }
43
+ exports.default = reactHydrateOrRender;
@@ -1,4 +1,4 @@
1
- import type { ReactElement, Component, FunctionComponent, ComponentClass } from 'react';
1
+ import type { ReactElement, ReactNode, Component, FunctionComponent, ComponentClass } from 'react';
2
2
  declare type Store = any;
3
3
  declare type ReactComponent = FunctionComponent | ComponentClass | string;
4
4
  export interface RailsContext {
@@ -86,6 +86,11 @@ export interface RenderResult {
86
86
  hasErrors: boolean;
87
87
  renderingError?: RenderingError;
88
88
  }
89
+ export interface Root {
90
+ render(children: ReactNode): void;
91
+ unmount(): void;
92
+ }
93
+ export declare type RenderReturnType = void | Element | Component | Root;
89
94
  export interface ReactOnRails {
90
95
  register(components: {
91
96
  [id: string]: ReactComponentOrRenderFunction;
@@ -93,10 +98,11 @@ export interface ReactOnRails {
93
98
  registerStore(stores: {
94
99
  [id: string]: Store;
95
100
  }): void;
96
- getStore(name: string, throwIfMissing: boolean): Store | undefined;
101
+ getStore(name: string, throwIfMissing?: boolean): Store | undefined;
97
102
  setOptions(newOptions: {
98
103
  traceTurbolinks: boolean;
99
104
  }): void;
105
+ reactHydrateOrRender(domNode: Element, reactElement: ReactElement, hydrate: boolean): RenderReturnType;
100
106
  reactOnRailsPageLoaded(): void;
101
107
  authenticityToken(): string | null;
102
108
  authenticityHeaders(otherHeaders: {
@@ -106,7 +112,7 @@ export interface ReactOnRails {
106
112
  getStoreGenerator(name: string): StoreGenerator;
107
113
  setStore(name: string, store: Store): void;
108
114
  clearHydratedStores(): void;
109
- render(name: string, props: Record<string, string>, domNodeId: string, hydrate: boolean): void | Element | Component;
115
+ render(name: string, props: Record<string, string>, domNodeId: string, hydrate: boolean): RenderReturnType;
110
116
  getComponent(name: string): RegisteredComponent;
111
117
  serverRenderReactComponent(options: RenderParams): null | string | Promise<RenderResult>;
112
118
  handleError(options: ErrorOptions): string | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-on-rails",
3
- "version": "13.0.2",
3
+ "version": "13.2.0",
4
4
  "description": "react-on-rails JavaScript for react_on_rails Ruby gem",
5
5
  "main": "node_package/lib/ReactOnRails.js",
6
6
  "directories": {
@@ -15,10 +15,10 @@
15
15
  "@babel/preset-react": "^7.12.10",
16
16
  "@babel/types": "^7.12.10",
17
17
  "@types/jest": "^26.0.18",
18
- "@types/node": "^14.14.11",
19
18
  "@types/react": "^16.9.23",
20
19
  "@types/react-dom": "^16.9.5",
21
20
  "@types/turbolinks": "^5.2.0",
21
+ "@types/webpack-env": "^1.17.0",
22
22
  "@typescript-eslint/eslint-plugin": "^4.10.0",
23
23
  "@typescript-eslint/parser": "^4.10.0",
24
24
  "babelify": "^10.0.0",