universal_renderer 0.3.1 → 0.4.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.
- checksums.yaml +4 -4
- data/README.md +23 -15
- data/lib/universal_renderer/client/stream/execution.rb +8 -8
- data/lib/universal_renderer/renderable.rb +12 -34
- data/lib/universal_renderer/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a84ac507fd401a10c8d36c9b7600d02b4403d84bdcbd63a0c4ac1f707efe0a7f
|
4
|
+
data.tar.gz: deac3f050553555b313864a1eb06ea69ff8e8d25ed78c47a70cb0c54e562012e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cda8091a0ad83367e3a0e8bcb93d560ce4b9e778cc543b7edba4084d0e7a540eda82fcd952e40b00115fef62326d15f760de905d882c607224047565d58c9c96
|
7
|
+
data.tar.gz: 15a2798e31d87ca6eec64e0613fedf2da515db85f3b798a792b0917d1719e970862c476c51eaebf65d8550dc0885bd5619edf662819085e3a132d877a4bdf73b
|
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# UniversalRenderer
|
2
2
|
|
3
|
+
[](https://rubygems.org/gems/universal_renderer) [](https://www.npmjs.com/package/universal-renderer)
|
4
|
+
|
3
5
|
A streamlined solution for integrating Server-Side Rendering (SSR) into Rails applications.
|
4
6
|
|
5
7
|
## Overview
|
@@ -185,23 +187,22 @@ To set up the SSR server for your Rails application:
|
|
185
187
|
4. Create an SSR entry point at `app/frontend/ssr/ssr.ts`:
|
186
188
|
|
187
189
|
```ts
|
188
|
-
import { renderToString } from "react-dom/server";
|
190
|
+
import { renderToString } from "react-dom/server.node";
|
189
191
|
import { createSsrServer } from "universal-renderer";
|
190
|
-
import { createServer as createViteServer } from "vite";
|
191
|
-
|
192
192
|
import setup from "@/ssr/setup";
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
193
|
+
import {
|
194
|
+
createRenderStreamTransformer,
|
195
|
+
extractMeta,
|
196
|
+
getRequestLogger,
|
197
|
+
getStateElement,
|
198
|
+
} from "@/ssr/utils";
|
198
199
|
|
199
200
|
const app = await createSsrServer({
|
200
|
-
|
201
|
+
middleware: (app) => {
|
202
|
+
app.use(getRequestLogger());
|
203
|
+
},
|
201
204
|
callbacks: {
|
202
|
-
|
203
|
-
// since Vite's ssrLoadModule doesn't include the types
|
204
|
-
setup: (await vite.ssrLoadModule("@/ssr/setup")).default as typeof setup,
|
205
|
+
setup: setup,
|
205
206
|
render: async ({ jsx, helmetContext, sheet, state }) => {
|
206
207
|
const root = renderToString(jsx);
|
207
208
|
const meta = extractMeta(helmetContext);
|
@@ -212,9 +213,16 @@ To set up the SSR server for your Rails application:
|
|
212
213
|
sheet?.seal();
|
213
214
|
queryClient?.clear();
|
214
215
|
},
|
215
|
-
|
216
|
-
|
217
|
-
|
216
|
+
error: (error, _, errorContext) => {
|
217
|
+
console.error(`[SSR] ${errorContext} error:`, error);
|
218
|
+
},
|
219
|
+
},
|
220
|
+
streamCallbacks: {
|
221
|
+
getReactNode: async ({ jsx }) => jsx,
|
222
|
+
getMetaTags: async ({ helmetContext }) => extractMeta(helmetContext),
|
223
|
+
createRenderStreamTransformer,
|
224
|
+
onBeforeWriteClosingHtml: async (res, { state }) => {
|
225
|
+
res.write(getStateElement(state) + "\n");
|
218
226
|
},
|
219
227
|
},
|
220
228
|
});
|
@@ -13,7 +13,7 @@ module UniversalRenderer
|
|
13
13
|
Execution._handle_node_response_streaming(
|
14
14
|
node_res,
|
15
15
|
response,
|
16
|
-
stream_uri
|
16
|
+
stream_uri,
|
17
17
|
)
|
18
18
|
|
19
19
|
return true
|
@@ -28,18 +28,18 @@ module UniversalRenderer
|
|
28
28
|
Execution._handle_streaming_for_node_error(
|
29
29
|
node_res,
|
30
30
|
response,
|
31
|
-
stream_uri
|
31
|
+
stream_uri,
|
32
32
|
)
|
33
33
|
end
|
34
34
|
rescue StandardError => e
|
35
35
|
Rails.logger.error(
|
36
|
-
"Error during SSR data transfer or stream writing from #{stream_uri}: #{e.class.name} - #{e.message}"
|
36
|
+
"Error during SSR data transfer or stream writing from #{stream_uri}: #{e.class.name} - #{e.message}",
|
37
37
|
)
|
38
38
|
|
39
39
|
Execution._write_generic_html_error(
|
40
40
|
response.stream,
|
41
41
|
"Streaming Error",
|
42
|
-
"<p>A problem occurred while loading content. Please refresh.</p>"
|
42
|
+
"<p>A problem occurred while loading content. Please refresh.</p>",
|
43
43
|
)
|
44
44
|
ensure
|
45
45
|
response.stream.close unless response.stream.closed?
|
@@ -53,7 +53,7 @@ module UniversalRenderer
|
|
53
53
|
|
54
54
|
def _handle_streaming_for_node_error(node_res, response, stream_uri)
|
55
55
|
Rails.logger.error(
|
56
|
-
"SSR stream server at #{stream_uri} responded with #{node_res.code} #{node_res.message}."
|
56
|
+
"SSR stream server at #{stream_uri} responded with #{node_res.code} #{node_res.message}.",
|
57
57
|
)
|
58
58
|
|
59
59
|
is_potentially_viewable_error =
|
@@ -61,19 +61,19 @@ module UniversalRenderer
|
|
61
61
|
|
62
62
|
if is_potentially_viewable_error
|
63
63
|
Rails.logger.info(
|
64
|
-
"Attempting to stream HTML error page from Node SSR server."
|
64
|
+
"Attempting to stream HTML error page from Node SSR server.",
|
65
65
|
)
|
66
66
|
Execution._stream_response_body(node_res, response.stream)
|
67
67
|
else
|
68
68
|
Rails.logger.warn(
|
69
69
|
"Node SSR server error response Content-Type ('#{node_res["Content-Type"]}') is not text/html. " \
|
70
|
-
"Injecting generic error message into the stream."
|
70
|
+
"Injecting generic error message into the stream.",
|
71
71
|
)
|
72
72
|
|
73
73
|
Execution._write_generic_html_error(
|
74
74
|
response.stream,
|
75
75
|
"Application Error",
|
76
|
-
"<p>There was an issue rendering this page. Please try again later.</p>"
|
76
|
+
"<p>There was an issue rendering this page. Please try again later.</p>",
|
77
77
|
)
|
78
78
|
end
|
79
79
|
end
|
@@ -31,7 +31,7 @@ module UniversalRenderer
|
|
31
31
|
@ssr =
|
32
32
|
UniversalRenderer::Client::Base.fetch(
|
33
33
|
request.original_url,
|
34
|
-
@universal_renderer_props
|
34
|
+
@universal_renderer_props,
|
35
35
|
)
|
36
36
|
end
|
37
37
|
|
@@ -39,52 +39,44 @@ module UniversalRenderer
|
|
39
39
|
self.class.try(:ssr_streaming_preference)
|
40
40
|
end
|
41
41
|
|
42
|
-
private
|
43
|
-
|
44
42
|
def render(*args, **kwargs)
|
45
43
|
return super unless self.class.enable_ssr
|
46
44
|
|
47
45
|
return super unless request.format.html?
|
48
46
|
|
49
47
|
if use_ssr_streaming?
|
50
|
-
render_ssr_stream(*args, **kwargs)
|
48
|
+
success = render_ssr_stream(*args, **kwargs)
|
49
|
+
super unless success
|
51
50
|
else
|
52
51
|
fetch_ssr
|
53
52
|
super
|
54
53
|
end
|
55
54
|
end
|
56
55
|
|
57
|
-
|
58
|
-
set_streaming_headers
|
56
|
+
private
|
59
57
|
|
58
|
+
def render_ssr_stream(*args, **kwargs)
|
60
59
|
full_layout = render_to_string(*args, **kwargs)
|
61
|
-
|
62
|
-
split_index = full_layout.index(SSR::Placeholders::META)
|
63
|
-
before_meta = full_layout[0...split_index]
|
64
|
-
after_meta = full_layout[split_index..]
|
65
|
-
|
66
|
-
response.stream.write(before_meta)
|
67
|
-
|
68
60
|
current_props = @universal_renderer_props.dup
|
69
61
|
|
70
62
|
streaming_succeeded =
|
71
63
|
UniversalRenderer::Client::Stream.stream(
|
72
64
|
request.original_url,
|
73
65
|
current_props,
|
74
|
-
|
75
|
-
response
|
66
|
+
full_layout,
|
67
|
+
response,
|
76
68
|
)
|
77
69
|
|
78
70
|
# SSR streaming failed or was not possible (e.g. server down, config missing).
|
79
|
-
|
71
|
+
if streaming_succeeded
|
72
|
+
response.stream.close unless response.stream.closed?
|
73
|
+
else
|
80
74
|
Rails.logger.error(
|
81
75
|
"SSR stream fallback: " \
|
82
|
-
"Streaming failed, proceeding with standard rendering."
|
76
|
+
"Streaming failed, proceeding with standard rendering.",
|
83
77
|
)
|
84
|
-
|
78
|
+
false
|
85
79
|
end
|
86
|
-
|
87
|
-
response.stream.close unless response.stream.closed?
|
88
80
|
end
|
89
81
|
|
90
82
|
def initialize_props
|
@@ -144,19 +136,5 @@ module UniversalRenderer
|
|
144
136
|
@universal_renderer_props[prop_key] << value_to_add
|
145
137
|
end
|
146
138
|
end
|
147
|
-
|
148
|
-
def set_streaming_headers
|
149
|
-
# Tell Cloudflare / proxies not to cache or buffer.
|
150
|
-
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
|
151
|
-
response.headers["Pragma"] = "no-cache"
|
152
|
-
response.headers["Expires"] = "0"
|
153
|
-
|
154
|
-
# Disable Nginx buffering per-response.
|
155
|
-
response.headers["X-Accel-Buffering"] = "no"
|
156
|
-
response.headers["Content-Type"] = "text/html"
|
157
|
-
|
158
|
-
# Remove Content-Length header to prevent buffering.
|
159
|
-
response.headers.delete("Content-Length")
|
160
|
-
end
|
161
139
|
end
|
162
140
|
end
|