react_on_rails_pro 16.2.0.beta.8
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.
- checksums.yaml +7 -0
- data/.controlplane/Dockerfile +49 -0
- data/.controlplane/controlplane.yml +22 -0
- data/.controlplane/gvc.yml +25 -0
- data/.controlplane/postgres.yml +33 -0
- data/.controlplane/rails.yml +49 -0
- data/.controlplane/redis.yml +18 -0
- data/.gitignore +77 -0
- data/.prettierignore +12 -0
- data/.prettierrc +19 -0
- data/.rspec +2 -0
- data/.rubocop.yml +120 -0
- data/.scss-lint.yml +205 -0
- data/CHANGELOG.md +570 -0
- data/CI_SETUP.md +502 -0
- data/CONTRIBUTING.md +376 -0
- data/Dockerfile +63 -0
- data/Gemfile +8 -0
- data/Gemfile.development_dependencies +74 -0
- data/Gemfile.loader +32 -0
- data/Gemfile.lock +527 -0
- data/LICENSE +98 -0
- data/LICENSE_SETUP.md +272 -0
- data/README.md +577 -0
- data/Rakefile +13 -0
- data/app/controllers/react_on_rails_pro/rsc_payload_controller.rb +7 -0
- data/app/helpers/react_on_rails_pro_helper.rb +360 -0
- data/app/views/react_on_rails_pro/rsc_payload.html.erb +1 -0
- data/babel.config.js +4 -0
- data/docs/bundle-caching.md +205 -0
- data/docs/caching.md +234 -0
- data/docs/code-splitting-loadable-components.md +313 -0
- data/docs/code-splitting.md +349 -0
- data/docs/configuration.md +165 -0
- data/docs/contributors-info/onboarding-customers.md +6 -0
- data/docs/contributors-info/releasing.md +40 -0
- data/docs/contributors-info/style.md +33 -0
- data/docs/home-pro.md +146 -0
- data/docs/installation.md +203 -0
- data/docs/js-memory-leaks.md +22 -0
- data/docs/node-renderer/basics.md +92 -0
- data/docs/node-renderer/debugging.md +38 -0
- data/docs/node-renderer/error-reporting-and-tracing.md +160 -0
- data/docs/node-renderer/heroku.md +102 -0
- data/docs/node-renderer/js-configuration.md +91 -0
- data/docs/node-renderer/troubleshooting.md +5 -0
- data/docs/profiling-server-side-rendering-code.md +179 -0
- data/docs/react-server-components/add-streaming-and-interactivity.md +190 -0
- data/docs/react-server-components/create-without-ssr.md +448 -0
- data/docs/react-server-components/glossary.md +102 -0
- data/docs/react-server-components/how-react-server-components-work.md +243 -0
- data/docs/react-server-components/inside-client-components.md +332 -0
- data/docs/react-server-components/purpose-and-benefits.md +243 -0
- data/docs/react-server-components/rendering-flow.md +86 -0
- data/docs/react-server-components/selective-hydration-in-streamed-components.md +75 -0
- data/docs/react-server-components/server-side-rendering.md +72 -0
- data/docs/react-server-components/tutorial.md +19 -0
- data/docs/release-notes/4.0.md +94 -0
- data/docs/release-notes/v4-react-server-components.md +66 -0
- data/docs/ruby-api.md +11 -0
- data/docs/streaming-server-rendering.md +210 -0
- data/docs/troubleshooting.md +24 -0
- data/docs/updating.md +219 -0
- data/eslint.config.mjs +220 -0
- data/lib/react_on_rails_pro/assets_precompile.rb +230 -0
- data/lib/react_on_rails_pro/cache.rb +88 -0
- data/lib/react_on_rails_pro/concerns/rsc_payload_renderer.rb +38 -0
- data/lib/react_on_rails_pro/concerns/stream.rb +103 -0
- data/lib/react_on_rails_pro/configuration.rb +228 -0
- data/lib/react_on_rails_pro/constants.rb +8 -0
- data/lib/react_on_rails_pro/engine.rb +24 -0
- data/lib/react_on_rails_pro/error.rb +14 -0
- data/lib/react_on_rails_pro/license_public_key.rb +30 -0
- data/lib/react_on_rails_pro/license_validator.rb +188 -0
- data/lib/react_on_rails_pro/prepare_node_renderer_bundles.rb +40 -0
- data/lib/react_on_rails_pro/rendering_error.rb +5 -0
- data/lib/react_on_rails_pro/request.rb +318 -0
- data/lib/react_on_rails_pro/routes.rb +13 -0
- data/lib/react_on_rails_pro/server_rendering_js_code.rb +102 -0
- data/lib/react_on_rails_pro/server_rendering_pool/node_rendering_pool.rb +133 -0
- data/lib/react_on_rails_pro/server_rendering_pool/pro_rendering.rb +117 -0
- data/lib/react_on_rails_pro/stream_cache.rb +61 -0
- data/lib/react_on_rails_pro/stream_request.rb +170 -0
- data/lib/react_on_rails_pro/utils.rb +222 -0
- data/lib/react_on_rails_pro/v8_log_processor.rb +50 -0
- data/lib/react_on_rails_pro/version.rb +6 -0
- data/lib/react_on_rails_pro.rb +23 -0
- data/package-scripts.yml +109 -0
- data/package.json +159 -0
- data/rakelib/dummy_apps.rake +22 -0
- data/rakelib/lint.rake +32 -0
- data/rakelib/public_key_management.rake +155 -0
- data/rakelib/rbs.rake +47 -0
- data/rakelib/run_rspec.rake +81 -0
- data/rakelib/task_helpers.rb +45 -0
- data/rakelib/yard.rake +20 -0
- data/react_on_rails_pro.gemspec +47 -0
- data/readme-gen-docs.md +1 -0
- data/script/bootstrap +33 -0
- data/script/preinstall.js +31 -0
- data/script/setup +23 -0
- data/script/test +38 -0
- data/sig/react_on_rails_pro/cache.rbs +13 -0
- data/sig/react_on_rails_pro/configuration.rbs +100 -0
- data/sig/react_on_rails_pro/error.rbs +4 -0
- data/sig/react_on_rails_pro/utils.rbs +7 -0
- data/sig/react_on_rails_pro.rbs +5 -0
- data/yarn.lock +7599 -0
- metadata +319 -0
data/docs/home-pro.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# React on Rails Pro
|
|
2
|
+
|
|
3
|
+
Node rendering and caching performance enhancements for [React on Rails](https://github.com/shakacode/react_on_rails). Now supports React 18 with updates to React on Rails! Check the [React on Rails CHANGELOG.md](https://github.com/shakacode/react_on_rails/blob/master/CHANGELOG.md) for details and the updates to the [loadable-components instructions](https://github.com/shakacode/react_on_rails_pro/blob/master/docs/code-splitting-loadable-components.md).
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
The best way to see how React on Rails Pro works is to install this repo locally and take a look at
|
|
7
|
+
the example application:
|
|
8
|
+
|
|
9
|
+
[spec/dummy](https://github.com/shakacode/react_on_rails/blob/master/spec/dummy/README.md)
|
|
10
|
+
1. Uses a @rails/webpacker standard configuration.
|
|
11
|
+
1. Has pages that demonstrate:
|
|
12
|
+
1. caching
|
|
13
|
+
2. loadable-components
|
|
14
|
+
1. Has all the basic react_on_rails specs that run against the Node Renderer
|
|
15
|
+
1. Demonstrates using HMR and loadable-components with almost the same example that is present in [loadable-components for SSR](https://github.com/gregberge/loadable-components/tree/main/examples/server-side-rendering)
|
|
16
|
+
|
|
17
|
+
See the README.md in those sample apps for more details.
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
### 🚀 Next-Gen Server Rendering: Streaming with React 18's Latest APIs
|
|
22
|
+
React on Rails Pro supports React 18's Streaming Server-Side Rendering, allowing you to progressively render and stream HTML content to the client. This enables faster page loads and better user experience.
|
|
23
|
+
|
|
24
|
+
See [docs/streaming-server-rendering](./streaming-server-rendering.md) for more details.
|
|
25
|
+
|
|
26
|
+
### Caching
|
|
27
|
+
Caching of SSR is critical for achieving optimum performance.
|
|
28
|
+
|
|
29
|
+
* **Fragment Caching**: for `react_component` and `react_component_hash`, including lazy evaluation of props.
|
|
30
|
+
* **Prerender Caching**: Server rendering JavaScript evaluation is cached if `prerender_caching` is turned on in your Rails config. This applies to all JavaScript evaluation methods.
|
|
31
|
+
|
|
32
|
+
See [docs/caching](./caching.md) for more details.
|
|
33
|
+
|
|
34
|
+
### Clearing of Global State
|
|
35
|
+
Suppose you detect that some library used in server-rendering is leaking state between calls to server render. In that case, you can set the `config.ssr_pre_hook_js` in your `config/initializers/react_on_rails_pro.rb` to run some JavaScript to clear the globally leaked state at the beginning of each call to server render.
|
|
36
|
+
|
|
37
|
+
For more details, see [Rails Configuration](https://github.com/shakacode/react_on_rails/blob/master/docs/configuration.md).
|
|
38
|
+
|
|
39
|
+
### React On Rails Pro Node Renderer
|
|
40
|
+
The "React on Rails Pro Node Renderer" provides more efficient server rendering on a standalone Node JS server.
|
|
41
|
+
See the [Node Renderer Docs](./node-renderer/basics.md).
|
|
42
|
+
|
|
43
|
+
### Bundle Caching
|
|
44
|
+
Don't wait for the same webpack bundles to be built over and over. See the [bundle-caching docs](./bundle-caching.md).
|
|
45
|
+
|
|
46
|
+
## Other Utility Methods
|
|
47
|
+
See the [Ruby API](./ruby-api.md).
|
|
48
|
+
|
|
49
|
+
## References
|
|
50
|
+
|
|
51
|
+
* [Installation](./installation.md)
|
|
52
|
+
* [Streaming Server Rendering](./streaming-server-rendering.md)
|
|
53
|
+
* [Caching](./caching.md)
|
|
54
|
+
* [Rails Configuration](./configuration.md)
|
|
55
|
+
* [Node Renderer Docs](./node-renderer/basics.md)
|
|
56
|
+
|
|
57
|
+
# Features
|
|
58
|
+
## Code Splitting
|
|
59
|
+
|
|
60
|
+
From [The Cost of JavaScript in 2018](https://medium.com/@addyosmani/the-cost-of-javascript-in-2018-7d8950fbb5d4):
|
|
61
|
+
|
|
62
|
+
> To stay fast, only load JavaScript needed for the current page. Prioritize what a user will need and lazy-load the rest with code-splitting. This gives you the best chance at loading and getting interactive fast. Stacks with route-based code-splitting by default are game-changers.
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
## Caching
|
|
66
|
+
### Server Rendering
|
|
67
|
+
Server rendering of JavaScript evaluation is cached if `prerender_caching` is turned on in your Rails config. This applies to all JavaScript evaluation methods, including ExecJS and the Node VM Renderer.
|
|
68
|
+
|
|
69
|
+
### Pro: Fragment Caching
|
|
70
|
+
|
|
71
|
+
Fragment caching is a [React on Rails Pro](https://www.shakacode.com/react-on-rails-pro/) feature. Fragment caching is a **HUGE** performance booster for your apps. Use the `cached_react_component` and `cached_react_component_hash`. The API is the same as `react_component` and `react_component_hash`, but for 2 differences:
|
|
72
|
+
|
|
73
|
+
1. The `cache_key` takes the same parameters as any Rails `cache` view helper.
|
|
74
|
+
1. The **props** are passed via a block so that evaluation of the props is not done unless the cache is broken. Suppose you put your props calculation into some method called `some_slow_method_that_returns_props`:
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
<%= cached_react_component("App", cache_key: [@user, @post], prerender: true) do
|
|
78
|
+
some_slow_method_that_returns_props
|
|
79
|
+
end %>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Such fragment caching saves CPU work for your web server and greatly reduces the request time. It completely skips the evaluation costs of:
|
|
83
|
+
|
|
84
|
+
1. Database calls to compute the props.
|
|
85
|
+
2. Serialization the props values hash into a JSON string for evaluating JavaScript to server render.
|
|
86
|
+
3. Costs associated with evaluating JavaScript from your Ruby code.
|
|
87
|
+
4. Creating the HTML string containing the props and the server-rendered JavaScript code.
|
|
88
|
+
|
|
89
|
+
Note, even without server rendering (without step 3 above), fragment caching is still effective.
|
|
90
|
+
See [Caching](./caching.md) for more additional details.
|
|
91
|
+
|
|
92
|
+
## React On Rails Pro Node React Render
|
|
93
|
+
The "React on Rails Pro Node React Renderer" provides more efficient React Server Side Rendering on a standalone Node JS server.
|
|
94
|
+
|
|
95
|
+
### Overall Management Memory and CPU on both the Rendering and Ruby Servers
|
|
96
|
+
A separate Node rendering server is easier to manage in terms of monitoring memory and CPU performance, allocating dynos, etc. This also makes it easier to manage the ruby servers, as you no longer have to consider the impact of starting an embedded V8. Thus, you can never hang your Ruby servers due to JavaScript memory leaks.
|
|
97
|
+
|
|
98
|
+
### Proper Node Tooling
|
|
99
|
+
A disadvantage of Ruby embedded JavaScript (ExecJS) is that it precludes the use of standard Node tooling for doing things like profiling and tracking down memory leaks. With the renderer on a separate Node.js server, we were able to use node-memwatch (https://github.com/marcominetti/node-memwatch) to find few memory leaks in the Egghead React code.
|
|
100
|
+
|
|
101
|
+
### Caching of React Rendering
|
|
102
|
+
To limit the load on the renderer server or embedded ExecJS, caching of React rendering requests can be enabled by a config setting. Because current React rendering requests are idempotent (same value regardless of calls), caching should be feasible for all server rendering calls. The current renderer does not allow any asynchronous calls to fetch data. The rendering request includes all data for rendering.
|
|
103
|
+
|
|
104
|
+
### Rolling Restart of Node Workers
|
|
105
|
+
Due to poor performance and crashes due to memory leaks, the rolling restart of node workers was thus added as an option to the core rendering product. This option is cheap insurance against the renderer getting too slow from a memory leak due to a bug in some newly deployed JavaScript code.
|
|
106
|
+
|
|
107
|
+
### Docs
|
|
108
|
+
See the [Node React Render Docs](./node-renderer/basics.md).
|
|
109
|
+
|
|
110
|
+
## Other Utility Methods
|
|
111
|
+
See the [Ruby API](./ruby-api.md).
|
|
112
|
+
|
|
113
|
+
# Testimonials
|
|
114
|
+
|
|
115
|
+
"Do you want your app to randomly crash sometimes in hard to predict ways? Then ExecJS is perfect for you"
|
|
116
|
+
Anybody who regularly hits six-digit request numbers a day is going to be in for a bad time." Pete Keen, https://egghead.io
|
|
117
|
+
|
|
118
|
+
For details, see [Egghead React on Rails Pro Deployment Highlights](https://github.com/shakacode/react_on_rails/wiki/Egghead-React-on-Rails-Pro-Deployment-Highlights/).
|
|
119
|
+
|
|
120
|
+
# FAQ
|
|
121
|
+
|
|
122
|
+
## Why should I use React on Rails Pro if ExecJS seems to work?
|
|
123
|
+
|
|
124
|
+
Caching is extremely useful to any server rendering you're doing, with or without ExecJS.
|
|
125
|
+
|
|
126
|
+
React on Rails pro support caching at 2 levels:
|
|
127
|
+
1. Caching of rendering request to ExecJS (or the Node renderer). This avoids extra calls to ExecJS.
|
|
128
|
+
2. Fragment caching of server rendering. This avoids even the calculations of prop values from the database and the cost of converting the props to a string (lots of CPU there)
|
|
129
|
+
|
|
130
|
+
By doing such caching, you will take a CPU load off your Ruby server as well as improving response time. And this is with virtually no code changes on your part.
|
|
131
|
+
|
|
132
|
+
# Support React on Rails development
|
|
133
|
+
|
|
134
|
+
Support React on Rails development [by becoming a Github sponsor](https://github.com/sponsors/shakacode) and get these benefits:
|
|
135
|
+
|
|
136
|
+
1. 1-hour per month of support via Slack, PR reviews, and Zoom for React on Rails,
|
|
137
|
+
React-Rails, Shakapacker, rails/webpacker, ReScript (ReasonML), TypeScript, Rust, etc.
|
|
138
|
+
2. React on Rails Pro Software that extends React on Rails with Node server rendering,
|
|
139
|
+
fragment caching, code-splitting, and other performance enhancements for React on Rails.
|
|
140
|
+
|
|
141
|
+
For more info, email [justin@shakacode.com](mailto:justin@shakacode.com).
|
|
142
|
+
|
|
143
|
+
# References
|
|
144
|
+
|
|
145
|
+
* [Caching](./caching.md)
|
|
146
|
+
* [Rails Configuration](./configuration.md)
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# Installation
|
|
2
|
+
|
|
3
|
+
React on Rails Pro packages are published publicly on npmjs.org and RubyGems.org. Installation requires a valid **license token** for runtime validation. Contact [justin@shakacode.com](mailto:justin@shakacode.com) to purchase a license.
|
|
4
|
+
|
|
5
|
+
**Upgrading from GitHub Packages?** See the [Upgrading Guide](./updating.md) for migration instructions.
|
|
6
|
+
|
|
7
|
+
Check the [CHANGELOG](https://github.com/shakacode/react_on_rails/blob/master/CHANGELOG.md) to see what version you want.
|
|
8
|
+
|
|
9
|
+
## Version Format
|
|
10
|
+
|
|
11
|
+
For the below docs, find the desired `<version>` in the CHANGELOG. Note that for pre-release versions:
|
|
12
|
+
|
|
13
|
+
- Gems use all periods: `16.2.0.beta.1`
|
|
14
|
+
- NPM packages use dashes: `16.2.0-beta.1`
|
|
15
|
+
|
|
16
|
+
# Ruby Gem Installation
|
|
17
|
+
|
|
18
|
+
## Prerequisites
|
|
19
|
+
|
|
20
|
+
Ensure your **Rails** app is using the **react_on_rails** gem, version 16.0.0 or higher.
|
|
21
|
+
|
|
22
|
+
## Install react_on_rails_pro Gem
|
|
23
|
+
|
|
24
|
+
Add the `react_on_rails_pro` gem to your **Gemfile**:
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
gem "react_on_rails_pro", "~> 16.2"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Then run:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
bundle install
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Or install directly:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
gem install react_on_rails_pro --version "<version>"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## License Configuration
|
|
43
|
+
|
|
44
|
+
Set your license token as an environment variable:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
export REACT_ON_RAILS_PRO_LICENSE="your-license-token-here"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Or configure it in your Rails initializer (not recommended for production):
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
# config/initializers/react_on_rails_pro.rb
|
|
54
|
+
ReactOnRailsPro.configure do |config|
|
|
55
|
+
config.license_token = ENV["REACT_ON_RAILS_PRO_LICENSE"]
|
|
56
|
+
end
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
⚠️ **Security Warning**: Never commit your license token to version control. Always use environment variables or secure secret management systems (like Rails credentials, Heroku config vars, AWS Secrets Manager, etc.).
|
|
60
|
+
|
|
61
|
+
## Rails Configuration
|
|
62
|
+
|
|
63
|
+
You don't need to create an initializer if you are satisfied with the defaults as described in [Configuration](./configuration.md).
|
|
64
|
+
|
|
65
|
+
For basic setup:
|
|
66
|
+
|
|
67
|
+
```ruby
|
|
68
|
+
# config/initializers/react_on_rails_pro.rb
|
|
69
|
+
ReactOnRailsPro.configure do |config|
|
|
70
|
+
# Your configuration here
|
|
71
|
+
# See docs/configuration.md for all options
|
|
72
|
+
end
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
# Node Package Installation
|
|
76
|
+
|
|
77
|
+
**Note:** You only need to install the Node Package if you are using the standalone node renderer (`NodeRenderer`). If you're using `ExecJS` (the default), skip this section.
|
|
78
|
+
|
|
79
|
+
## Install react-on-rails-pro-node-renderer
|
|
80
|
+
|
|
81
|
+
### Using npm:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
npm install react-on-rails-pro-node-renderer
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Using yarn:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
yarn add react-on-rails-pro-node-renderer
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Add to package.json:
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"dependencies": {
|
|
98
|
+
"react-on-rails-pro-node-renderer": "^16.2.0"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Node Renderer Setup
|
|
104
|
+
|
|
105
|
+
Create a JavaScript file to configure and launch the node renderer, for example `react-on-rails-pro-node-renderer.js`:
|
|
106
|
+
|
|
107
|
+
```js
|
|
108
|
+
const path = require('path');
|
|
109
|
+
const { reactOnRailsProNodeRenderer } = require('react-on-rails-pro-node-renderer');
|
|
110
|
+
|
|
111
|
+
const env = process.env;
|
|
112
|
+
|
|
113
|
+
const config = {
|
|
114
|
+
serverBundleCachePath: path.resolve(__dirname, '../.node-renderer-bundles'),
|
|
115
|
+
|
|
116
|
+
// Listen at RENDERER_PORT env value or default port 3800
|
|
117
|
+
logLevel: env.RENDERER_LOG_LEVEL || 'debug', // show all logs
|
|
118
|
+
|
|
119
|
+
// Password for Rails <-> Node renderer communication
|
|
120
|
+
// See value in /config/initializers/react_on_rails_pro.rb
|
|
121
|
+
password: env.RENDERER_PASSWORD || 'changeme',
|
|
122
|
+
|
|
123
|
+
port: env.RENDERER_PORT || 3800,
|
|
124
|
+
|
|
125
|
+
// supportModules should be set to true to allow the server-bundle code to
|
|
126
|
+
// see require, exports, etc. (`false` is like the ExecJS behavior)
|
|
127
|
+
// This option is required to equal `true` in order to use loadable components
|
|
128
|
+
supportModules: true,
|
|
129
|
+
|
|
130
|
+
// workersCount defaults to the number of CPUs minus 1
|
|
131
|
+
workersCount: Number(env.NODE_RENDERER_CONCURRENCY || 3),
|
|
132
|
+
|
|
133
|
+
// Optional: Automatic worker restarting (for memory leak mitigation)
|
|
134
|
+
// allWorkersRestartInterval: 15, // minutes between restarting all workers
|
|
135
|
+
// delayBetweenIndividualWorkerRestarts: 2, // minutes between each worker restart
|
|
136
|
+
// gracefulWorkerRestartTimeout: undefined, // timeout for graceful worker restart; forces restart if worker stuck
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// Renderer detects a total number of CPUs on virtual hostings like Heroku
|
|
140
|
+
// or CircleCI instead of CPUs number allocated for current container. This
|
|
141
|
+
// results in spawning many workers while only 1-2 of them really needed.
|
|
142
|
+
if (env.CI) {
|
|
143
|
+
config.workersCount = 2;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
reactOnRailsProNodeRenderer(config);
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Add a script to your `package.json`:
|
|
150
|
+
|
|
151
|
+
```json
|
|
152
|
+
{
|
|
153
|
+
"scripts": {
|
|
154
|
+
"node-renderer": "node ./react-on-rails-pro-node-renderer.js"
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Start the renderer:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
npm run node-renderer
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Rails Configuration for Node Renderer
|
|
166
|
+
|
|
167
|
+
Configure Rails to use the remote node renderer:
|
|
168
|
+
|
|
169
|
+
```ruby
|
|
170
|
+
# config/initializers/react_on_rails_pro.rb
|
|
171
|
+
ReactOnRailsPro.configure do |config|
|
|
172
|
+
config.server_renderer = "NodeRenderer"
|
|
173
|
+
|
|
174
|
+
# Configure renderer connection
|
|
175
|
+
config.renderer_url = ENV["REACT_RENDERER_URL"] || "http://localhost:3800"
|
|
176
|
+
config.renderer_password = ENV["RENDERER_PASSWORD"] || "changeme"
|
|
177
|
+
|
|
178
|
+
# Enable prerender caching (recommended)
|
|
179
|
+
config.prerender_caching = true
|
|
180
|
+
end
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Configuration Options
|
|
184
|
+
|
|
185
|
+
See [Rails Configuration Options](./configuration.md) for all available settings.
|
|
186
|
+
|
|
187
|
+
Pay attention to:
|
|
188
|
+
|
|
189
|
+
- `config.server_renderer = "NodeRenderer"` - Required to use node renderer
|
|
190
|
+
- `config.renderer_url` - URL where your node renderer is running
|
|
191
|
+
- `config.renderer_password` - Shared secret for authentication
|
|
192
|
+
- `config.prerender_caching` - Enable caching (recommended)
|
|
193
|
+
|
|
194
|
+
## Webpack Configuration
|
|
195
|
+
|
|
196
|
+
Set your server bundle webpack configuration to use a target of `node` per the [Webpack docs](https://webpack.js.org/concepts/targets/#usage).
|
|
197
|
+
|
|
198
|
+
## Additional Documentation
|
|
199
|
+
|
|
200
|
+
- [Node Renderer Basics](./node-renderer/basics.md)
|
|
201
|
+
- [Node Renderer JavaScript Configuration](./node-renderer/js-configuration.md)
|
|
202
|
+
- [Rails Configuration Options](./configuration.md)
|
|
203
|
+
- [Error Reporting and Tracing](./node-renderer/error-reporting-and-tracing.md)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# JS Memory Leaks
|
|
2
|
+
|
|
3
|
+
## Finding Memory Leaks
|
|
4
|
+
For memory leaks, see [node-memwatch](https://github.com/marcominetti/node-memwatch). Use the `—inspect` flag to make and compare heap snapshots.
|
|
5
|
+
|
|
6
|
+
## Causes of Memory Leaks
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Mobx (mobx-react)
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
import { useStaticRendering } from "mobx-react";
|
|
13
|
+
|
|
14
|
+
const App = (props, railsContext) => {
|
|
15
|
+
const { location, serverSide } = railsContext;
|
|
16
|
+
const context = {};
|
|
17
|
+
|
|
18
|
+
useStaticRendering(true);
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
* See details here: [Mobx site](https://github.com/mobxjs/mobx-react#server-side-rendering-with-usestaticrendering)
|
|
22
|
+
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Requirements
|
|
2
|
+
|
|
3
|
+
- You must use React on Rails v11.0.7 or higher.
|
|
4
|
+
|
|
5
|
+
# Install the Gem and the Node Module
|
|
6
|
+
|
|
7
|
+
See [Installation](../installation.md).
|
|
8
|
+
|
|
9
|
+
# Setup Node Renderer Server
|
|
10
|
+
|
|
11
|
+
**node-renderer** is a standalone Node application to serve React SSR requests from a **Rails** client. You don't need any **Ruby** code to setup and launch it. You can configure with the command line or with a launch file.
|
|
12
|
+
|
|
13
|
+
## Simple Command Line for node-renderer
|
|
14
|
+
|
|
15
|
+
1. ENV values for the default config are (See [JS Configuration](./js-configuration.md) for more details):
|
|
16
|
+
- `RENDERER_PORT`
|
|
17
|
+
- `RENDERER_LOG_LEVEL`
|
|
18
|
+
- `RENDERER_BUNDLE_PATH`
|
|
19
|
+
- `RENDERER_WORKERS_COUNT`
|
|
20
|
+
- `RENDERER_PASSWORD`
|
|
21
|
+
- `RENDERER_ALL_WORKERS_RESTART_INTERVAL`
|
|
22
|
+
- `RENDERER_DELAY_BETWEEN_INDIVIDUAL_WORKER_RESTARTS`
|
|
23
|
+
- `RENDERER_SUPPORT_MODULES`
|
|
24
|
+
2. Configure ENV values and run the command. Note, you can set port with args `-p <PORT>`. For example, assuming node-renderer is in your path:
|
|
25
|
+
```
|
|
26
|
+
RENDERER_BUNDLE_PATH=/app/.node-renderer-bundles node-renderer
|
|
27
|
+
```
|
|
28
|
+
3. You can use a command line argument of `-p SOME_PORT` to override any ENV value for the PORT.
|
|
29
|
+
|
|
30
|
+
## JavaScript Configuration File
|
|
31
|
+
|
|
32
|
+
For the most control over the setup, create a JavaScript file to start the NodeRenderer.
|
|
33
|
+
|
|
34
|
+
1. Create some project directory, let's say `renderer-app`:
|
|
35
|
+
```sh
|
|
36
|
+
mkdir renderer-app
|
|
37
|
+
cd renderer-app
|
|
38
|
+
```
|
|
39
|
+
2. Make sure you have **Node.js** version **14** or higher and **Yarn** installed.
|
|
40
|
+
3. Init node application and install the `react-on-rails-pro-node-renderer` package.
|
|
41
|
+
```sh
|
|
42
|
+
yarn init
|
|
43
|
+
yarn add react-on-rails-pro-node-renderer
|
|
44
|
+
```
|
|
45
|
+
4. Configure a JavaScript file that will launch the rendering server per the docs in [Node Renderer JavaScript Configuration](./js-configuration.md). For example, create a file `node-renderer.js`. Here is a simple example that uses all the defaults except for serverBundleCachePath:
|
|
46
|
+
|
|
47
|
+
```javascript
|
|
48
|
+
import path from 'path';
|
|
49
|
+
import reactOnRailsProNodeRenderer from 'react-on-rails-pro-node-renderer';
|
|
50
|
+
|
|
51
|
+
const config = {
|
|
52
|
+
serverBundleCachePath: path.resolve(__dirname, '../.node-renderer-bundles'),
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
reactOnRailsProNodeRenderer(config);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
5. Now you can launch your renderer server with `node node-renderer.js`. You will probably add a script to your `package.json`.
|
|
59
|
+
6. You can use a command line argument of `-p SOME_PORT` to override any configured or ENV value for the port.
|
|
60
|
+
|
|
61
|
+
# Setup Rails Application
|
|
62
|
+
|
|
63
|
+
Create `config/initializers/react_on_rails_pro.rb` and configure the **renderer server**. See configuration values in [Configuration](../configuration.md). Pay attention to:
|
|
64
|
+
|
|
65
|
+
1. Set `config.server_renderer = "NodeRenderer"`
|
|
66
|
+
2. Leave the default of `config.prerender_caching = true` and ensure your Rails cache is properly configured to handle the additional cache load.
|
|
67
|
+
3. Configure values beginning with `renderer_`
|
|
68
|
+
4. Use ENV values for values like `renderer_url` so that your deployed server is properly configured. If the ENV value is unset, the default for the renderer_url is `localhost:3800`.
|
|
69
|
+
5. Here's a tiny example using mostly defaults:
|
|
70
|
+
|
|
71
|
+
```ruby
|
|
72
|
+
ReactOnRailsPro.configure do |config|
|
|
73
|
+
config.server_renderer = "NodeRenderer"
|
|
74
|
+
|
|
75
|
+
# when this ENV value is not defined, the local server at localhost:3800 is used
|
|
76
|
+
config.renderer_url = ENV["REACT_RENDERER_URL"]
|
|
77
|
+
end
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Troublshooting
|
|
81
|
+
|
|
82
|
+
- See [JS Memory Leaks](../js-memory-leaks.md).
|
|
83
|
+
|
|
84
|
+
## Upgrading
|
|
85
|
+
|
|
86
|
+
The NodeRenderer has a protocol version on both the Rails and Node sides. If the Rails server sends a protocol version that does not match the Node side, an error is returned. Ideally, you want to keep both the Rails and Node sides at the same version.
|
|
87
|
+
|
|
88
|
+
## References
|
|
89
|
+
|
|
90
|
+
- [Installation](../installation.md).
|
|
91
|
+
- [Rails Options for node-renderer](../configuration.md)
|
|
92
|
+
- [JS Options for node-renderer](./js-configuration.md)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
Because the renderer communicates over a port to the server, you can start a renderer instance in this repo and hack on it.
|
|
2
|
+
|
|
3
|
+
# Yalc vs Yarn Link
|
|
4
|
+
The project is setup to use [yalc](https://github.com/whitecolor/yalc). This means that at the top level
|
|
5
|
+
directory, `yalc publish` will send the node package files to the global yalc store. Running `yarn` in the
|
|
6
|
+
`/spec/dummy/client` directory will copy the files from the global yalc store over to the local `node_modules`
|
|
7
|
+
directory.
|
|
8
|
+
|
|
9
|
+
# Debugging the Node Renderer
|
|
10
|
+
1. cd to the top level of the project.
|
|
11
|
+
1. `yarn` to install any libraries.
|
|
12
|
+
1. To compile renderer files on changes, open console and run `yarn build:dev`.
|
|
13
|
+
1. Open another console tab and run `RENDERER_LOG_LEVEL=debug yarn start`
|
|
14
|
+
1. Reload the browser page that causes the renderer issue. You can then update the JS code, and restart the `yarn start` to run the renderer with the new code.
|
|
15
|
+
1. Be sure to restart the rails server if you change any ruby code in loaded gems.
|
|
16
|
+
1. Note, the default setup for spec/dummy to reference the pro renderer is to use yalc, which may or may not be using a link, which means that you have to re-run yarn to get the files updated when changing the renderer.
|
|
17
|
+
1. Check out the top level nps task `nps renderer.debug` and `spec/dummy/package.json` which has script `"node-renderer-debug"`.
|
|
18
|
+
|
|
19
|
+
## Debugging using the Node debugger
|
|
20
|
+
1. See [this article](https://github.com/shakacode/react_on_rails/issues/1196) on setting up the debugger.
|
|
21
|
+
|
|
22
|
+
## Debugging Jest tests
|
|
23
|
+
1. See [the Jest documentation](https://jestjs.io/docs/troubleshooting) for overall guidance.
|
|
24
|
+
2. For RubyMine, see [the RubyMine documentation](https://www.jetbrains.com/help/ruby/running-unit-tests-on-jest.html) for the current information. The original [Testing With Jest in WebStorm](https://blog.jetbrains.com/webstorm/2018/10/testing-with-jest-in-webstorm/) post can be useful as well.
|
|
25
|
+
|
|
26
|
+
# Debugging the Ruby gem
|
|
27
|
+
|
|
28
|
+
Open the gemfile in the problematic app.
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
gem "react_on_rails_pro", path: "../../../shakacode/react-on-rails/react_on_rails_pro"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Optionally, also specify react_on_rails to be local:
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
gem "react_on_rails", path: "../../../shakacode/react-on-rails/react_on_rails"
|
|
38
|
+
```
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Error Reporting and Tracing
|
|
2
|
+
|
|
3
|
+
[Please see this documentation for versions before 4.0.0](https://github.com/shakacode/react_on_rails_pro/blob/ac2afba93c672f49f16bf967d6accbed0fda386e/docs/node-renderer/error-reporting-and-tracing.md).
|
|
4
|
+
|
|
5
|
+
To integrate with error reporting and tracing services,
|
|
6
|
+
you need a custom configuration script as described in [Node Renderer JavaScript Configuration](./js-configuration.md).
|
|
7
|
+
|
|
8
|
+
It should initialize the services according to your requirements and then enable integrations.
|
|
9
|
+
|
|
10
|
+
## Sentry
|
|
11
|
+
|
|
12
|
+
1. [Set up Sentry](https://docs.sentry.io/platforms/javascript/guides/fastify/). You may create an `instrument.js` file as described there and require it in your configuration script, but it is simpler to call `Sentry.init` directly in your configuration script.
|
|
13
|
+
2. Call `Sentry.init` with the desired options according to [the documentation](https://docs.sentry.io/platforms/javascript/guides/fastify/configuration/).
|
|
14
|
+
3. Then load the integration:
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
require('react-on-rails-pro-node-renderer/integrations/sentry').init();
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- Use `react-on-rails-pro-node-renderer/integrations/sentry6` instead of `.../sentry` for versions of Sentry SDK older than 7.63.0.
|
|
21
|
+
- For Sentry SDK v8+ you can use `.init({ fastify: true })` to capture additional Fastify-related information.
|
|
22
|
+
|
|
23
|
+
### Sentry Tracing
|
|
24
|
+
|
|
25
|
+
To enable Sentry Tracing:
|
|
26
|
+
1. Include `enableTracing`, `tracesSampleRate`, or `tracesSampler` in your `Sentry.init` call. See [the Sentry documentation](https://docs.sentry.io/platforms/javascript/tracing/) for details, but ignore `Sentry.browserTracingIntegration()`.
|
|
27
|
+
2. Depending on your Sentry SDK version:
|
|
28
|
+
- if it is older than 7.63.0, install `@sentry/tracing` as well as `@sentry/node` (with the same exact version) and pass `integrations: [new Sentry.Integrations.Http({ tracing: true })]` to `Sentry.init`.
|
|
29
|
+
- for newer v7.x.y, pass `integrations: Sentry.autoDiscoverNodePerformanceMonitoringIntegrations()`.
|
|
30
|
+
- for v8.x.y, Node HTTP tracing is included by default.
|
|
31
|
+
3. Pass `{ tracing: true }` to the `init` function of the integration. It can be combined with `fastify: true`.
|
|
32
|
+
|
|
33
|
+
### Sentry Profiling
|
|
34
|
+
|
|
35
|
+
[Follow this documentation](https://docs.sentry.io/platforms/javascript/guides/fastify/profiling/).
|
|
36
|
+
|
|
37
|
+
## Honeybadger
|
|
38
|
+
|
|
39
|
+
1. [Set up Honeybadger](https://docs.honeybadger.io/lib/javascript/integration/node/). Call `Honeybadger.configure` with the desired options in the configuration script.
|
|
40
|
+
2. Then load the integration:
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
require('react-on-rails-pro-node-renderer/integrations/honeybadger').init();
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Use `init({ fastify: true })` to capture additional Fastify-related information.
|
|
47
|
+
|
|
48
|
+
## Other services
|
|
49
|
+
You can create your own integrations in the same way as the provided ones.
|
|
50
|
+
If you have access to the React on Rails Pro repository,
|
|
51
|
+
you can use [their implementations](https://github.com/shakacode/react_on_rails_pro/tree/master/packages/node-renderer/src/integrations) as examples.
|
|
52
|
+
Import these functions from `react-on-rails-pro-node-renderer/integrations/api`:
|
|
53
|
+
|
|
54
|
+
### Error reporting services
|
|
55
|
+
|
|
56
|
+
- `addErrorNotifier` and `addMessageNotifier` tell React on Rails Pro how to report errors to your chosen service.
|
|
57
|
+
- Use `addNotifier` if the service uses the same reporting function for both JavaScript `Error`s and string messages.
|
|
58
|
+
|
|
59
|
+
For example, integrating with BugSnag can be as simple as
|
|
60
|
+
```js
|
|
61
|
+
const Bugsnag = require('@bugsnag/js');
|
|
62
|
+
const { addNotifier } = require('react-on-rails-pro-node-renderer/integrations/api');
|
|
63
|
+
|
|
64
|
+
Bugsnag.start({ /* your options */ });
|
|
65
|
+
|
|
66
|
+
addNotifier((msg) => { Bugsnag.notify(msg); });
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Tracing services
|
|
70
|
+
|
|
71
|
+
- `setupTracing` takes an object with two properties:
|
|
72
|
+
- `executor` should wrap an async function in the service's unit of work.
|
|
73
|
+
- Since the only units of work we currently track are rendering requests, the options to start them are specified in `startSsrRequestOptions`.
|
|
74
|
+
|
|
75
|
+
To track requests as [sessions](https://docs.bugsnag.com/platforms/javascript/capturing-sessions/#startsession) in BugSnag 8.x+,
|
|
76
|
+
the above example becomes
|
|
77
|
+
```js
|
|
78
|
+
const Bugsnag = require('@bugsnag/js');
|
|
79
|
+
const { addNotifier, setupTracing } = require('react-on-rails-pro-node-renderer/integrations/api');
|
|
80
|
+
|
|
81
|
+
Bugsnag.start({ /* your options */ });
|
|
82
|
+
|
|
83
|
+
addNotifier((msg) => {
|
|
84
|
+
Bugsnag.notify(msg);
|
|
85
|
+
});
|
|
86
|
+
setupTracing({
|
|
87
|
+
executor: async (fn) => {
|
|
88
|
+
Bugsnag.startSession();
|
|
89
|
+
try {
|
|
90
|
+
return await fn();
|
|
91
|
+
} finally {
|
|
92
|
+
Bugsnag.pauseSession();
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
You can optionally add `startSsrRequestOptions` property to capture the request data:
|
|
99
|
+
|
|
100
|
+
```js
|
|
101
|
+
setupTracing({
|
|
102
|
+
startSsrRequestOptions: ({ renderingRequest }) => ({ bugsnag: { renderingRequest } }),
|
|
103
|
+
executor: async (fn, { bugsnag }) => {
|
|
104
|
+
Bugsnag.startSession();
|
|
105
|
+
// bugsnag will look like { renderingRequest }
|
|
106
|
+
Bugsnag.leaveBreadcrumb('SSR request', bugsnag, 'request');
|
|
107
|
+
try {
|
|
108
|
+
return await fn();
|
|
109
|
+
} finally {
|
|
110
|
+
Bugsnag.pauseSession();
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Bugsnag v7 is a bit more complicated:
|
|
117
|
+
|
|
118
|
+
```js
|
|
119
|
+
const Bugsnag = require('@bugsnag/js');
|
|
120
|
+
const { addNotifier, setupTracing } = require('react-on-rails-pro-node-renderer/integrations/api');
|
|
121
|
+
|
|
122
|
+
Bugsnag.start({ /* your options */ });
|
|
123
|
+
|
|
124
|
+
addNotifier((msg, { bugsnag = Bugsnag }) => {
|
|
125
|
+
bugsnag.notify(msg);
|
|
126
|
+
});
|
|
127
|
+
setupTracing({
|
|
128
|
+
executor: async (fn) => {
|
|
129
|
+
const bugsnag = Bugsnag.startSession();
|
|
130
|
+
try {
|
|
131
|
+
return await fn({ bugsnag });
|
|
132
|
+
} finally {
|
|
133
|
+
bugsnag.pauseSession();
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Fastify integrations
|
|
140
|
+
|
|
141
|
+
If you want to report HTTP requests from Fastify, you can use `configureFastify` to add hooks or plugins as necessary.
|
|
142
|
+
Extending the above example:
|
|
143
|
+
|
|
144
|
+
```js
|
|
145
|
+
const { configureFastify } = require('react-on-rails-pro-node-renderer/integrations/api');
|
|
146
|
+
|
|
147
|
+
configureFastify((app) => {
|
|
148
|
+
app.addHook('onError', (_req, _reply, error, done) => {
|
|
149
|
+
Bugsnag.notify(error);
|
|
150
|
+
done();
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
You could also treat Fastify requests as sessions
|
|
156
|
+
using [onRequest](https://fastify.dev/docs/latest/Reference/Hooks/#onrequest)
|
|
157
|
+
or [preHandler](https://fastify.dev/docs/latest/Reference/Hooks/#prehandler) hooks.
|
|
158
|
+
|
|
159
|
+
It isn't recommended to use the [fastify-bugsnag](https://github.com/ZigaStrgar/fastify-bugsnag) plugin
|
|
160
|
+
since it wants to start Bugsnag on its own.
|