@nestjs-ssr/react 0.2.6 → 0.3.1
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/dist/cli/init.js +35 -72
- package/dist/cli/init.mjs +35 -72
- package/dist/client.d.mts +55 -304
- package/dist/client.d.ts +55 -304
- package/dist/client.js +360 -13
- package/dist/client.mjs +336 -5
- package/dist/{index-BMdluY1g.d.mts → index-BzOLOiIZ.d.ts} +207 -44
- package/dist/{index-rl0S5kqW.d.ts → index-DdE--mA2.d.mts} +207 -44
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +660 -326
- package/dist/index.mjs +654 -328
- package/dist/render/index.d.mts +2 -2
- package/dist/render/index.d.ts +2 -2
- package/dist/render/index.js +624 -324
- package/dist/render/index.mjs +624 -324
- package/dist/{render-response.interface-Dc-Kwb09.d.mts → render-response.interface-CxbuKGnV.d.mts} +57 -1
- package/dist/{render-response.interface-Dc-Kwb09.d.ts → render-response.interface-CxbuKGnV.d.ts} +57 -1
- package/dist/templates/entry-client.tsx +21 -5
- package/dist/templates/entry-server.tsx +48 -6
- package/dist/use-page-context-05ODF4zW.d.ts +281 -0
- package/dist/use-page-context-CGT9woWe.d.mts +281 -0
- package/package.json +1 -1
- package/src/global.d.ts +11 -0
- package/src/templates/entry-client.tsx +21 -5
- package/src/templates/entry-server.tsx +48 -6
package/dist/index.js
CHANGED
|
@@ -192,6 +192,153 @@ window.__LAYOUTS__ = ${devalue.uneval(layoutMetadata)};
|
|
|
192
192
|
exports.TemplateParserService = _ts_decorate([
|
|
193
193
|
common.Injectable()
|
|
194
194
|
], exports.TemplateParserService);
|
|
195
|
+
|
|
196
|
+
// src/render/renderers/string-renderer.ts
|
|
197
|
+
function _ts_decorate2(decorators, target, key, desc) {
|
|
198
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
199
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
200
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
201
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
202
|
+
}
|
|
203
|
+
__name(_ts_decorate2, "_ts_decorate");
|
|
204
|
+
function _ts_metadata(k, v) {
|
|
205
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
206
|
+
}
|
|
207
|
+
__name(_ts_metadata, "_ts_metadata");
|
|
208
|
+
var StringRenderer = class _StringRenderer {
|
|
209
|
+
static {
|
|
210
|
+
__name(this, "StringRenderer");
|
|
211
|
+
}
|
|
212
|
+
templateParser;
|
|
213
|
+
logger = new common.Logger(_StringRenderer.name);
|
|
214
|
+
constructor(templateParser) {
|
|
215
|
+
this.templateParser = templateParser;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Render a React component to a complete HTML string
|
|
219
|
+
*/
|
|
220
|
+
async render(viewComponent, data, context, head) {
|
|
221
|
+
const startTime = Date.now();
|
|
222
|
+
let template = context.template;
|
|
223
|
+
if (context.vite) {
|
|
224
|
+
template = await context.vite.transformIndexHtml("/", template);
|
|
225
|
+
}
|
|
226
|
+
let renderModule;
|
|
227
|
+
if (context.vite) {
|
|
228
|
+
renderModule = await context.vite.ssrLoadModule(context.entryServerPath);
|
|
229
|
+
} else {
|
|
230
|
+
if (context.serverManifest) {
|
|
231
|
+
const manifestEntry = Object.entries(context.serverManifest).find(([key, value]) => value.isEntry && key.includes("entry-server"));
|
|
232
|
+
if (manifestEntry) {
|
|
233
|
+
const [, entry] = manifestEntry;
|
|
234
|
+
const serverPath = `${process.cwd()}/dist/server/${entry.file}`;
|
|
235
|
+
renderModule = await import(serverPath);
|
|
236
|
+
} else {
|
|
237
|
+
throw new Error("Server bundle not found in manifest. Run `pnpm build:server` to generate the server bundle.");
|
|
238
|
+
}
|
|
239
|
+
} else {
|
|
240
|
+
throw new Error("Server bundle not found in manifest. Run `pnpm build:server` to generate the server bundle.");
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
const { data: pageData, __context: pageContext, __layouts: layouts } = data;
|
|
244
|
+
const appHtml = await renderModule.renderComponent(viewComponent, data);
|
|
245
|
+
const componentName = viewComponent.displayName || viewComponent.name || "Component";
|
|
246
|
+
const layoutMetadata = layouts ? layouts.map((l) => ({
|
|
247
|
+
name: l.layout.displayName || l.layout.name || "default",
|
|
248
|
+
props: l.props
|
|
249
|
+
})) : [];
|
|
250
|
+
const initialStateScript = `
|
|
251
|
+
<script>
|
|
252
|
+
window.__INITIAL_STATE__ = ${devalue.uneval(pageData)};
|
|
253
|
+
window.__CONTEXT__ = ${devalue.uneval(pageContext)};
|
|
254
|
+
window.__COMPONENT_NAME__ = ${devalue.uneval(componentName)};
|
|
255
|
+
window.__LAYOUTS__ = ${devalue.uneval(layoutMetadata)};
|
|
256
|
+
</script>
|
|
257
|
+
`;
|
|
258
|
+
let clientScript = "";
|
|
259
|
+
let styles = "";
|
|
260
|
+
if (context.vite) {
|
|
261
|
+
clientScript = `<script type="module" src="/src/views/entry-client.tsx"></script>`;
|
|
262
|
+
} else {
|
|
263
|
+
if (context.manifest) {
|
|
264
|
+
const manifestEntry = Object.entries(context.manifest).find(([key, value]) => value.isEntry && key.includes("entry-client"));
|
|
265
|
+
if (manifestEntry) {
|
|
266
|
+
const [, entry] = manifestEntry;
|
|
267
|
+
const entryFile = entry.file;
|
|
268
|
+
clientScript = `<script type="module" src="/${entryFile}"></script>`;
|
|
269
|
+
if (entry.css) {
|
|
270
|
+
const cssFiles = entry.css;
|
|
271
|
+
styles = cssFiles.map((css) => `<link rel="stylesheet" href="/${css}" />`).join("\n ");
|
|
272
|
+
}
|
|
273
|
+
} else {
|
|
274
|
+
this.logger.error("\u26A0\uFE0F Client entry not found in manifest");
|
|
275
|
+
clientScript = `<script type="module" src="/assets/client.js"></script>`;
|
|
276
|
+
}
|
|
277
|
+
} else {
|
|
278
|
+
this.logger.error("\u26A0\uFE0F Client manifest not found");
|
|
279
|
+
clientScript = `<script type="module" src="/assets/client.js"></script>`;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
const headTags = this.templateParser.buildHeadTags(head);
|
|
283
|
+
let html = template.replace("<!--app-html-->", appHtml);
|
|
284
|
+
html = html.replace("<!--initial-state-->", initialStateScript);
|
|
285
|
+
html = html.replace("<!--client-scripts-->", clientScript);
|
|
286
|
+
html = html.replace("<!--styles-->", styles);
|
|
287
|
+
html = html.replace("<!--head-meta-->", headTags);
|
|
288
|
+
if (context.isDevelopment) {
|
|
289
|
+
const duration = Date.now() - startTime;
|
|
290
|
+
const name = typeof viewComponent === "function" ? viewComponent.name : String(viewComponent);
|
|
291
|
+
this.logger.log(`[SSR] ${name} rendered in ${duration}ms (string mode)`);
|
|
292
|
+
}
|
|
293
|
+
return html;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Render a segment for client-side navigation.
|
|
297
|
+
* Returns just the HTML and metadata without the full page template.
|
|
298
|
+
*/
|
|
299
|
+
async renderSegment(viewComponent, data, context, swapTarget, head) {
|
|
300
|
+
const startTime = Date.now();
|
|
301
|
+
let renderModule;
|
|
302
|
+
if (context.vite) {
|
|
303
|
+
renderModule = await context.vite.ssrLoadModule(context.entryServerPath);
|
|
304
|
+
} else {
|
|
305
|
+
if (context.serverManifest) {
|
|
306
|
+
const manifestEntry = Object.entries(context.serverManifest).find(([key, value]) => value.isEntry && key.includes("entry-server"));
|
|
307
|
+
if (manifestEntry) {
|
|
308
|
+
const [, entry] = manifestEntry;
|
|
309
|
+
const serverPath = `${process.cwd()}/dist/server/${entry.file}`;
|
|
310
|
+
renderModule = await import(serverPath);
|
|
311
|
+
} else {
|
|
312
|
+
throw new Error("Server bundle not found in manifest. Run `pnpm build:server` to generate the server bundle.");
|
|
313
|
+
}
|
|
314
|
+
} else {
|
|
315
|
+
throw new Error("Server bundle not found in manifest. Run `pnpm build:server` to generate the server bundle.");
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
const { data: pageData, __context: pageContext } = data;
|
|
319
|
+
const html = await renderModule.renderSegment(viewComponent, data);
|
|
320
|
+
const componentName = viewComponent.displayName || viewComponent.name || "Component";
|
|
321
|
+
if (context.isDevelopment) {
|
|
322
|
+
const duration = Date.now() - startTime;
|
|
323
|
+
this.logger.log(`[SSR] ${componentName} segment rendered in ${duration}ms`);
|
|
324
|
+
}
|
|
325
|
+
return {
|
|
326
|
+
html,
|
|
327
|
+
head,
|
|
328
|
+
props: pageData,
|
|
329
|
+
swapTarget,
|
|
330
|
+
componentName,
|
|
331
|
+
context: pageContext
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
StringRenderer = _ts_decorate2([
|
|
336
|
+
common.Injectable(),
|
|
337
|
+
_ts_metadata("design:type", Function),
|
|
338
|
+
_ts_metadata("design:paramtypes", [
|
|
339
|
+
typeof exports.TemplateParserService === "undefined" ? Object : exports.TemplateParserService
|
|
340
|
+
])
|
|
341
|
+
], StringRenderer);
|
|
195
342
|
function ErrorPageDevelopment({ error, viewPath, phase }) {
|
|
196
343
|
const stackLines = error.stack ? error.stack.split("\n").slice(1) : [];
|
|
197
344
|
return /* @__PURE__ */ React__default.default.createElement("html", {
|
|
@@ -307,17 +454,17 @@ function ErrorPageProduction() {
|
|
|
307
454
|
__name(ErrorPageProduction, "ErrorPageProduction");
|
|
308
455
|
|
|
309
456
|
// src/render/streaming-error-handler.ts
|
|
310
|
-
function
|
|
457
|
+
function _ts_decorate3(decorators, target, key, desc) {
|
|
311
458
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
312
459
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
313
460
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
314
461
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
315
462
|
}
|
|
316
|
-
__name(
|
|
317
|
-
function
|
|
463
|
+
__name(_ts_decorate3, "_ts_decorate");
|
|
464
|
+
function _ts_metadata2(k, v) {
|
|
318
465
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
319
466
|
}
|
|
320
|
-
__name(
|
|
467
|
+
__name(_ts_metadata2, "_ts_metadata");
|
|
321
468
|
function _ts_param(paramIndex, decorator) {
|
|
322
469
|
return function(target, key) {
|
|
323
470
|
decorator(target, key, paramIndex);
|
|
@@ -341,6 +488,14 @@ exports.StreamingErrorHandler = class _StreamingErrorHandler {
|
|
|
341
488
|
*/
|
|
342
489
|
handleShellError(error, res, viewPath, isDevelopment) {
|
|
343
490
|
this.logger.error(`Shell error rendering ${viewPath}: ${error.message}`, error.stack);
|
|
491
|
+
if (res.headersSent) {
|
|
492
|
+
this.logger.error(`Cannot send error page for ${viewPath} - headers already sent (streaming started)`);
|
|
493
|
+
if (!res.writableEnded) {
|
|
494
|
+
res.write(this.renderInlineErrorOverlay(error, viewPath, isDevelopment));
|
|
495
|
+
res.end();
|
|
496
|
+
}
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
344
499
|
res.statusCode = 500;
|
|
345
500
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
346
501
|
if (isDevelopment) {
|
|
@@ -376,32 +531,263 @@ exports.StreamingErrorHandler = class _StreamingErrorHandler {
|
|
|
376
531
|
const element = React.createElement(ErrorComponent);
|
|
377
532
|
return "<!DOCTYPE html>\n" + server.renderToStaticMarkup(element);
|
|
378
533
|
}
|
|
534
|
+
/**
|
|
535
|
+
* Render inline error overlay for when headers are already sent
|
|
536
|
+
* This gets injected into the stream to show a visible error UI
|
|
537
|
+
*/
|
|
538
|
+
renderInlineErrorOverlay(error, viewPath, isDevelopment) {
|
|
539
|
+
const errorMessage = escapeHtml__default.default(error.message);
|
|
540
|
+
const errorStack = escapeHtml__default.default(error.stack || "");
|
|
541
|
+
const escapedViewPath = escapeHtml__default.default(viewPath);
|
|
542
|
+
if (isDevelopment) {
|
|
543
|
+
return `
|
|
544
|
+
<div id="ssr-error-overlay" style="
|
|
545
|
+
position: fixed;
|
|
546
|
+
inset: 0;
|
|
547
|
+
z-index: 99999;
|
|
548
|
+
background: rgba(0, 0, 0, 0.85);
|
|
549
|
+
color: #fff;
|
|
550
|
+
font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
|
|
551
|
+
font-size: 14px;
|
|
552
|
+
padding: 32px;
|
|
553
|
+
overflow: auto;
|
|
554
|
+
">
|
|
555
|
+
<div style="max-width: 900px; margin: 0 auto;">
|
|
556
|
+
<div style="display: flex; align-items: center; gap: 12px; margin-bottom: 24px;">
|
|
557
|
+
<span style="font-size: 32px;">\u26A0\uFE0F</span>
|
|
558
|
+
<h1 style="margin: 0; font-size: 24px; font-weight: 600; color: #ff6b6b;">
|
|
559
|
+
SSR Streaming Error
|
|
560
|
+
</h1>
|
|
561
|
+
</div>
|
|
562
|
+
<p style="color: #aaa; margin-bottom: 16px;">
|
|
563
|
+
An error occurred after streaming started in <code style="background: #333; padding: 2px 6px; border-radius: 4px;">${escapedViewPath}</code>
|
|
564
|
+
</p>
|
|
565
|
+
<div style="background: #1a1a1a; border: 1px solid #333; border-radius: 8px; padding: 16px; margin-bottom: 16px;">
|
|
566
|
+
<div style="color: #ff6b6b; font-weight: 600; margin-bottom: 8px;">Error Message:</div>
|
|
567
|
+
<pre style="margin: 0; white-space: pre-wrap; word-break: break-word; color: #fff;">${errorMessage}</pre>
|
|
568
|
+
</div>
|
|
569
|
+
<div style="background: #1a1a1a; border: 1px solid #333; border-radius: 8px; padding: 16px;">
|
|
570
|
+
<div style="color: #888; font-weight: 600; margin-bottom: 8px;">Stack Trace:</div>
|
|
571
|
+
<pre style="margin: 0; white-space: pre-wrap; word-break: break-word; color: #888; font-size: 12px;">${errorStack}</pre>
|
|
572
|
+
</div>
|
|
573
|
+
<button onclick="document.getElementById('ssr-error-overlay').remove()" style="
|
|
574
|
+
margin-top: 24px;
|
|
575
|
+
background: #333;
|
|
576
|
+
color: #fff;
|
|
577
|
+
border: 1px solid #555;
|
|
578
|
+
padding: 8px 16px;
|
|
579
|
+
border-radius: 6px;
|
|
580
|
+
cursor: pointer;
|
|
581
|
+
font-family: inherit;
|
|
582
|
+
">Dismiss</button>
|
|
583
|
+
</div>
|
|
584
|
+
</div>
|
|
585
|
+
<script>console.error('SSR Streaming Error in ${escapedViewPath}:', ${devalue.uneval(error.message)});</script>
|
|
586
|
+
`;
|
|
587
|
+
} else {
|
|
588
|
+
return `
|
|
589
|
+
<div id="ssr-error-overlay" style="
|
|
590
|
+
position: fixed;
|
|
591
|
+
inset: 0;
|
|
592
|
+
z-index: 99999;
|
|
593
|
+
background: #fff;
|
|
594
|
+
color: #333;
|
|
595
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
596
|
+
display: flex;
|
|
597
|
+
align-items: center;
|
|
598
|
+
justify-content: center;
|
|
599
|
+
text-align: center;
|
|
600
|
+
">
|
|
601
|
+
<div>
|
|
602
|
+
<h1 style="font-size: 24px; font-weight: 600; margin-bottom: 16px;">Something went wrong</h1>
|
|
603
|
+
<p style="color: #666; margin-bottom: 24px;">We're sorry, but something went wrong. Please try refreshing the page.</p>
|
|
604
|
+
<button onclick="location.reload()" style="
|
|
605
|
+
background: #333;
|
|
606
|
+
color: #fff;
|
|
607
|
+
border: none;
|
|
608
|
+
padding: 12px 24px;
|
|
609
|
+
border-radius: 6px;
|
|
610
|
+
cursor: pointer;
|
|
611
|
+
font-family: inherit;
|
|
612
|
+
font-size: 16px;
|
|
613
|
+
">Refresh Page</button>
|
|
614
|
+
</div>
|
|
615
|
+
</div>
|
|
616
|
+
`;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
379
619
|
};
|
|
380
|
-
exports.StreamingErrorHandler =
|
|
620
|
+
exports.StreamingErrorHandler = _ts_decorate3([
|
|
381
621
|
common.Injectable(),
|
|
382
622
|
_ts_param(0, common.Optional()),
|
|
383
623
|
_ts_param(0, common.Inject("ERROR_PAGE_DEVELOPMENT")),
|
|
384
624
|
_ts_param(1, common.Optional()),
|
|
385
625
|
_ts_param(1, common.Inject("ERROR_PAGE_PRODUCTION")),
|
|
386
|
-
|
|
387
|
-
|
|
626
|
+
_ts_metadata2("design:type", Function),
|
|
627
|
+
_ts_metadata2("design:paramtypes", [
|
|
388
628
|
typeof ComponentType === "undefined" ? Object : ComponentType,
|
|
389
629
|
typeof ComponentType === "undefined" ? Object : ComponentType
|
|
390
630
|
])
|
|
391
631
|
], exports.StreamingErrorHandler);
|
|
392
632
|
|
|
633
|
+
// src/render/renderers/stream-renderer.ts
|
|
634
|
+
function _ts_decorate4(decorators, target, key, desc) {
|
|
635
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
636
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
637
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
638
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
639
|
+
}
|
|
640
|
+
__name(_ts_decorate4, "_ts_decorate");
|
|
641
|
+
function _ts_metadata3(k, v) {
|
|
642
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
643
|
+
}
|
|
644
|
+
__name(_ts_metadata3, "_ts_metadata");
|
|
645
|
+
var StreamRenderer = class _StreamRenderer {
|
|
646
|
+
static {
|
|
647
|
+
__name(this, "StreamRenderer");
|
|
648
|
+
}
|
|
649
|
+
templateParser;
|
|
650
|
+
streamingErrorHandler;
|
|
651
|
+
logger = new common.Logger(_StreamRenderer.name);
|
|
652
|
+
constructor(templateParser, streamingErrorHandler) {
|
|
653
|
+
this.templateParser = templateParser;
|
|
654
|
+
this.streamingErrorHandler = streamingErrorHandler;
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* Render a React component using streaming SSR
|
|
658
|
+
*
|
|
659
|
+
* @param viewComponent - The React component to render
|
|
660
|
+
* @param data - Data to pass to the component
|
|
661
|
+
* @param res - Express response object (required for streaming)
|
|
662
|
+
* @param context - Render context with Vite and manifest info
|
|
663
|
+
* @param head - Head data for SEO tags
|
|
664
|
+
*/
|
|
665
|
+
async render(viewComponent, data, res, context, head) {
|
|
666
|
+
const startTime = Date.now();
|
|
667
|
+
let shellReadyTime = 0;
|
|
668
|
+
return new Promise((resolve, reject) => {
|
|
669
|
+
const executeStream = /* @__PURE__ */ __name(async () => {
|
|
670
|
+
let template = context.template;
|
|
671
|
+
if (context.vite) {
|
|
672
|
+
template = await context.vite.transformIndexHtml("/", template);
|
|
673
|
+
}
|
|
674
|
+
const templateParts = this.templateParser.parseTemplate(template);
|
|
675
|
+
let renderModule;
|
|
676
|
+
if (context.vite) {
|
|
677
|
+
renderModule = await context.vite.ssrLoadModule(context.entryServerPath);
|
|
678
|
+
} else {
|
|
679
|
+
if (context.serverManifest) {
|
|
680
|
+
const manifestEntry = Object.entries(context.serverManifest).find(([key, value]) => value.isEntry && key.includes("entry-server"));
|
|
681
|
+
if (manifestEntry) {
|
|
682
|
+
const [, entry] = manifestEntry;
|
|
683
|
+
const serverPath = `${process.cwd()}/dist/server/${entry.file}`;
|
|
684
|
+
renderModule = await import(serverPath);
|
|
685
|
+
} else {
|
|
686
|
+
throw new Error("Server bundle not found in manifest. Run `pnpm build:server` to generate the server bundle.");
|
|
687
|
+
}
|
|
688
|
+
} else {
|
|
689
|
+
throw new Error("Server bundle not found in manifest. Run `pnpm build:server` to generate the server bundle.");
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
const { data: pageData, __context: pageContext, __layouts: layouts } = data;
|
|
693
|
+
const componentName = viewComponent.displayName || viewComponent.name || "Component";
|
|
694
|
+
const inlineScripts = this.templateParser.buildInlineScripts(pageData, pageContext, componentName, layouts);
|
|
695
|
+
const clientScript = this.templateParser.getClientScriptTag(context.isDevelopment, context.manifest);
|
|
696
|
+
const stylesheetTags = this.templateParser.getStylesheetTags(context.isDevelopment, context.manifest);
|
|
697
|
+
const headTags = this.templateParser.buildHeadTags(head);
|
|
698
|
+
let didError = false;
|
|
699
|
+
let shellErrorOccurred = false;
|
|
700
|
+
const { PassThrough } = await import('stream');
|
|
701
|
+
const reactStream = new PassThrough();
|
|
702
|
+
let allReadyFired = false;
|
|
703
|
+
const { pipe, abort } = renderModule.renderComponentStream(viewComponent, data, {
|
|
704
|
+
onShellReady: /* @__PURE__ */ __name(() => {
|
|
705
|
+
shellReadyTime = Date.now();
|
|
706
|
+
if (!res.headersSent) {
|
|
707
|
+
res.statusCode = didError ? 500 : 200;
|
|
708
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
709
|
+
}
|
|
710
|
+
let htmlStart = templateParts.htmlStart;
|
|
711
|
+
htmlStart = htmlStart.replace("<!--styles-->", stylesheetTags);
|
|
712
|
+
htmlStart = htmlStart.replace("<!--head-meta-->", headTags);
|
|
713
|
+
res.write(htmlStart);
|
|
714
|
+
res.write(templateParts.rootStart);
|
|
715
|
+
pipe(reactStream);
|
|
716
|
+
reactStream.pipe(res, {
|
|
717
|
+
end: false
|
|
718
|
+
});
|
|
719
|
+
if (context.isDevelopment) {
|
|
720
|
+
const ttfb = shellReadyTime - startTime;
|
|
721
|
+
this.logger.log(`[SSR] ${componentName} shell ready in ${ttfb}ms (stream mode - TTFB)`);
|
|
722
|
+
}
|
|
723
|
+
}, "onShellReady"),
|
|
724
|
+
onShellError: /* @__PURE__ */ __name((error) => {
|
|
725
|
+
shellErrorOccurred = true;
|
|
726
|
+
this.streamingErrorHandler.handleShellError(error, res, componentName, context.isDevelopment);
|
|
727
|
+
resolve();
|
|
728
|
+
}, "onShellError"),
|
|
729
|
+
onError: /* @__PURE__ */ __name((error) => {
|
|
730
|
+
didError = true;
|
|
731
|
+
this.streamingErrorHandler.handleStreamError(error, componentName);
|
|
732
|
+
}, "onError"),
|
|
733
|
+
onAllReady: /* @__PURE__ */ __name(() => {
|
|
734
|
+
allReadyFired = true;
|
|
735
|
+
}, "onAllReady")
|
|
736
|
+
});
|
|
737
|
+
reactStream.on("end", () => {
|
|
738
|
+
if (shellErrorOccurred) {
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
res.write(inlineScripts);
|
|
742
|
+
res.write(clientScript);
|
|
743
|
+
res.write(templateParts.rootEnd);
|
|
744
|
+
res.write(templateParts.htmlEnd);
|
|
745
|
+
res.end();
|
|
746
|
+
if (context.isDevelopment) {
|
|
747
|
+
const totalTime = Date.now() - startTime;
|
|
748
|
+
const streamTime = Date.now() - shellReadyTime;
|
|
749
|
+
const viaAllReady = allReadyFired ? " (onAllReady fired)" : " (onAllReady never fired)";
|
|
750
|
+
this.logger.log(`[SSR] ${componentName} streaming complete in ${totalTime}ms total (${streamTime}ms streaming)${viaAllReady}`);
|
|
751
|
+
}
|
|
752
|
+
resolve();
|
|
753
|
+
});
|
|
754
|
+
reactStream.on("error", (error) => {
|
|
755
|
+
reject(error);
|
|
756
|
+
});
|
|
757
|
+
res.on("close", () => {
|
|
758
|
+
abort();
|
|
759
|
+
resolve();
|
|
760
|
+
});
|
|
761
|
+
}, "executeStream");
|
|
762
|
+
executeStream().catch((error) => {
|
|
763
|
+
const componentName = typeof viewComponent === "function" ? viewComponent.name : String(viewComponent);
|
|
764
|
+
this.streamingErrorHandler.handleShellError(error, res, componentName, context.isDevelopment);
|
|
765
|
+
resolve();
|
|
766
|
+
});
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
};
|
|
770
|
+
StreamRenderer = _ts_decorate4([
|
|
771
|
+
common.Injectable(),
|
|
772
|
+
_ts_metadata3("design:type", Function),
|
|
773
|
+
_ts_metadata3("design:paramtypes", [
|
|
774
|
+
typeof exports.TemplateParserService === "undefined" ? Object : exports.TemplateParserService,
|
|
775
|
+
typeof exports.StreamingErrorHandler === "undefined" ? Object : exports.StreamingErrorHandler
|
|
776
|
+
])
|
|
777
|
+
], StreamRenderer);
|
|
778
|
+
|
|
393
779
|
// src/render/render.service.ts
|
|
394
|
-
function
|
|
780
|
+
function _ts_decorate5(decorators, target, key, desc) {
|
|
395
781
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
396
782
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
397
783
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
398
784
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
399
785
|
}
|
|
400
|
-
__name(
|
|
401
|
-
function
|
|
786
|
+
__name(_ts_decorate5, "_ts_decorate");
|
|
787
|
+
function _ts_metadata4(k, v) {
|
|
402
788
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
403
789
|
}
|
|
404
|
-
__name(
|
|
790
|
+
__name(_ts_metadata4, "_ts_metadata");
|
|
405
791
|
function _ts_param2(paramIndex, decorator) {
|
|
406
792
|
return function(target, key) {
|
|
407
793
|
decorator(target, key, paramIndex);
|
|
@@ -412,8 +798,8 @@ exports.RenderService = class _RenderService {
|
|
|
412
798
|
static {
|
|
413
799
|
__name(this, "RenderService");
|
|
414
800
|
}
|
|
415
|
-
|
|
416
|
-
|
|
801
|
+
stringRenderer;
|
|
802
|
+
streamRenderer;
|
|
417
803
|
defaultHead;
|
|
418
804
|
logger = new common.Logger(_RenderService.name);
|
|
419
805
|
vite = null;
|
|
@@ -425,12 +811,12 @@ exports.RenderService = class _RenderService {
|
|
|
425
811
|
entryServerPath;
|
|
426
812
|
rootLayout = void 0;
|
|
427
813
|
rootLayoutChecked = false;
|
|
428
|
-
constructor(
|
|
429
|
-
this.
|
|
430
|
-
this.
|
|
814
|
+
constructor(stringRenderer, streamRenderer, ssrMode, defaultHead, customTemplate) {
|
|
815
|
+
this.stringRenderer = stringRenderer;
|
|
816
|
+
this.streamRenderer = streamRenderer;
|
|
431
817
|
this.defaultHead = defaultHead;
|
|
432
818
|
this.isDevelopment = process.env.NODE_ENV !== "production";
|
|
433
|
-
this.ssrMode = ssrMode || process.env.SSR_MODE || "
|
|
819
|
+
this.ssrMode = ssrMode || process.env.SSR_MODE || "string";
|
|
434
820
|
const absoluteServerPath = path.join(__dirname, "/templates/entry-server.tsx");
|
|
435
821
|
const relativeServerPath = path.relative(process.cwd(), absoluteServerPath);
|
|
436
822
|
if (relativeServerPath.startsWith("..")) {
|
|
@@ -438,67 +824,82 @@ exports.RenderService = class _RenderService {
|
|
|
438
824
|
} else {
|
|
439
825
|
this.entryServerPath = "/" + relativeServerPath.replace(/\\/g, "/");
|
|
440
826
|
}
|
|
827
|
+
this.template = this.loadTemplate(customTemplate);
|
|
828
|
+
if (!this.isDevelopment) {
|
|
829
|
+
this.loadManifests();
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* Load HTML template from custom path, package, or local location
|
|
834
|
+
*/
|
|
835
|
+
loadTemplate(customTemplate) {
|
|
441
836
|
if (customTemplate) {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
837
|
+
return this.loadCustomTemplate(customTemplate);
|
|
838
|
+
}
|
|
839
|
+
return this.loadDefaultTemplate();
|
|
840
|
+
}
|
|
841
|
+
loadCustomTemplate(customTemplate) {
|
|
842
|
+
if (customTemplate.includes("<!DOCTYPE") || customTemplate.includes("<html")) {
|
|
843
|
+
this.logger.log(`\u2713 Loaded custom template (inline)`);
|
|
844
|
+
return customTemplate;
|
|
845
|
+
}
|
|
846
|
+
const customTemplatePath = customTemplate.startsWith("/") ? customTemplate : path.join(process.cwd(), customTemplate);
|
|
847
|
+
if (!fs.existsSync(customTemplatePath)) {
|
|
848
|
+
throw new Error(`Custom template file not found at ${customTemplatePath}`);
|
|
849
|
+
}
|
|
850
|
+
try {
|
|
851
|
+
const template = fs.readFileSync(customTemplatePath, "utf-8");
|
|
852
|
+
this.logger.log(`\u2713 Loaded custom template from ${customTemplatePath}`);
|
|
853
|
+
return template;
|
|
854
|
+
} catch (error) {
|
|
855
|
+
throw new Error(`Failed to read custom template file at ${customTemplatePath}: ${error.message}`);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
loadDefaultTemplate() {
|
|
859
|
+
let templatePath;
|
|
860
|
+
if (this.isDevelopment) {
|
|
861
|
+
const packageTemplatePaths = [
|
|
862
|
+
path.join(__dirname, "../templates/index.html"),
|
|
863
|
+
path.join(__dirname, "../src/templates/index.html"),
|
|
864
|
+
path.join(__dirname, "../../src/templates/index.html")
|
|
865
|
+
];
|
|
866
|
+
const localTemplatePath = path.join(process.cwd(), "src/views/index.html");
|
|
867
|
+
const foundPackageTemplate = packageTemplatePaths.find((p) => fs.existsSync(p));
|
|
868
|
+
if (foundPackageTemplate) {
|
|
869
|
+
templatePath = foundPackageTemplate;
|
|
870
|
+
} else if (fs.existsSync(localTemplatePath)) {
|
|
871
|
+
templatePath = localTemplatePath;
|
|
445
872
|
} else {
|
|
446
|
-
|
|
447
|
-
if (!fs.existsSync(customTemplatePath)) {
|
|
448
|
-
throw new Error(`Custom template file not found at ${customTemplatePath}`);
|
|
449
|
-
}
|
|
450
|
-
try {
|
|
451
|
-
this.template = fs.readFileSync(customTemplatePath, "utf-8");
|
|
452
|
-
this.logger.log(`\u2713 Loaded custom template from ${customTemplatePath}`);
|
|
453
|
-
} catch (error) {
|
|
454
|
-
throw new Error(`Failed to read custom template file at ${customTemplatePath}: ${error.message}`);
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
} else {
|
|
458
|
-
let templatePath;
|
|
459
|
-
if (this.isDevelopment) {
|
|
460
|
-
const packageTemplatePaths = [
|
|
461
|
-
path.join(__dirname, "../templates/index.html"),
|
|
462
|
-
path.join(__dirname, "../src/templates/index.html"),
|
|
463
|
-
path.join(__dirname, "../../src/templates/index.html")
|
|
464
|
-
];
|
|
465
|
-
const localTemplatePath = path.join(process.cwd(), "src/views/index.html");
|
|
466
|
-
const foundPackageTemplate = packageTemplatePaths.find((p) => fs.existsSync(p));
|
|
467
|
-
if (foundPackageTemplate) {
|
|
468
|
-
templatePath = foundPackageTemplate;
|
|
469
|
-
} else if (fs.existsSync(localTemplatePath)) {
|
|
470
|
-
templatePath = localTemplatePath;
|
|
471
|
-
} else {
|
|
472
|
-
throw new Error(`Template file not found. Tried:
|
|
873
|
+
throw new Error(`Template file not found. Tried:
|
|
473
874
|
` + packageTemplatePaths.map((p) => ` - ${p} (package template)`).join("\n") + `
|
|
474
875
|
- ${localTemplatePath} (local template)`);
|
|
475
|
-
}
|
|
476
|
-
} else {
|
|
477
|
-
templatePath = path.join(process.cwd(), "dist/client/index.html");
|
|
478
|
-
if (!fs.existsSync(templatePath)) {
|
|
479
|
-
throw new Error(`Template file not found at ${templatePath}. Make sure to run the build process first.`);
|
|
480
|
-
}
|
|
481
876
|
}
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
throw new Error(`Failed to read template file at ${templatePath}: ${error.message}`);
|
|
877
|
+
} else {
|
|
878
|
+
templatePath = path.join(process.cwd(), "dist/client/index.html");
|
|
879
|
+
if (!fs.existsSync(templatePath)) {
|
|
880
|
+
throw new Error(`Template file not found at ${templatePath}. Make sure to run the build process first.`);
|
|
487
881
|
}
|
|
488
882
|
}
|
|
489
|
-
|
|
490
|
-
const
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
883
|
+
try {
|
|
884
|
+
const template = fs.readFileSync(templatePath, "utf-8");
|
|
885
|
+
this.logger.log(`\u2713 Loaded template from ${templatePath}`);
|
|
886
|
+
return template;
|
|
887
|
+
} catch (error) {
|
|
888
|
+
throw new Error(`Failed to read template file at ${templatePath}: ${error.message}`);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
loadManifests() {
|
|
892
|
+
const manifestPath = path.join(process.cwd(), "dist/client/.vite/manifest.json");
|
|
893
|
+
if (fs.existsSync(manifestPath)) {
|
|
894
|
+
this.manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
|
|
895
|
+
} else {
|
|
896
|
+
this.logger.warn("\u26A0\uFE0F Client manifest not found. Run `pnpm build:client` first.");
|
|
897
|
+
}
|
|
898
|
+
const serverManifestPath = path.join(process.cwd(), "dist/server/.vite/manifest.json");
|
|
899
|
+
if (fs.existsSync(serverManifestPath)) {
|
|
900
|
+
this.serverManifest = JSON.parse(fs.readFileSync(serverManifestPath, "utf-8"));
|
|
901
|
+
} else {
|
|
902
|
+
this.logger.warn("\u26A0\uFE0F Server manifest not found. Run `pnpm build:server` first.");
|
|
502
903
|
}
|
|
503
904
|
}
|
|
504
905
|
setViteServer(vite) {
|
|
@@ -552,16 +953,50 @@ exports.RenderService = class _RenderService {
|
|
|
552
953
|
}
|
|
553
954
|
/**
|
|
554
955
|
* Main render method that routes to string or stream mode
|
|
956
|
+
*
|
|
957
|
+
* String mode (default):
|
|
958
|
+
* - Returns complete HTML string
|
|
959
|
+
* - Atomic responses - works completely or fails completely
|
|
960
|
+
* - Proper HTTP status codes always
|
|
961
|
+
*
|
|
962
|
+
* Stream mode:
|
|
963
|
+
* - Writes directly to response
|
|
964
|
+
* - Better TTFB, progressive rendering
|
|
965
|
+
* - Requires response object
|
|
555
966
|
*/
|
|
556
967
|
async render(viewComponent, data = {}, res, head) {
|
|
557
968
|
const mergedHead = this.mergeHead(this.defaultHead, head);
|
|
969
|
+
const renderContext = {
|
|
970
|
+
template: this.template,
|
|
971
|
+
vite: this.vite,
|
|
972
|
+
manifest: this.manifest,
|
|
973
|
+
serverManifest: this.serverManifest,
|
|
974
|
+
entryServerPath: this.entryServerPath,
|
|
975
|
+
isDevelopment: this.isDevelopment
|
|
976
|
+
};
|
|
558
977
|
if (this.ssrMode === "stream") {
|
|
559
978
|
if (!res) {
|
|
560
979
|
throw new Error("Response object is required for streaming SSR mode. Pass res as third parameter.");
|
|
561
980
|
}
|
|
562
|
-
return this.
|
|
981
|
+
return this.streamRenderer.render(viewComponent, data, res, renderContext, mergedHead);
|
|
563
982
|
}
|
|
564
|
-
return this.
|
|
983
|
+
return this.stringRenderer.render(viewComponent, data, renderContext, mergedHead);
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* Render a segment for client-side navigation.
|
|
987
|
+
* Always uses string mode (streaming not supported for segments).
|
|
988
|
+
*/
|
|
989
|
+
async renderSegment(viewComponent, data, swapTarget, head) {
|
|
990
|
+
const mergedHead = this.mergeHead(this.defaultHead, head);
|
|
991
|
+
const renderContext = {
|
|
992
|
+
template: this.template,
|
|
993
|
+
vite: this.vite,
|
|
994
|
+
manifest: this.manifest,
|
|
995
|
+
serverManifest: this.serverManifest,
|
|
996
|
+
entryServerPath: this.entryServerPath,
|
|
997
|
+
isDevelopment: this.isDevelopment
|
|
998
|
+
};
|
|
999
|
+
return this.stringRenderer.renderSegment(viewComponent, data, renderContext, swapTarget, mergedHead);
|
|
565
1000
|
}
|
|
566
1001
|
/**
|
|
567
1002
|
* Merge default head with page-specific head
|
|
@@ -574,7 +1009,6 @@ exports.RenderService = class _RenderService {
|
|
|
574
1009
|
return {
|
|
575
1010
|
...defaultHead,
|
|
576
1011
|
...pageHead,
|
|
577
|
-
// Merge arrays (links and meta) instead of replacing
|
|
578
1012
|
links: [
|
|
579
1013
|
...defaultHead?.links || [],
|
|
580
1014
|
...pageHead?.links || []
|
|
@@ -585,198 +1019,8 @@ exports.RenderService = class _RenderService {
|
|
|
585
1019
|
]
|
|
586
1020
|
};
|
|
587
1021
|
}
|
|
588
|
-
/**
|
|
589
|
-
* Traditional string-based SSR using renderToString
|
|
590
|
-
*/
|
|
591
|
-
async renderToString(viewComponent, data = {}, head) {
|
|
592
|
-
const startTime = Date.now();
|
|
593
|
-
try {
|
|
594
|
-
let template = this.template;
|
|
595
|
-
if (this.vite) {
|
|
596
|
-
template = await this.vite.transformIndexHtml("/", template);
|
|
597
|
-
}
|
|
598
|
-
let renderModule;
|
|
599
|
-
if (this.vite) {
|
|
600
|
-
renderModule = await this.vite.ssrLoadModule(this.entryServerPath);
|
|
601
|
-
} else {
|
|
602
|
-
if (this.serverManifest) {
|
|
603
|
-
const manifestEntry = Object.entries(this.serverManifest).find(([key, value]) => value.isEntry && key.includes("entry-server"));
|
|
604
|
-
if (manifestEntry) {
|
|
605
|
-
const [, entry] = manifestEntry;
|
|
606
|
-
const serverPath = path.join(process.cwd(), "dist/server", entry.file);
|
|
607
|
-
renderModule = await import(serverPath);
|
|
608
|
-
} else {
|
|
609
|
-
throw new Error("Server bundle not found in manifest. Run `pnpm build:server` to generate the server bundle.");
|
|
610
|
-
}
|
|
611
|
-
} else {
|
|
612
|
-
throw new Error("Server bundle not found in manifest. Run `pnpm build:server` to generate the server bundle.");
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
const { data: pageData, __context: context, __layouts: layouts } = data;
|
|
616
|
-
const appHtml = await renderModule.renderComponent(viewComponent, data);
|
|
617
|
-
const componentName = viewComponent.displayName || viewComponent.name || "Component";
|
|
618
|
-
const layoutMetadata = layouts ? layouts.map((l) => ({
|
|
619
|
-
name: l.layout.displayName || l.layout.name || "default",
|
|
620
|
-
props: l.props
|
|
621
|
-
})) : [];
|
|
622
|
-
const initialStateScript = `
|
|
623
|
-
<script>
|
|
624
|
-
window.__INITIAL_STATE__ = ${devalue.uneval(pageData)};
|
|
625
|
-
window.__CONTEXT__ = ${devalue.uneval(context)};
|
|
626
|
-
window.__COMPONENT_NAME__ = ${devalue.uneval(componentName)};
|
|
627
|
-
window.__LAYOUTS__ = ${devalue.uneval(layoutMetadata)};
|
|
628
|
-
</script>
|
|
629
|
-
`;
|
|
630
|
-
let clientScript = "";
|
|
631
|
-
let styles = "";
|
|
632
|
-
if (this.vite) {
|
|
633
|
-
clientScript = `<script type="module" src="/src/views/entry-client.tsx"></script>`;
|
|
634
|
-
styles = "";
|
|
635
|
-
} else {
|
|
636
|
-
if (this.manifest) {
|
|
637
|
-
const manifestEntry = Object.entries(this.manifest).find(([key, value]) => value.isEntry && key.includes("entry-client"));
|
|
638
|
-
if (manifestEntry) {
|
|
639
|
-
const [, entry] = manifestEntry;
|
|
640
|
-
const entryFile = entry.file;
|
|
641
|
-
clientScript = `<script type="module" src="/${entryFile}"></script>`;
|
|
642
|
-
if (entry.css) {
|
|
643
|
-
const cssFiles = entry.css;
|
|
644
|
-
styles = cssFiles.map((css) => `<link rel="stylesheet" href="/${css}" />`).join("\n ");
|
|
645
|
-
}
|
|
646
|
-
} else {
|
|
647
|
-
this.logger.error("\u26A0\uFE0F Client entry not found in manifest");
|
|
648
|
-
clientScript = `<script type="module" src="/assets/client.js"></script>`;
|
|
649
|
-
}
|
|
650
|
-
} else {
|
|
651
|
-
this.logger.error("\u26A0\uFE0F Client manifest not found");
|
|
652
|
-
clientScript = `<script type="module" src="/assets/client.js"></script>`;
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
const headTags = this.templateParser.buildHeadTags(head);
|
|
656
|
-
let html = template.replace("<!--app-html-->", appHtml);
|
|
657
|
-
html = html.replace("<!--initial-state-->", initialStateScript);
|
|
658
|
-
html = html.replace("<!--client-scripts-->", clientScript);
|
|
659
|
-
html = html.replace("<!--styles-->", styles);
|
|
660
|
-
html = html.replace("<!--head-meta-->", headTags);
|
|
661
|
-
if (this.isDevelopment) {
|
|
662
|
-
const duration = Date.now() - startTime;
|
|
663
|
-
const componentName2 = typeof viewComponent === "function" ? viewComponent.name : String(viewComponent);
|
|
664
|
-
this.logger.log(`[SSR] ${componentName2} rendered in ${duration}ms (string mode)`);
|
|
665
|
-
}
|
|
666
|
-
return html;
|
|
667
|
-
} catch (error) {
|
|
668
|
-
throw error;
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
/**
|
|
672
|
-
* Modern streaming SSR using renderToPipeableStream
|
|
673
|
-
*/
|
|
674
|
-
async renderToStream(viewComponent, data = {}, res, head) {
|
|
675
|
-
const startTime = Date.now();
|
|
676
|
-
let shellReadyTime = 0;
|
|
677
|
-
return new Promise((resolve, reject) => {
|
|
678
|
-
const executeStream = /* @__PURE__ */ __name(async () => {
|
|
679
|
-
let template = this.template;
|
|
680
|
-
if (this.vite) {
|
|
681
|
-
template = await this.vite.transformIndexHtml("/", template);
|
|
682
|
-
}
|
|
683
|
-
const templateParts = this.templateParser.parseTemplate(template);
|
|
684
|
-
let renderModule;
|
|
685
|
-
if (this.vite) {
|
|
686
|
-
renderModule = await this.vite.ssrLoadModule(this.entryServerPath);
|
|
687
|
-
} else {
|
|
688
|
-
if (this.serverManifest) {
|
|
689
|
-
const manifestEntry = Object.entries(this.serverManifest).find(([key, value]) => value.isEntry && key.includes("entry-server"));
|
|
690
|
-
if (manifestEntry) {
|
|
691
|
-
const [, entry] = manifestEntry;
|
|
692
|
-
const serverPath = path.join(process.cwd(), "dist/server", entry.file);
|
|
693
|
-
renderModule = await import(serverPath);
|
|
694
|
-
} else {
|
|
695
|
-
throw new Error("Server bundle not found in manifest. Run `pnpm build:server` to generate the server bundle.");
|
|
696
|
-
}
|
|
697
|
-
} else {
|
|
698
|
-
throw new Error("Server bundle not found in manifest. Run `pnpm build:server` to generate the server bundle.");
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
const { data: pageData, __context: context, __layouts: layouts } = data;
|
|
702
|
-
const componentName = viewComponent.displayName || viewComponent.name || "Component";
|
|
703
|
-
const inlineScripts = this.templateParser.buildInlineScripts(pageData, context, componentName, layouts);
|
|
704
|
-
const clientScript = this.templateParser.getClientScriptTag(this.isDevelopment, this.manifest);
|
|
705
|
-
const stylesheetTags = this.templateParser.getStylesheetTags(this.isDevelopment, this.manifest);
|
|
706
|
-
const headTags = this.templateParser.buildHeadTags(head);
|
|
707
|
-
let didError = false;
|
|
708
|
-
let shellErrorOccurred = false;
|
|
709
|
-
const { PassThrough } = await import('stream');
|
|
710
|
-
const reactStream = new PassThrough();
|
|
711
|
-
let allReadyFired = false;
|
|
712
|
-
const { pipe, abort } = renderModule.renderComponentStream(viewComponent, data, {
|
|
713
|
-
onShellReady: /* @__PURE__ */ __name(() => {
|
|
714
|
-
shellReadyTime = Date.now();
|
|
715
|
-
if (!res.headersSent) {
|
|
716
|
-
res.statusCode = didError ? 500 : 200;
|
|
717
|
-
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
718
|
-
}
|
|
719
|
-
let htmlStart = templateParts.htmlStart;
|
|
720
|
-
htmlStart = htmlStart.replace("<!--styles-->", stylesheetTags);
|
|
721
|
-
htmlStart = htmlStart.replace("<!--head-meta-->", headTags);
|
|
722
|
-
res.write(htmlStart);
|
|
723
|
-
res.write(templateParts.rootStart);
|
|
724
|
-
pipe(reactStream);
|
|
725
|
-
reactStream.pipe(res, {
|
|
726
|
-
end: false
|
|
727
|
-
});
|
|
728
|
-
if (this.isDevelopment) {
|
|
729
|
-
const ttfb = shellReadyTime - startTime;
|
|
730
|
-
this.logger.log(`[SSR] ${componentName} shell ready in ${ttfb}ms (stream mode - TTFB)`);
|
|
731
|
-
}
|
|
732
|
-
}, "onShellReady"),
|
|
733
|
-
onShellError: /* @__PURE__ */ __name((error) => {
|
|
734
|
-
shellErrorOccurred = true;
|
|
735
|
-
this.streamingErrorHandler.handleShellError(error, res, componentName, this.isDevelopment);
|
|
736
|
-
resolve();
|
|
737
|
-
}, "onShellError"),
|
|
738
|
-
onError: /* @__PURE__ */ __name((error) => {
|
|
739
|
-
didError = true;
|
|
740
|
-
this.streamingErrorHandler.handleStreamError(error, componentName);
|
|
741
|
-
}, "onError"),
|
|
742
|
-
onAllReady: /* @__PURE__ */ __name(() => {
|
|
743
|
-
allReadyFired = true;
|
|
744
|
-
}, "onAllReady")
|
|
745
|
-
});
|
|
746
|
-
reactStream.on("end", () => {
|
|
747
|
-
if (shellErrorOccurred) {
|
|
748
|
-
return;
|
|
749
|
-
}
|
|
750
|
-
res.write(inlineScripts);
|
|
751
|
-
res.write(clientScript);
|
|
752
|
-
res.write(templateParts.rootEnd);
|
|
753
|
-
res.write(templateParts.htmlEnd);
|
|
754
|
-
res.end();
|
|
755
|
-
if (this.isDevelopment) {
|
|
756
|
-
const totalTime = Date.now() - startTime;
|
|
757
|
-
const streamTime = Date.now() - shellReadyTime;
|
|
758
|
-
const viaAllReady = allReadyFired ? " (onAllReady fired)" : " (onAllReady never fired)";
|
|
759
|
-
this.logger.log(`[SSR] ${componentName} streaming complete in ${totalTime}ms total (${streamTime}ms streaming)${viaAllReady}`);
|
|
760
|
-
}
|
|
761
|
-
resolve();
|
|
762
|
-
});
|
|
763
|
-
reactStream.on("error", (error) => {
|
|
764
|
-
reject(error);
|
|
765
|
-
});
|
|
766
|
-
res.on("close", () => {
|
|
767
|
-
abort();
|
|
768
|
-
resolve();
|
|
769
|
-
});
|
|
770
|
-
}, "executeStream");
|
|
771
|
-
executeStream().catch((error) => {
|
|
772
|
-
const componentName = typeof viewComponent === "function" ? viewComponent.name : String(viewComponent);
|
|
773
|
-
this.streamingErrorHandler.handleShellError(error, res, componentName, this.isDevelopment);
|
|
774
|
-
resolve();
|
|
775
|
-
});
|
|
776
|
-
});
|
|
777
|
-
}
|
|
778
1022
|
};
|
|
779
|
-
exports.RenderService =
|
|
1023
|
+
exports.RenderService = _ts_decorate5([
|
|
780
1024
|
common.Injectable(),
|
|
781
1025
|
_ts_param2(2, common.Optional()),
|
|
782
1026
|
_ts_param2(2, common.Inject("SSR_MODE")),
|
|
@@ -784,10 +1028,10 @@ exports.RenderService = _ts_decorate3([
|
|
|
784
1028
|
_ts_param2(3, common.Inject("DEFAULT_HEAD")),
|
|
785
1029
|
_ts_param2(4, common.Optional()),
|
|
786
1030
|
_ts_param2(4, common.Inject("CUSTOM_TEMPLATE")),
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
typeof
|
|
790
|
-
typeof
|
|
1031
|
+
_ts_metadata4("design:type", Function),
|
|
1032
|
+
_ts_metadata4("design:paramtypes", [
|
|
1033
|
+
typeof StringRenderer === "undefined" ? Object : StringRenderer,
|
|
1034
|
+
typeof StreamRenderer === "undefined" ? Object : StreamRenderer,
|
|
791
1035
|
typeof SSRMode === "undefined" ? Object : SSRMode,
|
|
792
1036
|
typeof HeadData === "undefined" ? Object : HeadData,
|
|
793
1037
|
String
|
|
@@ -816,17 +1060,17 @@ function Layout(layout, options) {
|
|
|
816
1060
|
__name(Layout, "Layout");
|
|
817
1061
|
|
|
818
1062
|
// src/render/render.interceptor.ts
|
|
819
|
-
function
|
|
1063
|
+
function _ts_decorate6(decorators, target, key, desc) {
|
|
820
1064
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
821
1065
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
822
1066
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
823
1067
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
824
1068
|
}
|
|
825
|
-
__name(
|
|
826
|
-
function
|
|
1069
|
+
__name(_ts_decorate6, "_ts_decorate");
|
|
1070
|
+
function _ts_metadata5(k, v) {
|
|
827
1071
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
828
1072
|
}
|
|
829
|
-
__name(
|
|
1073
|
+
__name(_ts_metadata5, "_ts_metadata");
|
|
830
1074
|
function _ts_param3(paramIndex, decorator) {
|
|
831
1075
|
return function(target, key) {
|
|
832
1076
|
decorator(target, key, paramIndex);
|
|
@@ -876,15 +1120,20 @@ exports.RenderInterceptor = class RenderInterceptor {
|
|
|
876
1120
|
} else if (renderOptions?.layout === false) {
|
|
877
1121
|
return layouts;
|
|
878
1122
|
}
|
|
879
|
-
if (controllerLayoutMeta) {
|
|
880
|
-
const
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
1123
|
+
if (controllerLayoutMeta?.layout) {
|
|
1124
|
+
const rootLayoutName = rootLayout?.displayName || rootLayout?.name;
|
|
1125
|
+
const controllerLayoutName = controllerLayoutMeta.layout.displayName || controllerLayoutMeta.layout.name;
|
|
1126
|
+
const isDuplicateOfRoot = rootLayout && rootLayoutName && controllerLayoutName === rootLayoutName;
|
|
1127
|
+
if (!isDuplicateOfRoot) {
|
|
1128
|
+
const mergedProps = {
|
|
1129
|
+
...controllerLayoutMeta.options?.props || {},
|
|
1130
|
+
...dynamicLayoutProps || {}
|
|
1131
|
+
};
|
|
1132
|
+
layouts.push({
|
|
1133
|
+
layout: controllerLayoutMeta.layout,
|
|
1134
|
+
props: mergedProps
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
888
1137
|
}
|
|
889
1138
|
if (renderOptions?.layout) {
|
|
890
1139
|
const mergedProps = {
|
|
@@ -898,6 +1147,53 @@ exports.RenderInterceptor = class RenderInterceptor {
|
|
|
898
1147
|
}
|
|
899
1148
|
return layouts;
|
|
900
1149
|
}
|
|
1150
|
+
/**
|
|
1151
|
+
* Detect request type based on headers.
|
|
1152
|
+
* - If X-Current-Layouts header is present, this is a segment request
|
|
1153
|
+
* - Only GET requests can be segments
|
|
1154
|
+
*/
|
|
1155
|
+
detectRequestType(request) {
|
|
1156
|
+
if (request.method !== "GET") {
|
|
1157
|
+
return {
|
|
1158
|
+
type: "full"
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
const layoutsHeader = request.headers["x-current-layouts"];
|
|
1162
|
+
if (layoutsHeader && typeof layoutsHeader === "string") {
|
|
1163
|
+
const currentLayouts = layoutsHeader.split(",").map((s) => s.trim());
|
|
1164
|
+
return {
|
|
1165
|
+
type: "segment",
|
|
1166
|
+
currentLayouts
|
|
1167
|
+
};
|
|
1168
|
+
}
|
|
1169
|
+
return {
|
|
1170
|
+
type: "full"
|
|
1171
|
+
};
|
|
1172
|
+
}
|
|
1173
|
+
/**
|
|
1174
|
+
* Determine swap target by finding deepest common layout.
|
|
1175
|
+
* Returns null if no common ancestor (client should do full navigation).
|
|
1176
|
+
*/
|
|
1177
|
+
determineSwapTarget(currentLayouts, targetLayouts) {
|
|
1178
|
+
const targetNames = targetLayouts.map((l) => l.layout.displayName || l.layout.name);
|
|
1179
|
+
let commonLayout = null;
|
|
1180
|
+
for (let i = 0; i < Math.min(currentLayouts.length, targetNames.length); i++) {
|
|
1181
|
+
if (currentLayouts[i] === targetNames[i]) {
|
|
1182
|
+
commonLayout = currentLayouts[i];
|
|
1183
|
+
} else {
|
|
1184
|
+
break;
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
return commonLayout;
|
|
1188
|
+
}
|
|
1189
|
+
/**
|
|
1190
|
+
* Filter layouts to only include those below the swap target.
|
|
1191
|
+
* The swap target's outlet will contain the filtered layouts.
|
|
1192
|
+
*/
|
|
1193
|
+
filterLayoutsFromSwapTarget(layouts, swapTarget) {
|
|
1194
|
+
const index = layouts.findIndex((l) => (l.layout.displayName || l.layout.name) === swapTarget);
|
|
1195
|
+
return index >= 0 ? layouts.slice(index + 1) : layouts;
|
|
1196
|
+
}
|
|
901
1197
|
intercept(context, next) {
|
|
902
1198
|
const viewPathOrComponent = this.reflector.get(RENDER_KEY, context.getHandler());
|
|
903
1199
|
if (!viewPathOrComponent) {
|
|
@@ -946,6 +1242,24 @@ exports.RenderInterceptor = class RenderInterceptor {
|
|
|
946
1242
|
__context: renderContext,
|
|
947
1243
|
__layouts: layoutChain
|
|
948
1244
|
};
|
|
1245
|
+
const { type, currentLayouts } = this.detectRequestType(request);
|
|
1246
|
+
if (type === "segment" && currentLayouts) {
|
|
1247
|
+
const swapTarget = this.determineSwapTarget(currentLayouts, layoutChain);
|
|
1248
|
+
if (!swapTarget) {
|
|
1249
|
+
response.type("application/json");
|
|
1250
|
+
return {
|
|
1251
|
+
swapTarget: null
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
const filteredLayouts = this.filterLayoutsFromSwapTarget(layoutChain, swapTarget);
|
|
1255
|
+
const segmentData = {
|
|
1256
|
+
...fullData,
|
|
1257
|
+
__layouts: filteredLayouts
|
|
1258
|
+
};
|
|
1259
|
+
const result = await this.renderService.renderSegment(viewPathOrComponent, segmentData, swapTarget, renderResponse.head);
|
|
1260
|
+
response.type("application/json");
|
|
1261
|
+
return result;
|
|
1262
|
+
}
|
|
949
1263
|
try {
|
|
950
1264
|
const html = await this.renderService.render(viewPathOrComponent, fullData, response, renderResponse.head);
|
|
951
1265
|
if (html !== void 0) {
|
|
@@ -959,31 +1273,31 @@ exports.RenderInterceptor = class RenderInterceptor {
|
|
|
959
1273
|
}));
|
|
960
1274
|
}
|
|
961
1275
|
};
|
|
962
|
-
exports.RenderInterceptor =
|
|
1276
|
+
exports.RenderInterceptor = _ts_decorate6([
|
|
963
1277
|
common.Injectable(),
|
|
964
1278
|
_ts_param3(2, common.Optional()),
|
|
965
1279
|
_ts_param3(2, common.Inject("ALLOWED_HEADERS")),
|
|
966
1280
|
_ts_param3(3, common.Optional()),
|
|
967
1281
|
_ts_param3(3, common.Inject("ALLOWED_COOKIES")),
|
|
968
|
-
|
|
969
|
-
|
|
1282
|
+
_ts_metadata5("design:type", Function),
|
|
1283
|
+
_ts_metadata5("design:paramtypes", [
|
|
970
1284
|
typeof core.Reflector === "undefined" ? Object : core.Reflector,
|
|
971
1285
|
typeof exports.RenderService === "undefined" ? Object : exports.RenderService,
|
|
972
1286
|
Array,
|
|
973
1287
|
Array
|
|
974
1288
|
])
|
|
975
1289
|
], exports.RenderInterceptor);
|
|
976
|
-
function
|
|
1290
|
+
function _ts_decorate7(decorators, target, key, desc) {
|
|
977
1291
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
978
1292
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
979
1293
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
980
1294
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
981
1295
|
}
|
|
982
|
-
__name(
|
|
983
|
-
function
|
|
1296
|
+
__name(_ts_decorate7, "_ts_decorate");
|
|
1297
|
+
function _ts_metadata6(k, v) {
|
|
984
1298
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
985
1299
|
}
|
|
986
|
-
__name(
|
|
1300
|
+
__name(_ts_metadata6, "_ts_metadata");
|
|
987
1301
|
function _ts_param4(paramIndex, decorator) {
|
|
988
1302
|
return function(target, key) {
|
|
989
1303
|
decorator(target, key, paramIndex);
|
|
@@ -997,14 +1311,12 @@ var ViteInitializerService = class _ViteInitializerService {
|
|
|
997
1311
|
renderService;
|
|
998
1312
|
httpAdapterHost;
|
|
999
1313
|
logger = new common.Logger(_ViteInitializerService.name);
|
|
1000
|
-
viteMode;
|
|
1001
1314
|
vitePort;
|
|
1002
1315
|
viteServer = null;
|
|
1003
1316
|
isShuttingDown = false;
|
|
1004
1317
|
constructor(renderService, httpAdapterHost, viteConfig) {
|
|
1005
1318
|
this.renderService = renderService;
|
|
1006
1319
|
this.httpAdapterHost = httpAdapterHost;
|
|
1007
|
-
this.viteMode = viteConfig?.mode || "embedded";
|
|
1008
1320
|
this.vitePort = viteConfig?.port || 5173;
|
|
1009
1321
|
this.registerSignalHandlers();
|
|
1010
1322
|
}
|
|
@@ -1036,30 +1348,12 @@ var ViteInitializerService = class _ViteInitializerService {
|
|
|
1036
1348
|
appType: "custom"
|
|
1037
1349
|
});
|
|
1038
1350
|
this.renderService.setViteServer(this.viteServer);
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
} else if (this.viteMode === "proxy") {
|
|
1042
|
-
await this.setupViteProxy();
|
|
1043
|
-
}
|
|
1044
|
-
this.logger.log(`\u2713 Vite initialized for SSR (mode: ${this.viteMode})`);
|
|
1351
|
+
await this.setupViteProxy();
|
|
1352
|
+
this.logger.log("\u2713 Vite initialized for SSR");
|
|
1045
1353
|
} catch (error) {
|
|
1046
1354
|
this.logger.warn(`Failed to initialize Vite: ${error.message}. Make sure vite is installed.`);
|
|
1047
1355
|
}
|
|
1048
1356
|
}
|
|
1049
|
-
async mountViteMiddleware(vite) {
|
|
1050
|
-
try {
|
|
1051
|
-
const httpAdapter = this.httpAdapterHost.httpAdapter;
|
|
1052
|
-
if (!httpAdapter) {
|
|
1053
|
-
this.logger.warn("HTTP adapter not available, skipping Vite middleware setup");
|
|
1054
|
-
return;
|
|
1055
|
-
}
|
|
1056
|
-
const app = httpAdapter.getInstance();
|
|
1057
|
-
app.use(vite.middlewares);
|
|
1058
|
-
this.logger.log(`\u2713 Vite middleware mounted (embedded mode with HMR)`);
|
|
1059
|
-
} catch (error) {
|
|
1060
|
-
this.logger.warn(`Failed to mount Vite middleware: ${error.message}`);
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
1357
|
async setupViteProxy() {
|
|
1064
1358
|
try {
|
|
1065
1359
|
const httpAdapter = this.httpAdapterHost.httpAdapter;
|
|
@@ -1078,7 +1372,7 @@ var ViteInitializerService = class _ViteInitializerService {
|
|
|
1078
1372
|
}, "pathFilter")
|
|
1079
1373
|
});
|
|
1080
1374
|
app.use(viteProxy);
|
|
1081
|
-
this.logger.log(`\u2713 Vite HMR proxy configured (
|
|
1375
|
+
this.logger.log(`\u2713 Vite HMR proxy configured (Vite dev server on port ${this.vitePort})`);
|
|
1082
1376
|
} catch (error) {
|
|
1083
1377
|
this.logger.warn(`Failed to setup Vite proxy: ${error.message}. Make sure http-proxy-middleware is installed.`);
|
|
1084
1378
|
}
|
|
@@ -1129,12 +1423,12 @@ var ViteInitializerService = class _ViteInitializerService {
|
|
|
1129
1423
|
}
|
|
1130
1424
|
}
|
|
1131
1425
|
};
|
|
1132
|
-
ViteInitializerService =
|
|
1426
|
+
ViteInitializerService = _ts_decorate7([
|
|
1133
1427
|
common.Injectable(),
|
|
1134
1428
|
_ts_param4(2, common.Optional()),
|
|
1135
1429
|
_ts_param4(2, common.Inject("VITE_CONFIG")),
|
|
1136
|
-
|
|
1137
|
-
|
|
1430
|
+
_ts_metadata6("design:type", Function),
|
|
1431
|
+
_ts_metadata6("design:paramtypes", [
|
|
1138
1432
|
typeof exports.RenderService === "undefined" ? Object : exports.RenderService,
|
|
1139
1433
|
typeof core.HttpAdapterHost === "undefined" ? Object : core.HttpAdapterHost,
|
|
1140
1434
|
typeof ViteConfig === "undefined" ? Object : ViteConfig
|
|
@@ -1142,13 +1436,13 @@ ViteInitializerService = _ts_decorate5([
|
|
|
1142
1436
|
], ViteInitializerService);
|
|
1143
1437
|
|
|
1144
1438
|
// src/render/render.module.ts
|
|
1145
|
-
function
|
|
1439
|
+
function _ts_decorate8(decorators, target, key, desc) {
|
|
1146
1440
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1147
1441
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1148
1442
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1149
1443
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1150
1444
|
}
|
|
1151
|
-
__name(
|
|
1445
|
+
__name(_ts_decorate8, "_ts_decorate");
|
|
1152
1446
|
exports.RenderModule = class _RenderModule {
|
|
1153
1447
|
static {
|
|
1154
1448
|
__name(this, "RenderModule");
|
|
@@ -1161,17 +1455,17 @@ exports.RenderModule = class _RenderModule {
|
|
|
1161
1455
|
*
|
|
1162
1456
|
* @example
|
|
1163
1457
|
* ```ts
|
|
1164
|
-
* // Zero config - uses
|
|
1458
|
+
* // Zero config - uses string mode (default, recommended)
|
|
1165
1459
|
* RenderModule.forRoot()
|
|
1166
1460
|
*
|
|
1167
|
-
* //
|
|
1168
|
-
* RenderModule.forRoot({ mode: 'stream' })
|
|
1169
|
-
*
|
|
1170
|
-
* // Enable HMR with proxy mode
|
|
1461
|
+
* // Custom Vite port
|
|
1171
1462
|
* RenderModule.forRoot({
|
|
1172
|
-
* vite: {
|
|
1463
|
+
* vite: { port: 3001 }
|
|
1173
1464
|
* })
|
|
1174
1465
|
*
|
|
1466
|
+
* // Enable streaming SSR (advanced - see mode docs for trade-offs)
|
|
1467
|
+
* RenderModule.forRoot({ mode: 'stream' })
|
|
1468
|
+
*
|
|
1175
1469
|
* // Custom error pages
|
|
1176
1470
|
* RenderModule.forRoot({
|
|
1177
1471
|
* errorPageDevelopment: DevErrorPage,
|
|
@@ -1185,6 +1479,8 @@ exports.RenderModule = class _RenderModule {
|
|
|
1185
1479
|
exports.TemplateParserService,
|
|
1186
1480
|
exports.StreamingErrorHandler,
|
|
1187
1481
|
ViteInitializerService,
|
|
1482
|
+
StringRenderer,
|
|
1483
|
+
StreamRenderer,
|
|
1188
1484
|
{
|
|
1189
1485
|
provide: core.APP_INTERCEPTOR,
|
|
1190
1486
|
useClass: exports.RenderInterceptor
|
|
@@ -1289,6 +1585,8 @@ exports.RenderModule = class _RenderModule {
|
|
|
1289
1585
|
exports.TemplateParserService,
|
|
1290
1586
|
exports.StreamingErrorHandler,
|
|
1291
1587
|
ViteInitializerService,
|
|
1588
|
+
StringRenderer,
|
|
1589
|
+
StreamRenderer,
|
|
1292
1590
|
{
|
|
1293
1591
|
provide: core.APP_INTERCEPTOR,
|
|
1294
1592
|
useClass: exports.RenderInterceptor
|
|
@@ -1373,7 +1671,7 @@ exports.RenderModule = class _RenderModule {
|
|
|
1373
1671
|
return this.forRootAsync(options);
|
|
1374
1672
|
}
|
|
1375
1673
|
};
|
|
1376
|
-
exports.RenderModule =
|
|
1674
|
+
exports.RenderModule = _ts_decorate8([
|
|
1377
1675
|
common.Global(),
|
|
1378
1676
|
common.Module({
|
|
1379
1677
|
providers: [
|
|
@@ -1381,6 +1679,8 @@ exports.RenderModule = _ts_decorate6([
|
|
|
1381
1679
|
exports.TemplateParserService,
|
|
1382
1680
|
exports.StreamingErrorHandler,
|
|
1383
1681
|
ViteInitializerService,
|
|
1682
|
+
StringRenderer,
|
|
1683
|
+
StreamRenderer,
|
|
1384
1684
|
{
|
|
1385
1685
|
provide: core.APP_INTERCEPTOR,
|
|
1386
1686
|
useClass: exports.RenderInterceptor
|
|
@@ -1395,8 +1695,25 @@ exports.RenderModule = _ts_decorate6([
|
|
|
1395
1695
|
]
|
|
1396
1696
|
})
|
|
1397
1697
|
], exports.RenderModule);
|
|
1398
|
-
var
|
|
1399
|
-
|
|
1698
|
+
var CONTEXT_KEY = /* @__PURE__ */ Symbol.for("nestjs-ssr.PageContext");
|
|
1699
|
+
var globalStore = globalThis;
|
|
1700
|
+
function getOrCreateContext() {
|
|
1701
|
+
if (!globalStore[CONTEXT_KEY]) {
|
|
1702
|
+
globalStore[CONTEXT_KEY] = /* @__PURE__ */ React.createContext(null);
|
|
1703
|
+
}
|
|
1704
|
+
return globalStore[CONTEXT_KEY];
|
|
1705
|
+
}
|
|
1706
|
+
__name(getOrCreateContext, "getOrCreateContext");
|
|
1707
|
+
var PageContext = getOrCreateContext();
|
|
1708
|
+
function registerPageContextState(setter) {
|
|
1709
|
+
}
|
|
1710
|
+
__name(registerPageContextState, "registerPageContextState");
|
|
1711
|
+
function PageContextProvider({ context: initialContext, children, isSegment = false }) {
|
|
1712
|
+
const [context, setContext] = React.useState(initialContext);
|
|
1713
|
+
React.useEffect(() => {
|
|
1714
|
+
}, [
|
|
1715
|
+
isSegment
|
|
1716
|
+
]);
|
|
1400
1717
|
return /* @__PURE__ */ React__default.default.createElement(PageContext.Provider, {
|
|
1401
1718
|
value: context
|
|
1402
1719
|
}, children);
|
|
@@ -1589,6 +1906,15 @@ function createSSRHooks() {
|
|
|
1589
1906
|
};
|
|
1590
1907
|
}
|
|
1591
1908
|
__name(createSSRHooks, "createSSRHooks");
|
|
1909
|
+
var defaultHooks = createSSRHooks();
|
|
1910
|
+
var usePageContext = defaultHooks.usePageContext;
|
|
1911
|
+
var useParams = defaultHooks.useParams;
|
|
1912
|
+
var useQuery = defaultHooks.useQuery;
|
|
1913
|
+
var useRequest = defaultHooks.useRequest;
|
|
1914
|
+
var useHeaders = defaultHooks.useHeaders;
|
|
1915
|
+
var useHeader = defaultHooks.useHeader;
|
|
1916
|
+
var useCookies = defaultHooks.useCookies;
|
|
1917
|
+
var useCookie = defaultHooks.useCookie;
|
|
1592
1918
|
|
|
1593
1919
|
exports.ErrorPageDevelopment = ErrorPageDevelopment;
|
|
1594
1920
|
exports.ErrorPageProduction = ErrorPageProduction;
|
|
@@ -1596,3 +1922,11 @@ exports.Layout = Layout;
|
|
|
1596
1922
|
exports.PageContextProvider = PageContextProvider;
|
|
1597
1923
|
exports.Render = Render;
|
|
1598
1924
|
exports.createSSRHooks = createSSRHooks;
|
|
1925
|
+
exports.useCookie = useCookie;
|
|
1926
|
+
exports.useCookies = useCookies;
|
|
1927
|
+
exports.useHeader = useHeader;
|
|
1928
|
+
exports.useHeaders = useHeaders;
|
|
1929
|
+
exports.usePageContext = usePageContext;
|
|
1930
|
+
exports.useParams = useParams;
|
|
1931
|
+
exports.useQuery = useQuery;
|
|
1932
|
+
exports.useRequest = useRequest;
|