Skip to content

Commit c964f07

Browse files
authored
feat(ssr): inject fallback reason to html (#7053)
1 parent 7224b38 commit c964f07

File tree

5 files changed

+107
-8
lines changed

5 files changed

+107
-8
lines changed

.changeset/quick-mayflies-sort.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@modern-js/server-core': patch
3+
---
4+
5+
feat(ssr): inject fallback reason to html
6+
feat(ssr): 注入降级原因到响应的 html

packages/server/core/src/plugins/render/render.ts

+40-4
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,8 @@ export async function createRender({
146146
forMatchpathname,
147147
matchEntryName,
148148
);
149-
const framework = metaName || 'modern-js';
150-
const fallbackHeader = `x-${cutNameByHyphen(framework)}-ssr-fallback`;
149+
const framework = cutNameByHyphen(metaName || 'modern-js');
150+
const fallbackHeader = `x-${framework}-ssr-fallback`;
151151
let fallbackReason = null;
152152

153153
const fallbackWrapper: FallbackWrapper = async (reason, error?) => {
@@ -227,13 +227,27 @@ export async function createRender({
227227
onTiming,
228228
};
229229

230+
if (fallbackReason) {
231+
renderOptions.html = injectFallbackReasonToHtml({
232+
html: renderOptions.html,
233+
reason: fallbackReason,
234+
framework,
235+
});
236+
}
237+
230238
let response: Response;
231239

232240
switch (renderMode) {
233241
case 'data':
234242
response =
235243
(await dataHandler(req, renderOptions)) ||
236-
(await renderHandler(req, renderOptions, 'ssr', fallbackWrapper));
244+
(await renderHandler(
245+
req,
246+
renderOptions,
247+
'ssr',
248+
fallbackWrapper,
249+
framework,
250+
));
237251
break;
238252
case 'rsc-tree':
239253
response = await renderRscHandler(req, renderOptions);
@@ -248,6 +262,7 @@ export async function createRender({
248262
renderOptions,
249263
renderMode,
250264
fallbackWrapper,
265+
framework,
251266
);
252267
break;
253268
default:
@@ -266,6 +281,7 @@ async function renderHandler(
266281
options: SSRRenderOptions,
267282
mode: 'ssr' | 'csr',
268283
fallbackWrapper: FallbackWrapper,
284+
framework: string,
269285
) {
270286
let response: Response | null = null;
271287

@@ -318,7 +334,14 @@ async function renderHandler(
318334
} catch (e) {
319335
options.onError(e as Error, ErrorDigest.ERENDER);
320336
await fallbackWrapper('error', e);
321-
response = csrRender(options.html);
337+
338+
response = csrRender(
339+
injectFallbackReasonToHtml({
340+
html: options.html,
341+
reason: 'error',
342+
framework,
343+
}),
344+
);
322345
}
323346
} else {
324347
response = csrRender(options.html);
@@ -374,6 +397,19 @@ async function getRenderMode(
374397
}
375398
}
376399

400+
function injectFallbackReasonToHtml({
401+
html,
402+
reason,
403+
framework,
404+
}: {
405+
html: string;
406+
reason: FallbackReason;
407+
framework: string;
408+
}) {
409+
const tag = `<script id="__${framework}_ssr_fallback_reason__" type="application/json">${JSON.stringify({ reason })}</script>`;
410+
return html.replace(/<\/head>/, `${tag}</head>`);
411+
}
412+
377413
function csrRender(html: string): Response {
378414
return new Response(html, {
379415
status: 200,

packages/server/core/tests/fixtures/render/ssr/bundles/main.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
export function requestHandler() {
1+
export function requestHandler(request) {
2+
if (request.headers.get('x-render-error')) {
3+
throw new Error('custom render error');
4+
}
25
return new Response('SSR Render', {
36
headers: {
47
'content-type': 'text/html; charset=UTF-8',
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1-
<body>
2-
<h3>Hello Modern</h3>
3-
</body>
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Document</title>
7+
</head>
8+
<body>
9+
<h3>Hello Modern</h3>
10+
</body>
11+
</html>

packages/server/core/tests/plugins/render.test.ts

+46
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,11 @@ describe('should render html correctly', () => {
121121
},
122122
);
123123
expect(html2).toMatch(/Hello Modern/);
124+
expect(
125+
html2.includes(
126+
`<script id="__modern_ssr_fallback_reason__" type="application/json">{"reason":"query"}</script>`,
127+
),
128+
).toBe(true);
124129
expect(fallbackHeader).toBe('1;reason=query');
125130

126131
const html3 = await Promise.resolve(
@@ -138,6 +143,11 @@ describe('should render html correctly', () => {
138143
return res.text();
139144
});
140145
expect(fallbackHeader).toBe('1;reason=header');
146+
expect(
147+
html3.includes(
148+
`<script id="__modern_ssr_fallback_reason__" type="application/json">{"reason":"header"}</script>`,
149+
),
150+
).toBe(true);
141151
expect(html3).toMatch(/Hello Modern/);
142152

143153
// custom fallback reason in header.
@@ -156,6 +166,11 @@ describe('should render html correctly', () => {
156166
return res.text();
157167
});
158168
expect(fallbackHeader).toBe('1;reason=header,custom fallback reason');
169+
expect(
170+
html4.includes(
171+
`<script id="__modern_ssr_fallback_reason__" type="application/json">{"reason":"header,custom fallback reason"}</script>`,
172+
),
173+
).toBe(true);
159174
expect(html4).toMatch(/Hello Modern/);
160175
});
161176

@@ -173,4 +188,35 @@ describe('should render html correctly', () => {
173188
const text2 = await response2.text();
174189
expect(text2).toBe('handle user');
175190
});
191+
192+
it('should fallback to csr when render error', async () => {
193+
const ssrPwd = path.join(pwd, 'ssr');
194+
const server = await createSSRServer(ssrPwd, {
195+
ssr: {
196+
forceCSR: true,
197+
},
198+
});
199+
let fallbackHeader;
200+
// custom fallback reason in header.
201+
const html = await Promise.resolve(
202+
server.request(
203+
'/',
204+
{
205+
headers: new Headers({
206+
'x-render-error': '1',
207+
}),
208+
},
209+
{},
210+
),
211+
).then(res => {
212+
fallbackHeader = res.headers.get('x-modern-ssr-fallback');
213+
return res.text();
214+
});
215+
expect(fallbackHeader).toBe('1;reason=error');
216+
expect(
217+
html.includes(
218+
`<script id="__modern_ssr_fallback_reason__" type="application/json">{"reason":"error"}</script>`,
219+
),
220+
).toBe(true);
221+
});
176222
});

0 commit comments

Comments
 (0)