@nestjs-ssr/react 0.2.5 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- 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 +646 -317
- package/dist/index.mjs +640 -319
- package/dist/render/index.d.mts +2 -2
- package/dist/render/index.d.ts +2 -2
- package/dist/render/index.js +610 -315
- package/dist/render/index.mjs +610 -315
- 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 +28 -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 +28 -6
package/dist/render/index.mjs
CHANGED
|
@@ -185,6 +185,153 @@ window.__LAYOUTS__ = ${uneval(layoutMetadata)};
|
|
|
185
185
|
TemplateParserService = _ts_decorate([
|
|
186
186
|
Injectable()
|
|
187
187
|
], TemplateParserService);
|
|
188
|
+
|
|
189
|
+
// src/render/renderers/string-renderer.ts
|
|
190
|
+
function _ts_decorate2(decorators, target, key, desc) {
|
|
191
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
192
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
193
|
+
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;
|
|
194
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
195
|
+
}
|
|
196
|
+
__name(_ts_decorate2, "_ts_decorate");
|
|
197
|
+
function _ts_metadata(k, v) {
|
|
198
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
199
|
+
}
|
|
200
|
+
__name(_ts_metadata, "_ts_metadata");
|
|
201
|
+
var StringRenderer = class _StringRenderer {
|
|
202
|
+
static {
|
|
203
|
+
__name(this, "StringRenderer");
|
|
204
|
+
}
|
|
205
|
+
templateParser;
|
|
206
|
+
logger = new Logger(_StringRenderer.name);
|
|
207
|
+
constructor(templateParser) {
|
|
208
|
+
this.templateParser = templateParser;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Render a React component to a complete HTML string
|
|
212
|
+
*/
|
|
213
|
+
async render(viewComponent, data, context, head) {
|
|
214
|
+
const startTime = Date.now();
|
|
215
|
+
let template = context.template;
|
|
216
|
+
if (context.vite) {
|
|
217
|
+
template = await context.vite.transformIndexHtml("/", template);
|
|
218
|
+
}
|
|
219
|
+
let renderModule;
|
|
220
|
+
if (context.vite) {
|
|
221
|
+
renderModule = await context.vite.ssrLoadModule(context.entryServerPath);
|
|
222
|
+
} else {
|
|
223
|
+
if (context.serverManifest) {
|
|
224
|
+
const manifestEntry = Object.entries(context.serverManifest).find(([key, value]) => value.isEntry && key.includes("entry-server"));
|
|
225
|
+
if (manifestEntry) {
|
|
226
|
+
const [, entry] = manifestEntry;
|
|
227
|
+
const serverPath = `${process.cwd()}/dist/server/${entry.file}`;
|
|
228
|
+
renderModule = await import(serverPath);
|
|
229
|
+
} else {
|
|
230
|
+
throw new Error("Server bundle not found in manifest. Run `pnpm build:server` to generate the server bundle.");
|
|
231
|
+
}
|
|
232
|
+
} else {
|
|
233
|
+
throw new Error("Server bundle not found in manifest. Run `pnpm build:server` to generate the server bundle.");
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
const { data: pageData, __context: pageContext, __layouts: layouts } = data;
|
|
237
|
+
const appHtml = await renderModule.renderComponent(viewComponent, data);
|
|
238
|
+
const componentName = viewComponent.displayName || viewComponent.name || "Component";
|
|
239
|
+
const layoutMetadata = layouts ? layouts.map((l) => ({
|
|
240
|
+
name: l.layout.displayName || l.layout.name || "default",
|
|
241
|
+
props: l.props
|
|
242
|
+
})) : [];
|
|
243
|
+
const initialStateScript = `
|
|
244
|
+
<script>
|
|
245
|
+
window.__INITIAL_STATE__ = ${uneval(pageData)};
|
|
246
|
+
window.__CONTEXT__ = ${uneval(pageContext)};
|
|
247
|
+
window.__COMPONENT_NAME__ = ${uneval(componentName)};
|
|
248
|
+
window.__LAYOUTS__ = ${uneval(layoutMetadata)};
|
|
249
|
+
</script>
|
|
250
|
+
`;
|
|
251
|
+
let clientScript = "";
|
|
252
|
+
let styles = "";
|
|
253
|
+
if (context.vite) {
|
|
254
|
+
clientScript = `<script type="module" src="/src/views/entry-client.tsx"></script>`;
|
|
255
|
+
} else {
|
|
256
|
+
if (context.manifest) {
|
|
257
|
+
const manifestEntry = Object.entries(context.manifest).find(([key, value]) => value.isEntry && key.includes("entry-client"));
|
|
258
|
+
if (manifestEntry) {
|
|
259
|
+
const [, entry] = manifestEntry;
|
|
260
|
+
const entryFile = entry.file;
|
|
261
|
+
clientScript = `<script type="module" src="/${entryFile}"></script>`;
|
|
262
|
+
if (entry.css) {
|
|
263
|
+
const cssFiles = entry.css;
|
|
264
|
+
styles = cssFiles.map((css) => `<link rel="stylesheet" href="/${css}" />`).join("\n ");
|
|
265
|
+
}
|
|
266
|
+
} else {
|
|
267
|
+
this.logger.error("\u26A0\uFE0F Client entry not found in manifest");
|
|
268
|
+
clientScript = `<script type="module" src="/assets/client.js"></script>`;
|
|
269
|
+
}
|
|
270
|
+
} else {
|
|
271
|
+
this.logger.error("\u26A0\uFE0F Client manifest not found");
|
|
272
|
+
clientScript = `<script type="module" src="/assets/client.js"></script>`;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
const headTags = this.templateParser.buildHeadTags(head);
|
|
276
|
+
let html = template.replace("<!--app-html-->", appHtml);
|
|
277
|
+
html = html.replace("<!--initial-state-->", initialStateScript);
|
|
278
|
+
html = html.replace("<!--client-scripts-->", clientScript);
|
|
279
|
+
html = html.replace("<!--styles-->", styles);
|
|
280
|
+
html = html.replace("<!--head-meta-->", headTags);
|
|
281
|
+
if (context.isDevelopment) {
|
|
282
|
+
const duration = Date.now() - startTime;
|
|
283
|
+
const name = typeof viewComponent === "function" ? viewComponent.name : String(viewComponent);
|
|
284
|
+
this.logger.log(`[SSR] ${name} rendered in ${duration}ms (string mode)`);
|
|
285
|
+
}
|
|
286
|
+
return html;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Render a segment for client-side navigation.
|
|
290
|
+
* Returns just the HTML and metadata without the full page template.
|
|
291
|
+
*/
|
|
292
|
+
async renderSegment(viewComponent, data, context, swapTarget, head) {
|
|
293
|
+
const startTime = Date.now();
|
|
294
|
+
let renderModule;
|
|
295
|
+
if (context.vite) {
|
|
296
|
+
renderModule = await context.vite.ssrLoadModule(context.entryServerPath);
|
|
297
|
+
} else {
|
|
298
|
+
if (context.serverManifest) {
|
|
299
|
+
const manifestEntry = Object.entries(context.serverManifest).find(([key, value]) => value.isEntry && key.includes("entry-server"));
|
|
300
|
+
if (manifestEntry) {
|
|
301
|
+
const [, entry] = manifestEntry;
|
|
302
|
+
const serverPath = `${process.cwd()}/dist/server/${entry.file}`;
|
|
303
|
+
renderModule = await import(serverPath);
|
|
304
|
+
} else {
|
|
305
|
+
throw new Error("Server bundle not found in manifest. Run `pnpm build:server` to generate the server bundle.");
|
|
306
|
+
}
|
|
307
|
+
} else {
|
|
308
|
+
throw new Error("Server bundle not found in manifest. Run `pnpm build:server` to generate the server bundle.");
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
const { data: pageData, __context: pageContext } = data;
|
|
312
|
+
const html = await renderModule.renderComponent(viewComponent, data);
|
|
313
|
+
const componentName = viewComponent.displayName || viewComponent.name || "Component";
|
|
314
|
+
if (context.isDevelopment) {
|
|
315
|
+
const duration = Date.now() - startTime;
|
|
316
|
+
this.logger.log(`[SSR] ${componentName} segment rendered in ${duration}ms`);
|
|
317
|
+
}
|
|
318
|
+
return {
|
|
319
|
+
html,
|
|
320
|
+
head,
|
|
321
|
+
props: pageData,
|
|
322
|
+
swapTarget,
|
|
323
|
+
componentName,
|
|
324
|
+
context: pageContext
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
StringRenderer = _ts_decorate2([
|
|
329
|
+
Injectable(),
|
|
330
|
+
_ts_metadata("design:type", Function),
|
|
331
|
+
_ts_metadata("design:paramtypes", [
|
|
332
|
+
typeof TemplateParserService === "undefined" ? Object : TemplateParserService
|
|
333
|
+
])
|
|
334
|
+
], StringRenderer);
|
|
188
335
|
function ErrorPageDevelopment({ error, viewPath, phase }) {
|
|
189
336
|
const stackLines = error.stack ? error.stack.split("\n").slice(1) : [];
|
|
190
337
|
return /* @__PURE__ */ React.createElement("html", {
|
|
@@ -300,17 +447,17 @@ function ErrorPageProduction() {
|
|
|
300
447
|
__name(ErrorPageProduction, "ErrorPageProduction");
|
|
301
448
|
|
|
302
449
|
// src/render/streaming-error-handler.ts
|
|
303
|
-
function
|
|
450
|
+
function _ts_decorate3(decorators, target, key, desc) {
|
|
304
451
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
305
452
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
306
453
|
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;
|
|
307
454
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
308
455
|
}
|
|
309
|
-
__name(
|
|
310
|
-
function
|
|
456
|
+
__name(_ts_decorate3, "_ts_decorate");
|
|
457
|
+
function _ts_metadata2(k, v) {
|
|
311
458
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
312
459
|
}
|
|
313
|
-
__name(
|
|
460
|
+
__name(_ts_metadata2, "_ts_metadata");
|
|
314
461
|
function _ts_param(paramIndex, decorator) {
|
|
315
462
|
return function(target, key) {
|
|
316
463
|
decorator(target, key, paramIndex);
|
|
@@ -334,6 +481,14 @@ var StreamingErrorHandler = class _StreamingErrorHandler {
|
|
|
334
481
|
*/
|
|
335
482
|
handleShellError(error, res, viewPath, isDevelopment) {
|
|
336
483
|
this.logger.error(`Shell error rendering ${viewPath}: ${error.message}`, error.stack);
|
|
484
|
+
if (res.headersSent) {
|
|
485
|
+
this.logger.error(`Cannot send error page for ${viewPath} - headers already sent (streaming started)`);
|
|
486
|
+
if (!res.writableEnded) {
|
|
487
|
+
res.write(this.renderInlineErrorOverlay(error, viewPath, isDevelopment));
|
|
488
|
+
res.end();
|
|
489
|
+
}
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
337
492
|
res.statusCode = 500;
|
|
338
493
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
339
494
|
if (isDevelopment) {
|
|
@@ -369,32 +524,263 @@ var StreamingErrorHandler = class _StreamingErrorHandler {
|
|
|
369
524
|
const element = createElement(ErrorComponent);
|
|
370
525
|
return "<!DOCTYPE html>\n" + renderToStaticMarkup(element);
|
|
371
526
|
}
|
|
527
|
+
/**
|
|
528
|
+
* Render inline error overlay for when headers are already sent
|
|
529
|
+
* This gets injected into the stream to show a visible error UI
|
|
530
|
+
*/
|
|
531
|
+
renderInlineErrorOverlay(error, viewPath, isDevelopment) {
|
|
532
|
+
const errorMessage = escapeHtml(error.message);
|
|
533
|
+
const errorStack = escapeHtml(error.stack || "");
|
|
534
|
+
const escapedViewPath = escapeHtml(viewPath);
|
|
535
|
+
if (isDevelopment) {
|
|
536
|
+
return `
|
|
537
|
+
<div id="ssr-error-overlay" style="
|
|
538
|
+
position: fixed;
|
|
539
|
+
inset: 0;
|
|
540
|
+
z-index: 99999;
|
|
541
|
+
background: rgba(0, 0, 0, 0.85);
|
|
542
|
+
color: #fff;
|
|
543
|
+
font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
|
|
544
|
+
font-size: 14px;
|
|
545
|
+
padding: 32px;
|
|
546
|
+
overflow: auto;
|
|
547
|
+
">
|
|
548
|
+
<div style="max-width: 900px; margin: 0 auto;">
|
|
549
|
+
<div style="display: flex; align-items: center; gap: 12px; margin-bottom: 24px;">
|
|
550
|
+
<span style="font-size: 32px;">\u26A0\uFE0F</span>
|
|
551
|
+
<h1 style="margin: 0; font-size: 24px; font-weight: 600; color: #ff6b6b;">
|
|
552
|
+
SSR Streaming Error
|
|
553
|
+
</h1>
|
|
554
|
+
</div>
|
|
555
|
+
<p style="color: #aaa; margin-bottom: 16px;">
|
|
556
|
+
An error occurred after streaming started in <code style="background: #333; padding: 2px 6px; border-radius: 4px;">${escapedViewPath}</code>
|
|
557
|
+
</p>
|
|
558
|
+
<div style="background: #1a1a1a; border: 1px solid #333; border-radius: 8px; padding: 16px; margin-bottom: 16px;">
|
|
559
|
+
<div style="color: #ff6b6b; font-weight: 600; margin-bottom: 8px;">Error Message:</div>
|
|
560
|
+
<pre style="margin: 0; white-space: pre-wrap; word-break: break-word; color: #fff;">${errorMessage}</pre>
|
|
561
|
+
</div>
|
|
562
|
+
<div style="background: #1a1a1a; border: 1px solid #333; border-radius: 8px; padding: 16px;">
|
|
563
|
+
<div style="color: #888; font-weight: 600; margin-bottom: 8px;">Stack Trace:</div>
|
|
564
|
+
<pre style="margin: 0; white-space: pre-wrap; word-break: break-word; color: #888; font-size: 12px;">${errorStack}</pre>
|
|
565
|
+
</div>
|
|
566
|
+
<button onclick="document.getElementById('ssr-error-overlay').remove()" style="
|
|
567
|
+
margin-top: 24px;
|
|
568
|
+
background: #333;
|
|
569
|
+
color: #fff;
|
|
570
|
+
border: 1px solid #555;
|
|
571
|
+
padding: 8px 16px;
|
|
572
|
+
border-radius: 6px;
|
|
573
|
+
cursor: pointer;
|
|
574
|
+
font-family: inherit;
|
|
575
|
+
">Dismiss</button>
|
|
576
|
+
</div>
|
|
577
|
+
</div>
|
|
578
|
+
<script>console.error('SSR Streaming Error in ${escapedViewPath}:', ${uneval(error.message)});</script>
|
|
579
|
+
`;
|
|
580
|
+
} else {
|
|
581
|
+
return `
|
|
582
|
+
<div id="ssr-error-overlay" style="
|
|
583
|
+
position: fixed;
|
|
584
|
+
inset: 0;
|
|
585
|
+
z-index: 99999;
|
|
586
|
+
background: #fff;
|
|
587
|
+
color: #333;
|
|
588
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
589
|
+
display: flex;
|
|
590
|
+
align-items: center;
|
|
591
|
+
justify-content: center;
|
|
592
|
+
text-align: center;
|
|
593
|
+
">
|
|
594
|
+
<div>
|
|
595
|
+
<h1 style="font-size: 24px; font-weight: 600; margin-bottom: 16px;">Something went wrong</h1>
|
|
596
|
+
<p style="color: #666; margin-bottom: 24px;">We're sorry, but something went wrong. Please try refreshing the page.</p>
|
|
597
|
+
<button onclick="location.reload()" style="
|
|
598
|
+
background: #333;
|
|
599
|
+
color: #fff;
|
|
600
|
+
border: none;
|
|
601
|
+
padding: 12px 24px;
|
|
602
|
+
border-radius: 6px;
|
|
603
|
+
cursor: pointer;
|
|
604
|
+
font-family: inherit;
|
|
605
|
+
font-size: 16px;
|
|
606
|
+
">Refresh Page</button>
|
|
607
|
+
</div>
|
|
608
|
+
</div>
|
|
609
|
+
`;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
372
612
|
};
|
|
373
|
-
StreamingErrorHandler =
|
|
613
|
+
StreamingErrorHandler = _ts_decorate3([
|
|
374
614
|
Injectable(),
|
|
375
615
|
_ts_param(0, Optional()),
|
|
376
616
|
_ts_param(0, Inject("ERROR_PAGE_DEVELOPMENT")),
|
|
377
617
|
_ts_param(1, Optional()),
|
|
378
618
|
_ts_param(1, Inject("ERROR_PAGE_PRODUCTION")),
|
|
379
|
-
|
|
380
|
-
|
|
619
|
+
_ts_metadata2("design:type", Function),
|
|
620
|
+
_ts_metadata2("design:paramtypes", [
|
|
381
621
|
typeof ComponentType === "undefined" ? Object : ComponentType,
|
|
382
622
|
typeof ComponentType === "undefined" ? Object : ComponentType
|
|
383
623
|
])
|
|
384
624
|
], StreamingErrorHandler);
|
|
385
625
|
|
|
626
|
+
// src/render/renderers/stream-renderer.ts
|
|
627
|
+
function _ts_decorate4(decorators, target, key, desc) {
|
|
628
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
629
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
630
|
+
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;
|
|
631
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
632
|
+
}
|
|
633
|
+
__name(_ts_decorate4, "_ts_decorate");
|
|
634
|
+
function _ts_metadata3(k, v) {
|
|
635
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
636
|
+
}
|
|
637
|
+
__name(_ts_metadata3, "_ts_metadata");
|
|
638
|
+
var StreamRenderer = class _StreamRenderer {
|
|
639
|
+
static {
|
|
640
|
+
__name(this, "StreamRenderer");
|
|
641
|
+
}
|
|
642
|
+
templateParser;
|
|
643
|
+
streamingErrorHandler;
|
|
644
|
+
logger = new Logger(_StreamRenderer.name);
|
|
645
|
+
constructor(templateParser, streamingErrorHandler) {
|
|
646
|
+
this.templateParser = templateParser;
|
|
647
|
+
this.streamingErrorHandler = streamingErrorHandler;
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* Render a React component using streaming SSR
|
|
651
|
+
*
|
|
652
|
+
* @param viewComponent - The React component to render
|
|
653
|
+
* @param data - Data to pass to the component
|
|
654
|
+
* @param res - Express response object (required for streaming)
|
|
655
|
+
* @param context - Render context with Vite and manifest info
|
|
656
|
+
* @param head - Head data for SEO tags
|
|
657
|
+
*/
|
|
658
|
+
async render(viewComponent, data, res, context, head) {
|
|
659
|
+
const startTime = Date.now();
|
|
660
|
+
let shellReadyTime = 0;
|
|
661
|
+
return new Promise((resolve, reject) => {
|
|
662
|
+
const executeStream = /* @__PURE__ */ __name(async () => {
|
|
663
|
+
let template = context.template;
|
|
664
|
+
if (context.vite) {
|
|
665
|
+
template = await context.vite.transformIndexHtml("/", template);
|
|
666
|
+
}
|
|
667
|
+
const templateParts = this.templateParser.parseTemplate(template);
|
|
668
|
+
let renderModule;
|
|
669
|
+
if (context.vite) {
|
|
670
|
+
renderModule = await context.vite.ssrLoadModule(context.entryServerPath);
|
|
671
|
+
} else {
|
|
672
|
+
if (context.serverManifest) {
|
|
673
|
+
const manifestEntry = Object.entries(context.serverManifest).find(([key, value]) => value.isEntry && key.includes("entry-server"));
|
|
674
|
+
if (manifestEntry) {
|
|
675
|
+
const [, entry] = manifestEntry;
|
|
676
|
+
const serverPath = `${process.cwd()}/dist/server/${entry.file}`;
|
|
677
|
+
renderModule = await import(serverPath);
|
|
678
|
+
} else {
|
|
679
|
+
throw new Error("Server bundle not found in manifest. Run `pnpm build:server` to generate the server bundle.");
|
|
680
|
+
}
|
|
681
|
+
} else {
|
|
682
|
+
throw new Error("Server bundle not found in manifest. Run `pnpm build:server` to generate the server bundle.");
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
const { data: pageData, __context: pageContext, __layouts: layouts } = data;
|
|
686
|
+
const componentName = viewComponent.displayName || viewComponent.name || "Component";
|
|
687
|
+
const inlineScripts = this.templateParser.buildInlineScripts(pageData, pageContext, componentName, layouts);
|
|
688
|
+
const clientScript = this.templateParser.getClientScriptTag(context.isDevelopment, context.manifest);
|
|
689
|
+
const stylesheetTags = this.templateParser.getStylesheetTags(context.isDevelopment, context.manifest);
|
|
690
|
+
const headTags = this.templateParser.buildHeadTags(head);
|
|
691
|
+
let didError = false;
|
|
692
|
+
let shellErrorOccurred = false;
|
|
693
|
+
const { PassThrough } = await import('stream');
|
|
694
|
+
const reactStream = new PassThrough();
|
|
695
|
+
let allReadyFired = false;
|
|
696
|
+
const { pipe, abort } = renderModule.renderComponentStream(viewComponent, data, {
|
|
697
|
+
onShellReady: /* @__PURE__ */ __name(() => {
|
|
698
|
+
shellReadyTime = Date.now();
|
|
699
|
+
if (!res.headersSent) {
|
|
700
|
+
res.statusCode = didError ? 500 : 200;
|
|
701
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
702
|
+
}
|
|
703
|
+
let htmlStart = templateParts.htmlStart;
|
|
704
|
+
htmlStart = htmlStart.replace("<!--styles-->", stylesheetTags);
|
|
705
|
+
htmlStart = htmlStart.replace("<!--head-meta-->", headTags);
|
|
706
|
+
res.write(htmlStart);
|
|
707
|
+
res.write(templateParts.rootStart);
|
|
708
|
+
pipe(reactStream);
|
|
709
|
+
reactStream.pipe(res, {
|
|
710
|
+
end: false
|
|
711
|
+
});
|
|
712
|
+
if (context.isDevelopment) {
|
|
713
|
+
const ttfb = shellReadyTime - startTime;
|
|
714
|
+
this.logger.log(`[SSR] ${componentName} shell ready in ${ttfb}ms (stream mode - TTFB)`);
|
|
715
|
+
}
|
|
716
|
+
}, "onShellReady"),
|
|
717
|
+
onShellError: /* @__PURE__ */ __name((error) => {
|
|
718
|
+
shellErrorOccurred = true;
|
|
719
|
+
this.streamingErrorHandler.handleShellError(error, res, componentName, context.isDevelopment);
|
|
720
|
+
resolve();
|
|
721
|
+
}, "onShellError"),
|
|
722
|
+
onError: /* @__PURE__ */ __name((error) => {
|
|
723
|
+
didError = true;
|
|
724
|
+
this.streamingErrorHandler.handleStreamError(error, componentName);
|
|
725
|
+
}, "onError"),
|
|
726
|
+
onAllReady: /* @__PURE__ */ __name(() => {
|
|
727
|
+
allReadyFired = true;
|
|
728
|
+
}, "onAllReady")
|
|
729
|
+
});
|
|
730
|
+
reactStream.on("end", () => {
|
|
731
|
+
if (shellErrorOccurred) {
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
res.write(inlineScripts);
|
|
735
|
+
res.write(clientScript);
|
|
736
|
+
res.write(templateParts.rootEnd);
|
|
737
|
+
res.write(templateParts.htmlEnd);
|
|
738
|
+
res.end();
|
|
739
|
+
if (context.isDevelopment) {
|
|
740
|
+
const totalTime = Date.now() - startTime;
|
|
741
|
+
const streamTime = Date.now() - shellReadyTime;
|
|
742
|
+
const viaAllReady = allReadyFired ? " (onAllReady fired)" : " (onAllReady never fired)";
|
|
743
|
+
this.logger.log(`[SSR] ${componentName} streaming complete in ${totalTime}ms total (${streamTime}ms streaming)${viaAllReady}`);
|
|
744
|
+
}
|
|
745
|
+
resolve();
|
|
746
|
+
});
|
|
747
|
+
reactStream.on("error", (error) => {
|
|
748
|
+
reject(error);
|
|
749
|
+
});
|
|
750
|
+
res.on("close", () => {
|
|
751
|
+
abort();
|
|
752
|
+
resolve();
|
|
753
|
+
});
|
|
754
|
+
}, "executeStream");
|
|
755
|
+
executeStream().catch((error) => {
|
|
756
|
+
const componentName = typeof viewComponent === "function" ? viewComponent.name : String(viewComponent);
|
|
757
|
+
this.streamingErrorHandler.handleShellError(error, res, componentName, context.isDevelopment);
|
|
758
|
+
resolve();
|
|
759
|
+
});
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
};
|
|
763
|
+
StreamRenderer = _ts_decorate4([
|
|
764
|
+
Injectable(),
|
|
765
|
+
_ts_metadata3("design:type", Function),
|
|
766
|
+
_ts_metadata3("design:paramtypes", [
|
|
767
|
+
typeof TemplateParserService === "undefined" ? Object : TemplateParserService,
|
|
768
|
+
typeof StreamingErrorHandler === "undefined" ? Object : StreamingErrorHandler
|
|
769
|
+
])
|
|
770
|
+
], StreamRenderer);
|
|
771
|
+
|
|
386
772
|
// src/render/render.service.ts
|
|
387
|
-
function
|
|
773
|
+
function _ts_decorate5(decorators, target, key, desc) {
|
|
388
774
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
389
775
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
390
776
|
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;
|
|
391
777
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
392
778
|
}
|
|
393
|
-
__name(
|
|
394
|
-
function
|
|
779
|
+
__name(_ts_decorate5, "_ts_decorate");
|
|
780
|
+
function _ts_metadata4(k, v) {
|
|
395
781
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
396
782
|
}
|
|
397
|
-
__name(
|
|
783
|
+
__name(_ts_metadata4, "_ts_metadata");
|
|
398
784
|
function _ts_param2(paramIndex, decorator) {
|
|
399
785
|
return function(target, key) {
|
|
400
786
|
decorator(target, key, paramIndex);
|
|
@@ -405,8 +791,8 @@ var RenderService = class _RenderService {
|
|
|
405
791
|
static {
|
|
406
792
|
__name(this, "RenderService");
|
|
407
793
|
}
|
|
408
|
-
|
|
409
|
-
|
|
794
|
+
stringRenderer;
|
|
795
|
+
streamRenderer;
|
|
410
796
|
defaultHead;
|
|
411
797
|
logger = new Logger(_RenderService.name);
|
|
412
798
|
vite = null;
|
|
@@ -418,12 +804,12 @@ var RenderService = class _RenderService {
|
|
|
418
804
|
entryServerPath;
|
|
419
805
|
rootLayout = void 0;
|
|
420
806
|
rootLayoutChecked = false;
|
|
421
|
-
constructor(
|
|
422
|
-
this.
|
|
423
|
-
this.
|
|
807
|
+
constructor(stringRenderer, streamRenderer, ssrMode, defaultHead, customTemplate) {
|
|
808
|
+
this.stringRenderer = stringRenderer;
|
|
809
|
+
this.streamRenderer = streamRenderer;
|
|
424
810
|
this.defaultHead = defaultHead;
|
|
425
811
|
this.isDevelopment = process.env.NODE_ENV !== "production";
|
|
426
|
-
this.ssrMode = ssrMode || process.env.SSR_MODE || "
|
|
812
|
+
this.ssrMode = ssrMode || process.env.SSR_MODE || "string";
|
|
427
813
|
const absoluteServerPath = join(__dirname, "/templates/entry-server.tsx");
|
|
428
814
|
const relativeServerPath = relative(process.cwd(), absoluteServerPath);
|
|
429
815
|
if (relativeServerPath.startsWith("..")) {
|
|
@@ -431,67 +817,82 @@ var RenderService = class _RenderService {
|
|
|
431
817
|
} else {
|
|
432
818
|
this.entryServerPath = "/" + relativeServerPath.replace(/\\/g, "/");
|
|
433
819
|
}
|
|
820
|
+
this.template = this.loadTemplate(customTemplate);
|
|
821
|
+
if (!this.isDevelopment) {
|
|
822
|
+
this.loadManifests();
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
/**
|
|
826
|
+
* Load HTML template from custom path, package, or local location
|
|
827
|
+
*/
|
|
828
|
+
loadTemplate(customTemplate) {
|
|
434
829
|
if (customTemplate) {
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
830
|
+
return this.loadCustomTemplate(customTemplate);
|
|
831
|
+
}
|
|
832
|
+
return this.loadDefaultTemplate();
|
|
833
|
+
}
|
|
834
|
+
loadCustomTemplate(customTemplate) {
|
|
835
|
+
if (customTemplate.includes("<!DOCTYPE") || customTemplate.includes("<html")) {
|
|
836
|
+
this.logger.log(`\u2713 Loaded custom template (inline)`);
|
|
837
|
+
return customTemplate;
|
|
838
|
+
}
|
|
839
|
+
const customTemplatePath = customTemplate.startsWith("/") ? customTemplate : join(process.cwd(), customTemplate);
|
|
840
|
+
if (!existsSync(customTemplatePath)) {
|
|
841
|
+
throw new Error(`Custom template file not found at ${customTemplatePath}`);
|
|
842
|
+
}
|
|
843
|
+
try {
|
|
844
|
+
const template = readFileSync(customTemplatePath, "utf-8");
|
|
845
|
+
this.logger.log(`\u2713 Loaded custom template from ${customTemplatePath}`);
|
|
846
|
+
return template;
|
|
847
|
+
} catch (error) {
|
|
848
|
+
throw new Error(`Failed to read custom template file at ${customTemplatePath}: ${error.message}`);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
loadDefaultTemplate() {
|
|
852
|
+
let templatePath;
|
|
853
|
+
if (this.isDevelopment) {
|
|
854
|
+
const packageTemplatePaths = [
|
|
855
|
+
join(__dirname, "../templates/index.html"),
|
|
856
|
+
join(__dirname, "../src/templates/index.html"),
|
|
857
|
+
join(__dirname, "../../src/templates/index.html")
|
|
858
|
+
];
|
|
859
|
+
const localTemplatePath = join(process.cwd(), "src/views/index.html");
|
|
860
|
+
const foundPackageTemplate = packageTemplatePaths.find((p) => existsSync(p));
|
|
861
|
+
if (foundPackageTemplate) {
|
|
862
|
+
templatePath = foundPackageTemplate;
|
|
863
|
+
} else if (existsSync(localTemplatePath)) {
|
|
864
|
+
templatePath = localTemplatePath;
|
|
438
865
|
} else {
|
|
439
|
-
|
|
440
|
-
if (!existsSync(customTemplatePath)) {
|
|
441
|
-
throw new Error(`Custom template file not found at ${customTemplatePath}`);
|
|
442
|
-
}
|
|
443
|
-
try {
|
|
444
|
-
this.template = readFileSync(customTemplatePath, "utf-8");
|
|
445
|
-
this.logger.log(`\u2713 Loaded custom template from ${customTemplatePath}`);
|
|
446
|
-
} catch (error) {
|
|
447
|
-
throw new Error(`Failed to read custom template file at ${customTemplatePath}: ${error.message}`);
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
} else {
|
|
451
|
-
let templatePath;
|
|
452
|
-
if (this.isDevelopment) {
|
|
453
|
-
const packageTemplatePaths = [
|
|
454
|
-
join(__dirname, "../templates/index.html"),
|
|
455
|
-
join(__dirname, "../src/templates/index.html"),
|
|
456
|
-
join(__dirname, "../../src/templates/index.html")
|
|
457
|
-
];
|
|
458
|
-
const localTemplatePath = join(process.cwd(), "src/views/index.html");
|
|
459
|
-
const foundPackageTemplate = packageTemplatePaths.find((p) => existsSync(p));
|
|
460
|
-
if (foundPackageTemplate) {
|
|
461
|
-
templatePath = foundPackageTemplate;
|
|
462
|
-
} else if (existsSync(localTemplatePath)) {
|
|
463
|
-
templatePath = localTemplatePath;
|
|
464
|
-
} else {
|
|
465
|
-
throw new Error(`Template file not found. Tried:
|
|
866
|
+
throw new Error(`Template file not found. Tried:
|
|
466
867
|
` + packageTemplatePaths.map((p) => ` - ${p} (package template)`).join("\n") + `
|
|
467
868
|
- ${localTemplatePath} (local template)`);
|
|
468
|
-
}
|
|
469
|
-
} else {
|
|
470
|
-
templatePath = join(process.cwd(), "dist/client/index.html");
|
|
471
|
-
if (!existsSync(templatePath)) {
|
|
472
|
-
throw new Error(`Template file not found at ${templatePath}. Make sure to run the build process first.`);
|
|
473
|
-
}
|
|
474
869
|
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
throw new Error(`Failed to read template file at ${templatePath}: ${error.message}`);
|
|
870
|
+
} else {
|
|
871
|
+
templatePath = join(process.cwd(), "dist/client/index.html");
|
|
872
|
+
if (!existsSync(templatePath)) {
|
|
873
|
+
throw new Error(`Template file not found at ${templatePath}. Make sure to run the build process first.`);
|
|
480
874
|
}
|
|
481
875
|
}
|
|
482
|
-
|
|
483
|
-
const
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
876
|
+
try {
|
|
877
|
+
const template = readFileSync(templatePath, "utf-8");
|
|
878
|
+
this.logger.log(`\u2713 Loaded template from ${templatePath}`);
|
|
879
|
+
return template;
|
|
880
|
+
} catch (error) {
|
|
881
|
+
throw new Error(`Failed to read template file at ${templatePath}: ${error.message}`);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
loadManifests() {
|
|
885
|
+
const manifestPath = join(process.cwd(), "dist/client/.vite/manifest.json");
|
|
886
|
+
if (existsSync(manifestPath)) {
|
|
887
|
+
this.manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
888
|
+
} else {
|
|
889
|
+
this.logger.warn("\u26A0\uFE0F Client manifest not found. Run `pnpm build:client` first.");
|
|
890
|
+
}
|
|
891
|
+
const serverManifestPath = join(process.cwd(), "dist/server/.vite/manifest.json");
|
|
892
|
+
if (existsSync(serverManifestPath)) {
|
|
893
|
+
this.serverManifest = JSON.parse(readFileSync(serverManifestPath, "utf-8"));
|
|
894
|
+
} else {
|
|
895
|
+
this.logger.warn("\u26A0\uFE0F Server manifest not found. Run `pnpm build:server` first.");
|
|
495
896
|
}
|
|
496
897
|
}
|
|
497
898
|
setViteServer(vite) {
|
|
@@ -545,16 +946,50 @@ var RenderService = class _RenderService {
|
|
|
545
946
|
}
|
|
546
947
|
/**
|
|
547
948
|
* Main render method that routes to string or stream mode
|
|
949
|
+
*
|
|
950
|
+
* String mode (default):
|
|
951
|
+
* - Returns complete HTML string
|
|
952
|
+
* - Atomic responses - works completely or fails completely
|
|
953
|
+
* - Proper HTTP status codes always
|
|
954
|
+
*
|
|
955
|
+
* Stream mode:
|
|
956
|
+
* - Writes directly to response
|
|
957
|
+
* - Better TTFB, progressive rendering
|
|
958
|
+
* - Requires response object
|
|
548
959
|
*/
|
|
549
960
|
async render(viewComponent, data = {}, res, head) {
|
|
550
961
|
const mergedHead = this.mergeHead(this.defaultHead, head);
|
|
962
|
+
const renderContext = {
|
|
963
|
+
template: this.template,
|
|
964
|
+
vite: this.vite,
|
|
965
|
+
manifest: this.manifest,
|
|
966
|
+
serverManifest: this.serverManifest,
|
|
967
|
+
entryServerPath: this.entryServerPath,
|
|
968
|
+
isDevelopment: this.isDevelopment
|
|
969
|
+
};
|
|
551
970
|
if (this.ssrMode === "stream") {
|
|
552
971
|
if (!res) {
|
|
553
972
|
throw new Error("Response object is required for streaming SSR mode. Pass res as third parameter.");
|
|
554
973
|
}
|
|
555
|
-
return this.
|
|
974
|
+
return this.streamRenderer.render(viewComponent, data, res, renderContext, mergedHead);
|
|
556
975
|
}
|
|
557
|
-
return this.
|
|
976
|
+
return this.stringRenderer.render(viewComponent, data, renderContext, mergedHead);
|
|
977
|
+
}
|
|
978
|
+
/**
|
|
979
|
+
* Render a segment for client-side navigation.
|
|
980
|
+
* Always uses string mode (streaming not supported for segments).
|
|
981
|
+
*/
|
|
982
|
+
async renderSegment(viewComponent, data, swapTarget, head) {
|
|
983
|
+
const mergedHead = this.mergeHead(this.defaultHead, head);
|
|
984
|
+
const renderContext = {
|
|
985
|
+
template: this.template,
|
|
986
|
+
vite: this.vite,
|
|
987
|
+
manifest: this.manifest,
|
|
988
|
+
serverManifest: this.serverManifest,
|
|
989
|
+
entryServerPath: this.entryServerPath,
|
|
990
|
+
isDevelopment: this.isDevelopment
|
|
991
|
+
};
|
|
992
|
+
return this.stringRenderer.renderSegment(viewComponent, data, renderContext, swapTarget, mergedHead);
|
|
558
993
|
}
|
|
559
994
|
/**
|
|
560
995
|
* Merge default head with page-specific head
|
|
@@ -567,7 +1002,6 @@ var RenderService = class _RenderService {
|
|
|
567
1002
|
return {
|
|
568
1003
|
...defaultHead,
|
|
569
1004
|
...pageHead,
|
|
570
|
-
// Merge arrays (links and meta) instead of replacing
|
|
571
1005
|
links: [
|
|
572
1006
|
...defaultHead?.links || [],
|
|
573
1007
|
...pageHead?.links || []
|
|
@@ -578,198 +1012,8 @@ var RenderService = class _RenderService {
|
|
|
578
1012
|
]
|
|
579
1013
|
};
|
|
580
1014
|
}
|
|
581
|
-
/**
|
|
582
|
-
* Traditional string-based SSR using renderToString
|
|
583
|
-
*/
|
|
584
|
-
async renderToString(viewComponent, data = {}, head) {
|
|
585
|
-
const startTime = Date.now();
|
|
586
|
-
try {
|
|
587
|
-
let template = this.template;
|
|
588
|
-
if (this.vite) {
|
|
589
|
-
template = await this.vite.transformIndexHtml("/", template);
|
|
590
|
-
}
|
|
591
|
-
let renderModule;
|
|
592
|
-
if (this.vite) {
|
|
593
|
-
renderModule = await this.vite.ssrLoadModule(this.entryServerPath);
|
|
594
|
-
} else {
|
|
595
|
-
if (this.serverManifest) {
|
|
596
|
-
const manifestEntry = Object.entries(this.serverManifest).find(([key, value]) => value.isEntry && key.includes("entry-server"));
|
|
597
|
-
if (manifestEntry) {
|
|
598
|
-
const [, entry] = manifestEntry;
|
|
599
|
-
const serverPath = join(process.cwd(), "dist/server", entry.file);
|
|
600
|
-
renderModule = await import(serverPath);
|
|
601
|
-
} else {
|
|
602
|
-
throw new Error("Server bundle not found in manifest. Run `pnpm build:server` to generate the server bundle.");
|
|
603
|
-
}
|
|
604
|
-
} else {
|
|
605
|
-
throw new Error("Server bundle not found in manifest. Run `pnpm build:server` to generate the server bundle.");
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
const { data: pageData, __context: context, __layouts: layouts } = data;
|
|
609
|
-
const appHtml = await renderModule.renderComponent(viewComponent, data);
|
|
610
|
-
const componentName = viewComponent.displayName || viewComponent.name || "Component";
|
|
611
|
-
const layoutMetadata = layouts ? layouts.map((l) => ({
|
|
612
|
-
name: l.layout.displayName || l.layout.name || "default",
|
|
613
|
-
props: l.props
|
|
614
|
-
})) : [];
|
|
615
|
-
const initialStateScript = `
|
|
616
|
-
<script>
|
|
617
|
-
window.__INITIAL_STATE__ = ${uneval(pageData)};
|
|
618
|
-
window.__CONTEXT__ = ${uneval(context)};
|
|
619
|
-
window.__COMPONENT_NAME__ = ${uneval(componentName)};
|
|
620
|
-
window.__LAYOUTS__ = ${uneval(layoutMetadata)};
|
|
621
|
-
</script>
|
|
622
|
-
`;
|
|
623
|
-
let clientScript = "";
|
|
624
|
-
let styles = "";
|
|
625
|
-
if (this.vite) {
|
|
626
|
-
clientScript = `<script type="module" src="/src/views/entry-client.tsx"></script>`;
|
|
627
|
-
styles = "";
|
|
628
|
-
} else {
|
|
629
|
-
if (this.manifest) {
|
|
630
|
-
const manifestEntry = Object.entries(this.manifest).find(([key, value]) => value.isEntry && key.includes("entry-client"));
|
|
631
|
-
if (manifestEntry) {
|
|
632
|
-
const [, entry] = manifestEntry;
|
|
633
|
-
const entryFile = entry.file;
|
|
634
|
-
clientScript = `<script type="module" src="/${entryFile}"></script>`;
|
|
635
|
-
if (entry.css) {
|
|
636
|
-
const cssFiles = entry.css;
|
|
637
|
-
styles = cssFiles.map((css) => `<link rel="stylesheet" href="/${css}" />`).join("\n ");
|
|
638
|
-
}
|
|
639
|
-
} else {
|
|
640
|
-
this.logger.error("\u26A0\uFE0F Client entry not found in manifest");
|
|
641
|
-
clientScript = `<script type="module" src="/assets/client.js"></script>`;
|
|
642
|
-
}
|
|
643
|
-
} else {
|
|
644
|
-
this.logger.error("\u26A0\uFE0F Client manifest not found");
|
|
645
|
-
clientScript = `<script type="module" src="/assets/client.js"></script>`;
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
const headTags = this.templateParser.buildHeadTags(head);
|
|
649
|
-
let html = template.replace("<!--app-html-->", appHtml);
|
|
650
|
-
html = html.replace("<!--initial-state-->", initialStateScript);
|
|
651
|
-
html = html.replace("<!--client-scripts-->", clientScript);
|
|
652
|
-
html = html.replace("<!--styles-->", styles);
|
|
653
|
-
html = html.replace("<!--head-meta-->", headTags);
|
|
654
|
-
if (this.isDevelopment) {
|
|
655
|
-
const duration = Date.now() - startTime;
|
|
656
|
-
const componentName2 = typeof viewComponent === "function" ? viewComponent.name : String(viewComponent);
|
|
657
|
-
this.logger.log(`[SSR] ${componentName2} rendered in ${duration}ms (string mode)`);
|
|
658
|
-
}
|
|
659
|
-
return html;
|
|
660
|
-
} catch (error) {
|
|
661
|
-
throw error;
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
/**
|
|
665
|
-
* Modern streaming SSR using renderToPipeableStream
|
|
666
|
-
*/
|
|
667
|
-
async renderToStream(viewComponent, data = {}, res, head) {
|
|
668
|
-
const startTime = Date.now();
|
|
669
|
-
let shellReadyTime = 0;
|
|
670
|
-
return new Promise((resolve, reject) => {
|
|
671
|
-
const executeStream = /* @__PURE__ */ __name(async () => {
|
|
672
|
-
let template = this.template;
|
|
673
|
-
if (this.vite) {
|
|
674
|
-
template = await this.vite.transformIndexHtml("/", template);
|
|
675
|
-
}
|
|
676
|
-
const templateParts = this.templateParser.parseTemplate(template);
|
|
677
|
-
let renderModule;
|
|
678
|
-
if (this.vite) {
|
|
679
|
-
renderModule = await this.vite.ssrLoadModule(this.entryServerPath);
|
|
680
|
-
} else {
|
|
681
|
-
if (this.serverManifest) {
|
|
682
|
-
const manifestEntry = Object.entries(this.serverManifest).find(([key, value]) => value.isEntry && key.includes("entry-server"));
|
|
683
|
-
if (manifestEntry) {
|
|
684
|
-
const [, entry] = manifestEntry;
|
|
685
|
-
const serverPath = join(process.cwd(), "dist/server", entry.file);
|
|
686
|
-
renderModule = await import(serverPath);
|
|
687
|
-
} else {
|
|
688
|
-
throw new Error("Server bundle not found in manifest. Run `pnpm build:server` to generate the server bundle.");
|
|
689
|
-
}
|
|
690
|
-
} else {
|
|
691
|
-
throw new Error("Server bundle not found in manifest. Run `pnpm build:server` to generate the server bundle.");
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
const { data: pageData, __context: context, __layouts: layouts } = data;
|
|
695
|
-
const componentName = viewComponent.displayName || viewComponent.name || "Component";
|
|
696
|
-
const inlineScripts = this.templateParser.buildInlineScripts(pageData, context, componentName, layouts);
|
|
697
|
-
const clientScript = this.templateParser.getClientScriptTag(this.isDevelopment, this.manifest);
|
|
698
|
-
const stylesheetTags = this.templateParser.getStylesheetTags(this.isDevelopment, this.manifest);
|
|
699
|
-
const headTags = this.templateParser.buildHeadTags(head);
|
|
700
|
-
let didError = false;
|
|
701
|
-
let shellErrorOccurred = false;
|
|
702
|
-
const { PassThrough } = await import('stream');
|
|
703
|
-
const reactStream = new PassThrough();
|
|
704
|
-
let allReadyFired = false;
|
|
705
|
-
const { pipe, abort } = renderModule.renderComponentStream(viewComponent, data, {
|
|
706
|
-
onShellReady: /* @__PURE__ */ __name(() => {
|
|
707
|
-
shellReadyTime = Date.now();
|
|
708
|
-
if (!res.headersSent) {
|
|
709
|
-
res.statusCode = didError ? 500 : 200;
|
|
710
|
-
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
711
|
-
}
|
|
712
|
-
let htmlStart = templateParts.htmlStart;
|
|
713
|
-
htmlStart = htmlStart.replace("<!--styles-->", stylesheetTags);
|
|
714
|
-
htmlStart = htmlStart.replace("<!--head-meta-->", headTags);
|
|
715
|
-
res.write(htmlStart);
|
|
716
|
-
res.write(templateParts.rootStart);
|
|
717
|
-
pipe(reactStream);
|
|
718
|
-
reactStream.pipe(res, {
|
|
719
|
-
end: false
|
|
720
|
-
});
|
|
721
|
-
if (this.isDevelopment) {
|
|
722
|
-
const ttfb = shellReadyTime - startTime;
|
|
723
|
-
this.logger.log(`[SSR] ${componentName} shell ready in ${ttfb}ms (stream mode - TTFB)`);
|
|
724
|
-
}
|
|
725
|
-
}, "onShellReady"),
|
|
726
|
-
onShellError: /* @__PURE__ */ __name((error) => {
|
|
727
|
-
shellErrorOccurred = true;
|
|
728
|
-
this.streamingErrorHandler.handleShellError(error, res, componentName, this.isDevelopment);
|
|
729
|
-
resolve();
|
|
730
|
-
}, "onShellError"),
|
|
731
|
-
onError: /* @__PURE__ */ __name((error) => {
|
|
732
|
-
didError = true;
|
|
733
|
-
this.streamingErrorHandler.handleStreamError(error, componentName);
|
|
734
|
-
}, "onError"),
|
|
735
|
-
onAllReady: /* @__PURE__ */ __name(() => {
|
|
736
|
-
allReadyFired = true;
|
|
737
|
-
}, "onAllReady")
|
|
738
|
-
});
|
|
739
|
-
reactStream.on("end", () => {
|
|
740
|
-
if (shellErrorOccurred) {
|
|
741
|
-
return;
|
|
742
|
-
}
|
|
743
|
-
res.write(inlineScripts);
|
|
744
|
-
res.write(clientScript);
|
|
745
|
-
res.write(templateParts.rootEnd);
|
|
746
|
-
res.write(templateParts.htmlEnd);
|
|
747
|
-
res.end();
|
|
748
|
-
if (this.isDevelopment) {
|
|
749
|
-
const totalTime = Date.now() - startTime;
|
|
750
|
-
const streamTime = Date.now() - shellReadyTime;
|
|
751
|
-
const viaAllReady = allReadyFired ? " (onAllReady fired)" : " (onAllReady never fired)";
|
|
752
|
-
this.logger.log(`[SSR] ${componentName} streaming complete in ${totalTime}ms total (${streamTime}ms streaming)${viaAllReady}`);
|
|
753
|
-
}
|
|
754
|
-
resolve();
|
|
755
|
-
});
|
|
756
|
-
reactStream.on("error", (error) => {
|
|
757
|
-
reject(error);
|
|
758
|
-
});
|
|
759
|
-
res.on("close", () => {
|
|
760
|
-
abort();
|
|
761
|
-
resolve();
|
|
762
|
-
});
|
|
763
|
-
}, "executeStream");
|
|
764
|
-
executeStream().catch((error) => {
|
|
765
|
-
const componentName = typeof viewComponent === "function" ? viewComponent.name : String(viewComponent);
|
|
766
|
-
this.streamingErrorHandler.handleShellError(error, res, componentName, this.isDevelopment);
|
|
767
|
-
resolve();
|
|
768
|
-
});
|
|
769
|
-
});
|
|
770
|
-
}
|
|
771
1015
|
};
|
|
772
|
-
RenderService =
|
|
1016
|
+
RenderService = _ts_decorate5([
|
|
773
1017
|
Injectable(),
|
|
774
1018
|
_ts_param2(2, Optional()),
|
|
775
1019
|
_ts_param2(2, Inject("SSR_MODE")),
|
|
@@ -777,10 +1021,10 @@ RenderService = _ts_decorate3([
|
|
|
777
1021
|
_ts_param2(3, Inject("DEFAULT_HEAD")),
|
|
778
1022
|
_ts_param2(4, Optional()),
|
|
779
1023
|
_ts_param2(4, Inject("CUSTOM_TEMPLATE")),
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
typeof
|
|
783
|
-
typeof
|
|
1024
|
+
_ts_metadata4("design:type", Function),
|
|
1025
|
+
_ts_metadata4("design:paramtypes", [
|
|
1026
|
+
typeof StringRenderer === "undefined" ? Object : StringRenderer,
|
|
1027
|
+
typeof StreamRenderer === "undefined" ? Object : StreamRenderer,
|
|
784
1028
|
typeof SSRMode === "undefined" ? Object : SSRMode,
|
|
785
1029
|
typeof HeadData === "undefined" ? Object : HeadData,
|
|
786
1030
|
String
|
|
@@ -791,17 +1035,17 @@ var RENDER_OPTIONS_KEY = "render_options";
|
|
|
791
1035
|
var LAYOUT_KEY = "layout";
|
|
792
1036
|
|
|
793
1037
|
// src/render/render.interceptor.ts
|
|
794
|
-
function
|
|
1038
|
+
function _ts_decorate6(decorators, target, key, desc) {
|
|
795
1039
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
796
1040
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
797
1041
|
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;
|
|
798
1042
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
799
1043
|
}
|
|
800
|
-
__name(
|
|
801
|
-
function
|
|
1044
|
+
__name(_ts_decorate6, "_ts_decorate");
|
|
1045
|
+
function _ts_metadata5(k, v) {
|
|
802
1046
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
803
1047
|
}
|
|
804
|
-
__name(
|
|
1048
|
+
__name(_ts_metadata5, "_ts_metadata");
|
|
805
1049
|
function _ts_param3(paramIndex, decorator) {
|
|
806
1050
|
return function(target, key) {
|
|
807
1051
|
decorator(target, key, paramIndex);
|
|
@@ -873,6 +1117,53 @@ var RenderInterceptor = class {
|
|
|
873
1117
|
}
|
|
874
1118
|
return layouts;
|
|
875
1119
|
}
|
|
1120
|
+
/**
|
|
1121
|
+
* Detect request type based on headers.
|
|
1122
|
+
* - If X-Current-Layouts header is present, this is a segment request
|
|
1123
|
+
* - Only GET requests can be segments
|
|
1124
|
+
*/
|
|
1125
|
+
detectRequestType(request) {
|
|
1126
|
+
if (request.method !== "GET") {
|
|
1127
|
+
return {
|
|
1128
|
+
type: "full"
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
const layoutsHeader = request.headers["x-current-layouts"];
|
|
1132
|
+
if (layoutsHeader && typeof layoutsHeader === "string") {
|
|
1133
|
+
const currentLayouts = layoutsHeader.split(",").map((s) => s.trim());
|
|
1134
|
+
return {
|
|
1135
|
+
type: "segment",
|
|
1136
|
+
currentLayouts
|
|
1137
|
+
};
|
|
1138
|
+
}
|
|
1139
|
+
return {
|
|
1140
|
+
type: "full"
|
|
1141
|
+
};
|
|
1142
|
+
}
|
|
1143
|
+
/**
|
|
1144
|
+
* Determine swap target by finding deepest common layout.
|
|
1145
|
+
* Returns null if no common ancestor (client should do full navigation).
|
|
1146
|
+
*/
|
|
1147
|
+
determineSwapTarget(currentLayouts, targetLayouts) {
|
|
1148
|
+
const targetNames = targetLayouts.map((l) => l.layout.displayName || l.layout.name);
|
|
1149
|
+
let commonLayout = null;
|
|
1150
|
+
for (let i = 0; i < Math.min(currentLayouts.length, targetNames.length); i++) {
|
|
1151
|
+
if (currentLayouts[i] === targetNames[i]) {
|
|
1152
|
+
commonLayout = currentLayouts[i];
|
|
1153
|
+
} else {
|
|
1154
|
+
break;
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
return commonLayout;
|
|
1158
|
+
}
|
|
1159
|
+
/**
|
|
1160
|
+
* Filter layouts to only include those below the swap target.
|
|
1161
|
+
* The swap target's outlet will contain the filtered layouts.
|
|
1162
|
+
*/
|
|
1163
|
+
filterLayoutsFromSwapTarget(layouts, swapTarget) {
|
|
1164
|
+
const index = layouts.findIndex((l) => (l.layout.displayName || l.layout.name) === swapTarget);
|
|
1165
|
+
return index >= 0 ? layouts.slice(index + 1) : layouts;
|
|
1166
|
+
}
|
|
876
1167
|
intercept(context, next) {
|
|
877
1168
|
const viewPathOrComponent = this.reflector.get(RENDER_KEY, context.getHandler());
|
|
878
1169
|
if (!viewPathOrComponent) {
|
|
@@ -921,6 +1212,24 @@ var RenderInterceptor = class {
|
|
|
921
1212
|
__context: renderContext,
|
|
922
1213
|
__layouts: layoutChain
|
|
923
1214
|
};
|
|
1215
|
+
const { type, currentLayouts } = this.detectRequestType(request);
|
|
1216
|
+
if (type === "segment" && currentLayouts) {
|
|
1217
|
+
const swapTarget = this.determineSwapTarget(currentLayouts, layoutChain);
|
|
1218
|
+
if (!swapTarget) {
|
|
1219
|
+
response.type("application/json");
|
|
1220
|
+
return {
|
|
1221
|
+
swapTarget: null
|
|
1222
|
+
};
|
|
1223
|
+
}
|
|
1224
|
+
const filteredLayouts = this.filterLayoutsFromSwapTarget(layoutChain, swapTarget);
|
|
1225
|
+
const segmentData = {
|
|
1226
|
+
...fullData,
|
|
1227
|
+
__layouts: filteredLayouts
|
|
1228
|
+
};
|
|
1229
|
+
const result = await this.renderService.renderSegment(viewPathOrComponent, segmentData, swapTarget, renderResponse.head);
|
|
1230
|
+
response.type("application/json");
|
|
1231
|
+
return result;
|
|
1232
|
+
}
|
|
924
1233
|
try {
|
|
925
1234
|
const html = await this.renderService.render(viewPathOrComponent, fullData, response, renderResponse.head);
|
|
926
1235
|
if (html !== void 0) {
|
|
@@ -934,31 +1243,31 @@ var RenderInterceptor = class {
|
|
|
934
1243
|
}));
|
|
935
1244
|
}
|
|
936
1245
|
};
|
|
937
|
-
RenderInterceptor =
|
|
1246
|
+
RenderInterceptor = _ts_decorate6([
|
|
938
1247
|
Injectable(),
|
|
939
1248
|
_ts_param3(2, Optional()),
|
|
940
1249
|
_ts_param3(2, Inject("ALLOWED_HEADERS")),
|
|
941
1250
|
_ts_param3(3, Optional()),
|
|
942
1251
|
_ts_param3(3, Inject("ALLOWED_COOKIES")),
|
|
943
|
-
|
|
944
|
-
|
|
1252
|
+
_ts_metadata5("design:type", Function),
|
|
1253
|
+
_ts_metadata5("design:paramtypes", [
|
|
945
1254
|
typeof Reflector === "undefined" ? Object : Reflector,
|
|
946
1255
|
typeof RenderService === "undefined" ? Object : RenderService,
|
|
947
1256
|
Array,
|
|
948
1257
|
Array
|
|
949
1258
|
])
|
|
950
1259
|
], RenderInterceptor);
|
|
951
|
-
function
|
|
1260
|
+
function _ts_decorate7(decorators, target, key, desc) {
|
|
952
1261
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
953
1262
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
954
1263
|
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;
|
|
955
1264
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
956
1265
|
}
|
|
957
|
-
__name(
|
|
958
|
-
function
|
|
1266
|
+
__name(_ts_decorate7, "_ts_decorate");
|
|
1267
|
+
function _ts_metadata6(k, v) {
|
|
959
1268
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
960
1269
|
}
|
|
961
|
-
__name(
|
|
1270
|
+
__name(_ts_metadata6, "_ts_metadata");
|
|
962
1271
|
function _ts_param4(paramIndex, decorator) {
|
|
963
1272
|
return function(target, key) {
|
|
964
1273
|
decorator(target, key, paramIndex);
|
|
@@ -972,14 +1281,12 @@ var ViteInitializerService = class _ViteInitializerService {
|
|
|
972
1281
|
renderService;
|
|
973
1282
|
httpAdapterHost;
|
|
974
1283
|
logger = new Logger(_ViteInitializerService.name);
|
|
975
|
-
viteMode;
|
|
976
1284
|
vitePort;
|
|
977
1285
|
viteServer = null;
|
|
978
1286
|
isShuttingDown = false;
|
|
979
1287
|
constructor(renderService, httpAdapterHost, viteConfig) {
|
|
980
1288
|
this.renderService = renderService;
|
|
981
1289
|
this.httpAdapterHost = httpAdapterHost;
|
|
982
|
-
this.viteMode = viteConfig?.mode || "embedded";
|
|
983
1290
|
this.vitePort = viteConfig?.port || 5173;
|
|
984
1291
|
this.registerSignalHandlers();
|
|
985
1292
|
}
|
|
@@ -1011,30 +1318,12 @@ var ViteInitializerService = class _ViteInitializerService {
|
|
|
1011
1318
|
appType: "custom"
|
|
1012
1319
|
});
|
|
1013
1320
|
this.renderService.setViteServer(this.viteServer);
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
} else if (this.viteMode === "proxy") {
|
|
1017
|
-
await this.setupViteProxy();
|
|
1018
|
-
}
|
|
1019
|
-
this.logger.log(`\u2713 Vite initialized for SSR (mode: ${this.viteMode})`);
|
|
1321
|
+
await this.setupViteProxy();
|
|
1322
|
+
this.logger.log("\u2713 Vite initialized for SSR");
|
|
1020
1323
|
} catch (error) {
|
|
1021
1324
|
this.logger.warn(`Failed to initialize Vite: ${error.message}. Make sure vite is installed.`);
|
|
1022
1325
|
}
|
|
1023
1326
|
}
|
|
1024
|
-
async mountViteMiddleware(vite) {
|
|
1025
|
-
try {
|
|
1026
|
-
const httpAdapter = this.httpAdapterHost.httpAdapter;
|
|
1027
|
-
if (!httpAdapter) {
|
|
1028
|
-
this.logger.warn("HTTP adapter not available, skipping Vite middleware setup");
|
|
1029
|
-
return;
|
|
1030
|
-
}
|
|
1031
|
-
const app = httpAdapter.getInstance();
|
|
1032
|
-
app.use(vite.middlewares);
|
|
1033
|
-
this.logger.log(`\u2713 Vite middleware mounted (embedded mode with HMR)`);
|
|
1034
|
-
} catch (error) {
|
|
1035
|
-
this.logger.warn(`Failed to mount Vite middleware: ${error.message}`);
|
|
1036
|
-
}
|
|
1037
|
-
}
|
|
1038
1327
|
async setupViteProxy() {
|
|
1039
1328
|
try {
|
|
1040
1329
|
const httpAdapter = this.httpAdapterHost.httpAdapter;
|
|
@@ -1053,7 +1342,7 @@ var ViteInitializerService = class _ViteInitializerService {
|
|
|
1053
1342
|
}, "pathFilter")
|
|
1054
1343
|
});
|
|
1055
1344
|
app.use(viteProxy);
|
|
1056
|
-
this.logger.log(`\u2713 Vite HMR proxy configured (
|
|
1345
|
+
this.logger.log(`\u2713 Vite HMR proxy configured (Vite dev server on port ${this.vitePort})`);
|
|
1057
1346
|
} catch (error) {
|
|
1058
1347
|
this.logger.warn(`Failed to setup Vite proxy: ${error.message}. Make sure http-proxy-middleware is installed.`);
|
|
1059
1348
|
}
|
|
@@ -1104,12 +1393,12 @@ var ViteInitializerService = class _ViteInitializerService {
|
|
|
1104
1393
|
}
|
|
1105
1394
|
}
|
|
1106
1395
|
};
|
|
1107
|
-
ViteInitializerService =
|
|
1396
|
+
ViteInitializerService = _ts_decorate7([
|
|
1108
1397
|
Injectable(),
|
|
1109
1398
|
_ts_param4(2, Optional()),
|
|
1110
1399
|
_ts_param4(2, Inject("VITE_CONFIG")),
|
|
1111
|
-
|
|
1112
|
-
|
|
1400
|
+
_ts_metadata6("design:type", Function),
|
|
1401
|
+
_ts_metadata6("design:paramtypes", [
|
|
1113
1402
|
typeof RenderService === "undefined" ? Object : RenderService,
|
|
1114
1403
|
typeof HttpAdapterHost === "undefined" ? Object : HttpAdapterHost,
|
|
1115
1404
|
typeof ViteConfig === "undefined" ? Object : ViteConfig
|
|
@@ -1117,13 +1406,13 @@ ViteInitializerService = _ts_decorate5([
|
|
|
1117
1406
|
], ViteInitializerService);
|
|
1118
1407
|
|
|
1119
1408
|
// src/render/render.module.ts
|
|
1120
|
-
function
|
|
1409
|
+
function _ts_decorate8(decorators, target, key, desc) {
|
|
1121
1410
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1122
1411
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1123
1412
|
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;
|
|
1124
1413
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1125
1414
|
}
|
|
1126
|
-
__name(
|
|
1415
|
+
__name(_ts_decorate8, "_ts_decorate");
|
|
1127
1416
|
var RenderModule = class _RenderModule {
|
|
1128
1417
|
static {
|
|
1129
1418
|
__name(this, "RenderModule");
|
|
@@ -1136,17 +1425,17 @@ var RenderModule = class _RenderModule {
|
|
|
1136
1425
|
*
|
|
1137
1426
|
* @example
|
|
1138
1427
|
* ```ts
|
|
1139
|
-
* // Zero config - uses
|
|
1428
|
+
* // Zero config - uses string mode (default, recommended)
|
|
1140
1429
|
* RenderModule.forRoot()
|
|
1141
1430
|
*
|
|
1142
|
-
* //
|
|
1143
|
-
* RenderModule.forRoot({ mode: 'stream' })
|
|
1144
|
-
*
|
|
1145
|
-
* // Enable HMR with proxy mode
|
|
1431
|
+
* // Custom Vite port
|
|
1146
1432
|
* RenderModule.forRoot({
|
|
1147
|
-
* vite: {
|
|
1433
|
+
* vite: { port: 3001 }
|
|
1148
1434
|
* })
|
|
1149
1435
|
*
|
|
1436
|
+
* // Enable streaming SSR (advanced - see mode docs for trade-offs)
|
|
1437
|
+
* RenderModule.forRoot({ mode: 'stream' })
|
|
1438
|
+
*
|
|
1150
1439
|
* // Custom error pages
|
|
1151
1440
|
* RenderModule.forRoot({
|
|
1152
1441
|
* errorPageDevelopment: DevErrorPage,
|
|
@@ -1160,6 +1449,8 @@ var RenderModule = class _RenderModule {
|
|
|
1160
1449
|
TemplateParserService,
|
|
1161
1450
|
StreamingErrorHandler,
|
|
1162
1451
|
ViteInitializerService,
|
|
1452
|
+
StringRenderer,
|
|
1453
|
+
StreamRenderer,
|
|
1163
1454
|
{
|
|
1164
1455
|
provide: APP_INTERCEPTOR,
|
|
1165
1456
|
useClass: RenderInterceptor
|
|
@@ -1264,6 +1555,8 @@ var RenderModule = class _RenderModule {
|
|
|
1264
1555
|
TemplateParserService,
|
|
1265
1556
|
StreamingErrorHandler,
|
|
1266
1557
|
ViteInitializerService,
|
|
1558
|
+
StringRenderer,
|
|
1559
|
+
StreamRenderer,
|
|
1267
1560
|
{
|
|
1268
1561
|
provide: APP_INTERCEPTOR,
|
|
1269
1562
|
useClass: RenderInterceptor
|
|
@@ -1348,7 +1641,7 @@ var RenderModule = class _RenderModule {
|
|
|
1348
1641
|
return this.forRootAsync(options);
|
|
1349
1642
|
}
|
|
1350
1643
|
};
|
|
1351
|
-
RenderModule =
|
|
1644
|
+
RenderModule = _ts_decorate8([
|
|
1352
1645
|
Global(),
|
|
1353
1646
|
Module({
|
|
1354
1647
|
providers: [
|
|
@@ -1356,6 +1649,8 @@ RenderModule = _ts_decorate6([
|
|
|
1356
1649
|
TemplateParserService,
|
|
1357
1650
|
StreamingErrorHandler,
|
|
1358
1651
|
ViteInitializerService,
|
|
1652
|
+
StringRenderer,
|
|
1653
|
+
StreamRenderer,
|
|
1359
1654
|
{
|
|
1360
1655
|
provide: APP_INTERCEPTOR,
|
|
1361
1656
|
useClass: RenderInterceptor
|