@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/render/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
|
|
@@ -798,17 +1042,17 @@ var RENDER_OPTIONS_KEY = "render_options";
|
|
|
798
1042
|
var LAYOUT_KEY = "layout";
|
|
799
1043
|
|
|
800
1044
|
// src/render/render.interceptor.ts
|
|
801
|
-
function
|
|
1045
|
+
function _ts_decorate6(decorators, target, key, desc) {
|
|
802
1046
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
803
1047
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
804
1048
|
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;
|
|
805
1049
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
806
1050
|
}
|
|
807
|
-
__name(
|
|
808
|
-
function
|
|
1051
|
+
__name(_ts_decorate6, "_ts_decorate");
|
|
1052
|
+
function _ts_metadata5(k, v) {
|
|
809
1053
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
810
1054
|
}
|
|
811
|
-
__name(
|
|
1055
|
+
__name(_ts_metadata5, "_ts_metadata");
|
|
812
1056
|
function _ts_param3(paramIndex, decorator) {
|
|
813
1057
|
return function(target, key) {
|
|
814
1058
|
decorator(target, key, paramIndex);
|
|
@@ -858,15 +1102,20 @@ exports.RenderInterceptor = class RenderInterceptor {
|
|
|
858
1102
|
} else if (renderOptions?.layout === false) {
|
|
859
1103
|
return layouts;
|
|
860
1104
|
}
|
|
861
|
-
if (controllerLayoutMeta) {
|
|
862
|
-
const
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
1105
|
+
if (controllerLayoutMeta?.layout) {
|
|
1106
|
+
const rootLayoutName = rootLayout?.displayName || rootLayout?.name;
|
|
1107
|
+
const controllerLayoutName = controllerLayoutMeta.layout.displayName || controllerLayoutMeta.layout.name;
|
|
1108
|
+
const isDuplicateOfRoot = rootLayout && rootLayoutName && controllerLayoutName === rootLayoutName;
|
|
1109
|
+
if (!isDuplicateOfRoot) {
|
|
1110
|
+
const mergedProps = {
|
|
1111
|
+
...controllerLayoutMeta.options?.props || {},
|
|
1112
|
+
...dynamicLayoutProps || {}
|
|
1113
|
+
};
|
|
1114
|
+
layouts.push({
|
|
1115
|
+
layout: controllerLayoutMeta.layout,
|
|
1116
|
+
props: mergedProps
|
|
1117
|
+
});
|
|
1118
|
+
}
|
|
870
1119
|
}
|
|
871
1120
|
if (renderOptions?.layout) {
|
|
872
1121
|
const mergedProps = {
|
|
@@ -880,6 +1129,53 @@ exports.RenderInterceptor = class RenderInterceptor {
|
|
|
880
1129
|
}
|
|
881
1130
|
return layouts;
|
|
882
1131
|
}
|
|
1132
|
+
/**
|
|
1133
|
+
* Detect request type based on headers.
|
|
1134
|
+
* - If X-Current-Layouts header is present, this is a segment request
|
|
1135
|
+
* - Only GET requests can be segments
|
|
1136
|
+
*/
|
|
1137
|
+
detectRequestType(request) {
|
|
1138
|
+
if (request.method !== "GET") {
|
|
1139
|
+
return {
|
|
1140
|
+
type: "full"
|
|
1141
|
+
};
|
|
1142
|
+
}
|
|
1143
|
+
const layoutsHeader = request.headers["x-current-layouts"];
|
|
1144
|
+
if (layoutsHeader && typeof layoutsHeader === "string") {
|
|
1145
|
+
const currentLayouts = layoutsHeader.split(",").map((s) => s.trim());
|
|
1146
|
+
return {
|
|
1147
|
+
type: "segment",
|
|
1148
|
+
currentLayouts
|
|
1149
|
+
};
|
|
1150
|
+
}
|
|
1151
|
+
return {
|
|
1152
|
+
type: "full"
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
1155
|
+
/**
|
|
1156
|
+
* Determine swap target by finding deepest common layout.
|
|
1157
|
+
* Returns null if no common ancestor (client should do full navigation).
|
|
1158
|
+
*/
|
|
1159
|
+
determineSwapTarget(currentLayouts, targetLayouts) {
|
|
1160
|
+
const targetNames = targetLayouts.map((l) => l.layout.displayName || l.layout.name);
|
|
1161
|
+
let commonLayout = null;
|
|
1162
|
+
for (let i = 0; i < Math.min(currentLayouts.length, targetNames.length); i++) {
|
|
1163
|
+
if (currentLayouts[i] === targetNames[i]) {
|
|
1164
|
+
commonLayout = currentLayouts[i];
|
|
1165
|
+
} else {
|
|
1166
|
+
break;
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
return commonLayout;
|
|
1170
|
+
}
|
|
1171
|
+
/**
|
|
1172
|
+
* Filter layouts to only include those below the swap target.
|
|
1173
|
+
* The swap target's outlet will contain the filtered layouts.
|
|
1174
|
+
*/
|
|
1175
|
+
filterLayoutsFromSwapTarget(layouts, swapTarget) {
|
|
1176
|
+
const index = layouts.findIndex((l) => (l.layout.displayName || l.layout.name) === swapTarget);
|
|
1177
|
+
return index >= 0 ? layouts.slice(index + 1) : layouts;
|
|
1178
|
+
}
|
|
883
1179
|
intercept(context, next) {
|
|
884
1180
|
const viewPathOrComponent = this.reflector.get(RENDER_KEY, context.getHandler());
|
|
885
1181
|
if (!viewPathOrComponent) {
|
|
@@ -928,6 +1224,24 @@ exports.RenderInterceptor = class RenderInterceptor {
|
|
|
928
1224
|
__context: renderContext,
|
|
929
1225
|
__layouts: layoutChain
|
|
930
1226
|
};
|
|
1227
|
+
const { type, currentLayouts } = this.detectRequestType(request);
|
|
1228
|
+
if (type === "segment" && currentLayouts) {
|
|
1229
|
+
const swapTarget = this.determineSwapTarget(currentLayouts, layoutChain);
|
|
1230
|
+
if (!swapTarget) {
|
|
1231
|
+
response.type("application/json");
|
|
1232
|
+
return {
|
|
1233
|
+
swapTarget: null
|
|
1234
|
+
};
|
|
1235
|
+
}
|
|
1236
|
+
const filteredLayouts = this.filterLayoutsFromSwapTarget(layoutChain, swapTarget);
|
|
1237
|
+
const segmentData = {
|
|
1238
|
+
...fullData,
|
|
1239
|
+
__layouts: filteredLayouts
|
|
1240
|
+
};
|
|
1241
|
+
const result = await this.renderService.renderSegment(viewPathOrComponent, segmentData, swapTarget, renderResponse.head);
|
|
1242
|
+
response.type("application/json");
|
|
1243
|
+
return result;
|
|
1244
|
+
}
|
|
931
1245
|
try {
|
|
932
1246
|
const html = await this.renderService.render(viewPathOrComponent, fullData, response, renderResponse.head);
|
|
933
1247
|
if (html !== void 0) {
|
|
@@ -941,31 +1255,31 @@ exports.RenderInterceptor = class RenderInterceptor {
|
|
|
941
1255
|
}));
|
|
942
1256
|
}
|
|
943
1257
|
};
|
|
944
|
-
exports.RenderInterceptor =
|
|
1258
|
+
exports.RenderInterceptor = _ts_decorate6([
|
|
945
1259
|
common.Injectable(),
|
|
946
1260
|
_ts_param3(2, common.Optional()),
|
|
947
1261
|
_ts_param3(2, common.Inject("ALLOWED_HEADERS")),
|
|
948
1262
|
_ts_param3(3, common.Optional()),
|
|
949
1263
|
_ts_param3(3, common.Inject("ALLOWED_COOKIES")),
|
|
950
|
-
|
|
951
|
-
|
|
1264
|
+
_ts_metadata5("design:type", Function),
|
|
1265
|
+
_ts_metadata5("design:paramtypes", [
|
|
952
1266
|
typeof core.Reflector === "undefined" ? Object : core.Reflector,
|
|
953
1267
|
typeof exports.RenderService === "undefined" ? Object : exports.RenderService,
|
|
954
1268
|
Array,
|
|
955
1269
|
Array
|
|
956
1270
|
])
|
|
957
1271
|
], exports.RenderInterceptor);
|
|
958
|
-
function
|
|
1272
|
+
function _ts_decorate7(decorators, target, key, desc) {
|
|
959
1273
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
960
1274
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
961
1275
|
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;
|
|
962
1276
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
963
1277
|
}
|
|
964
|
-
__name(
|
|
965
|
-
function
|
|
1278
|
+
__name(_ts_decorate7, "_ts_decorate");
|
|
1279
|
+
function _ts_metadata6(k, v) {
|
|
966
1280
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
967
1281
|
}
|
|
968
|
-
__name(
|
|
1282
|
+
__name(_ts_metadata6, "_ts_metadata");
|
|
969
1283
|
function _ts_param4(paramIndex, decorator) {
|
|
970
1284
|
return function(target, key) {
|
|
971
1285
|
decorator(target, key, paramIndex);
|
|
@@ -979,14 +1293,12 @@ var ViteInitializerService = class _ViteInitializerService {
|
|
|
979
1293
|
renderService;
|
|
980
1294
|
httpAdapterHost;
|
|
981
1295
|
logger = new common.Logger(_ViteInitializerService.name);
|
|
982
|
-
viteMode;
|
|
983
1296
|
vitePort;
|
|
984
1297
|
viteServer = null;
|
|
985
1298
|
isShuttingDown = false;
|
|
986
1299
|
constructor(renderService, httpAdapterHost, viteConfig) {
|
|
987
1300
|
this.renderService = renderService;
|
|
988
1301
|
this.httpAdapterHost = httpAdapterHost;
|
|
989
|
-
this.viteMode = viteConfig?.mode || "embedded";
|
|
990
1302
|
this.vitePort = viteConfig?.port || 5173;
|
|
991
1303
|
this.registerSignalHandlers();
|
|
992
1304
|
}
|
|
@@ -1018,30 +1330,12 @@ var ViteInitializerService = class _ViteInitializerService {
|
|
|
1018
1330
|
appType: "custom"
|
|
1019
1331
|
});
|
|
1020
1332
|
this.renderService.setViteServer(this.viteServer);
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
} else if (this.viteMode === "proxy") {
|
|
1024
|
-
await this.setupViteProxy();
|
|
1025
|
-
}
|
|
1026
|
-
this.logger.log(`\u2713 Vite initialized for SSR (mode: ${this.viteMode})`);
|
|
1333
|
+
await this.setupViteProxy();
|
|
1334
|
+
this.logger.log("\u2713 Vite initialized for SSR");
|
|
1027
1335
|
} catch (error) {
|
|
1028
1336
|
this.logger.warn(`Failed to initialize Vite: ${error.message}. Make sure vite is installed.`);
|
|
1029
1337
|
}
|
|
1030
1338
|
}
|
|
1031
|
-
async mountViteMiddleware(vite) {
|
|
1032
|
-
try {
|
|
1033
|
-
const httpAdapter = this.httpAdapterHost.httpAdapter;
|
|
1034
|
-
if (!httpAdapter) {
|
|
1035
|
-
this.logger.warn("HTTP adapter not available, skipping Vite middleware setup");
|
|
1036
|
-
return;
|
|
1037
|
-
}
|
|
1038
|
-
const app = httpAdapter.getInstance();
|
|
1039
|
-
app.use(vite.middlewares);
|
|
1040
|
-
this.logger.log(`\u2713 Vite middleware mounted (embedded mode with HMR)`);
|
|
1041
|
-
} catch (error) {
|
|
1042
|
-
this.logger.warn(`Failed to mount Vite middleware: ${error.message}`);
|
|
1043
|
-
}
|
|
1044
|
-
}
|
|
1045
1339
|
async setupViteProxy() {
|
|
1046
1340
|
try {
|
|
1047
1341
|
const httpAdapter = this.httpAdapterHost.httpAdapter;
|
|
@@ -1060,7 +1354,7 @@ var ViteInitializerService = class _ViteInitializerService {
|
|
|
1060
1354
|
}, "pathFilter")
|
|
1061
1355
|
});
|
|
1062
1356
|
app.use(viteProxy);
|
|
1063
|
-
this.logger.log(`\u2713 Vite HMR proxy configured (
|
|
1357
|
+
this.logger.log(`\u2713 Vite HMR proxy configured (Vite dev server on port ${this.vitePort})`);
|
|
1064
1358
|
} catch (error) {
|
|
1065
1359
|
this.logger.warn(`Failed to setup Vite proxy: ${error.message}. Make sure http-proxy-middleware is installed.`);
|
|
1066
1360
|
}
|
|
@@ -1111,12 +1405,12 @@ var ViteInitializerService = class _ViteInitializerService {
|
|
|
1111
1405
|
}
|
|
1112
1406
|
}
|
|
1113
1407
|
};
|
|
1114
|
-
ViteInitializerService =
|
|
1408
|
+
ViteInitializerService = _ts_decorate7([
|
|
1115
1409
|
common.Injectable(),
|
|
1116
1410
|
_ts_param4(2, common.Optional()),
|
|
1117
1411
|
_ts_param4(2, common.Inject("VITE_CONFIG")),
|
|
1118
|
-
|
|
1119
|
-
|
|
1412
|
+
_ts_metadata6("design:type", Function),
|
|
1413
|
+
_ts_metadata6("design:paramtypes", [
|
|
1120
1414
|
typeof exports.RenderService === "undefined" ? Object : exports.RenderService,
|
|
1121
1415
|
typeof core.HttpAdapterHost === "undefined" ? Object : core.HttpAdapterHost,
|
|
1122
1416
|
typeof ViteConfig === "undefined" ? Object : ViteConfig
|
|
@@ -1124,13 +1418,13 @@ ViteInitializerService = _ts_decorate5([
|
|
|
1124
1418
|
], ViteInitializerService);
|
|
1125
1419
|
|
|
1126
1420
|
// src/render/render.module.ts
|
|
1127
|
-
function
|
|
1421
|
+
function _ts_decorate8(decorators, target, key, desc) {
|
|
1128
1422
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1129
1423
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1130
1424
|
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;
|
|
1131
1425
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1132
1426
|
}
|
|
1133
|
-
__name(
|
|
1427
|
+
__name(_ts_decorate8, "_ts_decorate");
|
|
1134
1428
|
exports.RenderModule = class _RenderModule {
|
|
1135
1429
|
static {
|
|
1136
1430
|
__name(this, "RenderModule");
|
|
@@ -1143,17 +1437,17 @@ exports.RenderModule = class _RenderModule {
|
|
|
1143
1437
|
*
|
|
1144
1438
|
* @example
|
|
1145
1439
|
* ```ts
|
|
1146
|
-
* // Zero config - uses
|
|
1440
|
+
* // Zero config - uses string mode (default, recommended)
|
|
1147
1441
|
* RenderModule.forRoot()
|
|
1148
1442
|
*
|
|
1149
|
-
* //
|
|
1150
|
-
* RenderModule.forRoot({ mode: 'stream' })
|
|
1151
|
-
*
|
|
1152
|
-
* // Enable HMR with proxy mode
|
|
1443
|
+
* // Custom Vite port
|
|
1153
1444
|
* RenderModule.forRoot({
|
|
1154
|
-
* vite: {
|
|
1445
|
+
* vite: { port: 3001 }
|
|
1155
1446
|
* })
|
|
1156
1447
|
*
|
|
1448
|
+
* // Enable streaming SSR (advanced - see mode docs for trade-offs)
|
|
1449
|
+
* RenderModule.forRoot({ mode: 'stream' })
|
|
1450
|
+
*
|
|
1157
1451
|
* // Custom error pages
|
|
1158
1452
|
* RenderModule.forRoot({
|
|
1159
1453
|
* errorPageDevelopment: DevErrorPage,
|
|
@@ -1167,6 +1461,8 @@ exports.RenderModule = class _RenderModule {
|
|
|
1167
1461
|
exports.TemplateParserService,
|
|
1168
1462
|
exports.StreamingErrorHandler,
|
|
1169
1463
|
ViteInitializerService,
|
|
1464
|
+
StringRenderer,
|
|
1465
|
+
StreamRenderer,
|
|
1170
1466
|
{
|
|
1171
1467
|
provide: core.APP_INTERCEPTOR,
|
|
1172
1468
|
useClass: exports.RenderInterceptor
|
|
@@ -1271,6 +1567,8 @@ exports.RenderModule = class _RenderModule {
|
|
|
1271
1567
|
exports.TemplateParserService,
|
|
1272
1568
|
exports.StreamingErrorHandler,
|
|
1273
1569
|
ViteInitializerService,
|
|
1570
|
+
StringRenderer,
|
|
1571
|
+
StreamRenderer,
|
|
1274
1572
|
{
|
|
1275
1573
|
provide: core.APP_INTERCEPTOR,
|
|
1276
1574
|
useClass: exports.RenderInterceptor
|
|
@@ -1355,7 +1653,7 @@ exports.RenderModule = class _RenderModule {
|
|
|
1355
1653
|
return this.forRootAsync(options);
|
|
1356
1654
|
}
|
|
1357
1655
|
};
|
|
1358
|
-
exports.RenderModule =
|
|
1656
|
+
exports.RenderModule = _ts_decorate8([
|
|
1359
1657
|
common.Global(),
|
|
1360
1658
|
common.Module({
|
|
1361
1659
|
providers: [
|
|
@@ -1363,6 +1661,8 @@ exports.RenderModule = _ts_decorate6([
|
|
|
1363
1661
|
exports.TemplateParserService,
|
|
1364
1662
|
exports.StreamingErrorHandler,
|
|
1365
1663
|
ViteInitializerService,
|
|
1664
|
+
StringRenderer,
|
|
1665
|
+
StreamRenderer,
|
|
1366
1666
|
{
|
|
1367
1667
|
provide: core.APP_INTERCEPTOR,
|
|
1368
1668
|
useClass: exports.RenderInterceptor
|