@webjsdev/server 0.8.22 → 0.8.23
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/package.json +1 -1
- package/src/ssr.js +17 -8
package/package.json
CHANGED
package/src/ssr.js
CHANGED
|
@@ -82,7 +82,7 @@ export async function ssrPage(route, params, url, opts) {
|
|
|
82
82
|
const metadata = await collectMetadata(route, ctx, opts.dev);
|
|
83
83
|
|
|
84
84
|
try {
|
|
85
|
-
const suspenseCtx = { pending: [], nextId: 1, usedComponents: new Set() };
|
|
85
|
+
const suspenseCtx = { pending: [], nextId: 1, usedComponents: new Set(), dev: opts.dev };
|
|
86
86
|
// Parse the partial-nav "have" header from the client. The header
|
|
87
87
|
// lists comma-separated marker paths the client already has rendered
|
|
88
88
|
// in its DOM. The server walks the target route's layout chain
|
|
@@ -183,6 +183,7 @@ export async function ssrPage(route, params, url, opts) {
|
|
|
183
183
|
url,
|
|
184
184
|
metadata,
|
|
185
185
|
nonce,
|
|
186
|
+
opts.dev,
|
|
186
187
|
);
|
|
187
188
|
// Server HTML cache write (#241). The page opted in via `revalidate`, so
|
|
188
189
|
// FLAG this candidate for the response funnel rather than writing here: the
|
|
@@ -233,7 +234,7 @@ export async function ssrPage(route, params, url, opts) {
|
|
|
233
234
|
const mod = await loadModule(route.errors[i], opts.dev);
|
|
234
235
|
if (!mod.default) continue;
|
|
235
236
|
const tree = await mod.default({ ...ctx, error: err });
|
|
236
|
-
const body = await renderToString(tree);
|
|
237
|
+
const body = await renderToString(tree, { ssr: true, dev: opts.dev });
|
|
237
238
|
const moduleUrls = [route.file, ...route.layouts].map((f) => toUrlPath(f, opts.appDir));
|
|
238
239
|
const html = wrapInDocument(body, { metadata, moduleUrls, dev: opts.dev, nonce: errNonce });
|
|
239
240
|
return htmlResponse(html, 500, opts.req, url);
|
|
@@ -333,7 +334,7 @@ async function ssrNotFoundHtml(notFoundFile, opts) {
|
|
|
333
334
|
if (notFoundFile) {
|
|
334
335
|
try {
|
|
335
336
|
const mod = await loadModule(notFoundFile, opts.dev);
|
|
336
|
-
if (mod.default) body = await renderToString(await mod.default({}));
|
|
337
|
+
if (mod.default) body = await renderToString(await mod.default({}), { ssr: true, dev: opts.dev });
|
|
337
338
|
} catch (e) {
|
|
338
339
|
body = `<h1>404: Not found</h1><pre>${escapeHtml(String(e))}</pre>`;
|
|
339
340
|
}
|
|
@@ -430,7 +431,7 @@ async function loadingTemplates(route, ctx, dev) {
|
|
|
430
431
|
const mod = await loadModule(file, dev);
|
|
431
432
|
if (!mod.default) continue;
|
|
432
433
|
const tree = await mod.default(ctx);
|
|
433
|
-
const html = await renderToString(tree, { ssr: true });
|
|
434
|
+
const html = await renderToString(tree, { ssr: true, dev });
|
|
434
435
|
const segmentPath = loadingSegmentPath(file);
|
|
435
436
|
parts.push(`<template id="wj-loading:${segmentPath}">${html}</template>`);
|
|
436
437
|
} catch { /* skip broken loading file */ }
|
|
@@ -1444,15 +1445,18 @@ function deduplicatedPreloads(componentUrls, moduleUrls, graph, entryFiles, appD
|
|
|
1444
1445
|
* Build a streaming Response. Degrades to a single-flush response when
|
|
1445
1446
|
* there are no pending Suspense boundaries.
|
|
1446
1447
|
*
|
|
1447
|
-
* @param {string}
|
|
1448
|
+
* @param {string} prefix
|
|
1448
1449
|
* @param {string} bodyHtml
|
|
1450
|
+
* @param {string} closer
|
|
1449
1451
|
* @param {{ pending: {id: string, promise: Promise<unknown>}[], nextId: number }} ctx
|
|
1450
1452
|
* @param {number} status
|
|
1451
1453
|
* @param {Request | undefined} req
|
|
1452
1454
|
* @param {URL | undefined} url
|
|
1453
1455
|
* @param {Record<string, any>} [metadata]
|
|
1456
|
+
* @param {string} [nonce]
|
|
1457
|
+
* @param {boolean} [dev] dev surfaces a streamed-boundary error message; prod stays silent
|
|
1454
1458
|
*/
|
|
1455
|
-
function streamingHtmlResponse(prefix, bodyHtml, closer, ctx, status, req, url, metadata, nonce) {
|
|
1459
|
+
function streamingHtmlResponse(prefix, bodyHtml, closer, ctx, status, req, url, metadata, nonce, dev) {
|
|
1456
1460
|
const encoder = new TextEncoder();
|
|
1457
1461
|
const headers = new Headers({ 'content-type': 'text/html; charset=utf-8' });
|
|
1458
1462
|
// Default: no caching. Pages are dynamic by default: the developer
|
|
@@ -1499,14 +1503,19 @@ function streamingHtmlResponse(prefix, bodyHtml, closer, ctx, status, req, url,
|
|
|
1499
1503
|
batch.map(async (p) => {
|
|
1500
1504
|
try {
|
|
1501
1505
|
const resolved = await p.promise;
|
|
1502
|
-
const sub = { pending: [], nextId: ctx.nextId };
|
|
1506
|
+
const sub = { pending: [], nextId: ctx.nextId, dev: ctx.dev };
|
|
1503
1507
|
const html = await renderToString(resolved, { ssr: true, suspenseCtx: sub });
|
|
1504
1508
|
ctx.nextId = sub.nextId;
|
|
1505
1509
|
for (const n of sub.pending) ctx.pending.push(n);
|
|
1506
1510
|
return { id: p.id, html };
|
|
1507
1511
|
} catch (e) {
|
|
1512
|
+
// Match the SSR error-isolation policy (render-server.js's
|
|
1513
|
+
// defaultSSRErrorTemplate): dev surfaces the message so the
|
|
1514
|
+
// failure is obvious, prod stays SILENT so no internal detail
|
|
1515
|
+
// (a DB error, a stack-derived path) leaks to the client (#478).
|
|
1508
1516
|
const msg = e instanceof Error ? e.message : String(e);
|
|
1509
|
-
|
|
1517
|
+
const html = dev ? `<p>error: ${escapeHtml(msg)}</p>` : '';
|
|
1518
|
+
return { id: p.id, html };
|
|
1510
1519
|
}
|
|
1511
1520
|
})
|
|
1512
1521
|
);
|