@stratal/inertia 0.0.23 → 0.0.24
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 +33 -1
- package/dist/index.d.mts +36 -14
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +93 -51
- package/dist/index.mjs.map +1 -1
- package/dist/quarry.d.mts +8 -0
- package/dist/quarry.d.mts.map +1 -1
- package/dist/quarry.mjs +53 -6
- package/dist/quarry.mjs.map +1 -1
- package/dist/react.d.mts +2 -1
- package/dist/react.d.mts.map +1 -1
- package/dist/ssr.d.mts +65 -0
- package/dist/ssr.d.mts.map +1 -0
- package/dist/ssr.mjs +56 -0
- package/dist/ssr.mjs.map +1 -0
- package/dist/types-BhgXhWx6.d.mts +82 -0
- package/dist/types-BhgXhWx6.d.mts.map +1 -0
- package/dist/types-DzE1pdZs.d.mts +76 -0
- package/dist/types-DzE1pdZs.d.mts.map +1 -0
- package/package.json +7 -3
- package/dist/types--_iJ04lT.d.mts +0 -148
- package/dist/types--_iJ04lT.d.mts.map +0 -1
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ Inertia.js v3 server adapter for [Stratal](https://github.com/strataljs/stratal)
|
|
|
16
16
|
## Features
|
|
17
17
|
|
|
18
18
|
- **InertiaModule** — Drop-in Stratal module with `forRoot()` / `forRootAsync()` configuration
|
|
19
|
-
- **
|
|
19
|
+
- **Streaming SSR** — React 19 `renderToReadableStream` streaming via `createInertiaSsrApp`, with configurable per-route disabling
|
|
20
20
|
- **Shared Data** — Global shared props with static values or request-scoped resolvers
|
|
21
21
|
- **@InertiaRoute Decorator** — Convention-based Inertia page routes with auto-applied response schema
|
|
22
22
|
- **Partial Reloads** — Optional, deferred, and merge props for efficient data loading
|
|
@@ -82,6 +82,38 @@ export class NotesController {
|
|
|
82
82
|
}
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
+
### Streaming SSR
|
|
86
|
+
|
|
87
|
+
Enable SSR by pointing the module at a bundle that exports a streaming `render`:
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
InertiaModule.forRoot({
|
|
91
|
+
rootView: 'app',
|
|
92
|
+
ssr: { bundle: () => import('./inertia/ssr') },
|
|
93
|
+
})
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`src/inertia/ssr.tsx` (scaffolded by `quarry inertia:install`) uses
|
|
97
|
+
`createInertiaSsrApp`, which wires Inertia's `App`, head collection, and React 19's
|
|
98
|
+
`renderToReadableStream` — the shell flushes early and the body streams progressively:
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
import { createInertiaSsrApp } from '@stratal/inertia/ssr'
|
|
102
|
+
|
|
103
|
+
export const { render } = createInertiaSsrApp({
|
|
104
|
+
resolve: async (name) => {
|
|
105
|
+
const pages = import.meta.glob('./pages/**/*.tsx')
|
|
106
|
+
const page = await pages[`./pages/${name}.tsx`]?.()
|
|
107
|
+
if (!page) throw new Error(`Page not found: ${name}`)
|
|
108
|
+
return page
|
|
109
|
+
},
|
|
110
|
+
})
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
There is no client-side fallback — an SSR failure surfaces as an error rather than
|
|
114
|
+
silently degrading. Skip SSR per route with `ctx.withoutSsr()` or globally with
|
|
115
|
+
`ssr.disabled: ['admin/*']`.
|
|
116
|
+
|
|
85
117
|
## Documentation
|
|
86
118
|
|
|
87
119
|
Full guides and examples are available at **[stratal.dev](https://stratal.dev)**.
|
package/dist/index.d.mts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/// <reference path="../global.d.ts" />
|
|
2
|
-
import {
|
|
2
|
+
import { a as SeoTagDescriptor, i as SeoOpenGraph, n as SeoLinkTag, o as SeoTwitter, r as SeoMetaTag, t as SeoData } from "./types-DzE1pdZs.mjs";
|
|
3
|
+
import { _ as ResolvedInertiaPageProps, a as InertiaMergeProp, b as ViteManifestEntry, c as InertiaOptionalProp, d as InertiaPageRegistry, f as InertiaRenderOptions, g as InertiaTranslationKeys, h as InertiaSsrResult, i as InertiaI18nConfig, l as InertiaPage, m as InertiaSsrBundle, n as InertiaDeferredProp, o as InertiaMergeStrategy, p as InertiaSharedProps, r as InertiaFullPageProps, s as InertiaOnceProp, t as InertiaAlwaysProp, u as InertiaPageComponent, v as SharedDataResolver, y as ViteManifest } from "./types-BhgXhWx6.mjs";
|
|
3
4
|
import { ExceptionHandler } from "stratal/errors";
|
|
4
5
|
import { AsyncModuleOptions, DynamicModule, OnException, OnInitialize } from "stratal/module";
|
|
5
6
|
import { Middleware, Next, RouteConfig, RouteConfigurable, Router, RouterContext } from "stratal/router";
|
|
6
7
|
import { Container } from "stratal/di";
|
|
7
8
|
import { MessageKeyPrefix } from "stratal/i18n";
|
|
8
|
-
import { LoggerService } from "stratal/logger";
|
|
9
9
|
import { z } from "stratal/validation";
|
|
10
|
-
import {
|
|
10
|
+
import { Page } from "@inertiajs/core";
|
|
11
11
|
import { CookieOptions } from "hono/utils/cookie";
|
|
12
12
|
|
|
13
13
|
//#region src/flash/flash-store.d.ts
|
|
@@ -18,12 +18,9 @@ interface FlashStore {
|
|
|
18
18
|
}
|
|
19
19
|
//#endregion
|
|
20
20
|
//#region src/inertia.options.d.ts
|
|
21
|
-
interface SsrBundleModule {
|
|
22
|
-
render(page: Page): Promise<InertiaAppSSRResponse>;
|
|
23
|
-
}
|
|
24
21
|
interface InertiaSsrOptions {
|
|
25
|
-
bundle: () => Promise<
|
|
26
|
-
default:
|
|
22
|
+
bundle: () => Promise<InertiaSsrBundle | {
|
|
23
|
+
default: InertiaSsrBundle;
|
|
27
24
|
}>;
|
|
28
25
|
/**
|
|
29
26
|
* Route patterns where SSR is disabled (e.g., `"admin/*"`).
|
|
@@ -353,11 +350,17 @@ declare class SeoService {
|
|
|
353
350
|
//#region src/services/ssr-renderer.service.d.ts
|
|
354
351
|
declare class SsrRendererService {
|
|
355
352
|
private readonly options;
|
|
356
|
-
private readonly logger;
|
|
357
353
|
private bundle;
|
|
358
354
|
private loadPromise;
|
|
359
|
-
constructor(options: InertiaModuleOptions
|
|
360
|
-
|
|
355
|
+
constructor(options: InertiaModuleOptions);
|
|
356
|
+
/**
|
|
357
|
+
* Render a page to a streaming SSR result.
|
|
358
|
+
*
|
|
359
|
+
* The SSR bundle is imported once per worker (memoized). Bundle-load and render
|
|
360
|
+
* errors propagate — there is no silent client-side fallback. Callers must only
|
|
361
|
+
* invoke this when `options.ssr` is configured.
|
|
362
|
+
*/
|
|
363
|
+
render(page: Page): Promise<InertiaSsrResult>;
|
|
361
364
|
private ensureBundle;
|
|
362
365
|
private loadBundle;
|
|
363
366
|
}
|
|
@@ -367,18 +370,37 @@ declare class ManifestService {
|
|
|
367
370
|
private readonly manifest;
|
|
368
371
|
private readonly entryClientPath;
|
|
369
372
|
private readonly isDev;
|
|
373
|
+
private headTags;
|
|
374
|
+
private scriptTags;
|
|
370
375
|
constructor(options: InertiaModuleOptions);
|
|
371
376
|
getHeadTags(): string;
|
|
372
377
|
getScriptTags(): string;
|
|
378
|
+
private buildHeadTags;
|
|
379
|
+
private buildScriptTags;
|
|
373
380
|
}
|
|
374
381
|
//#endregion
|
|
375
382
|
//#region src/services/template.service.d.ts
|
|
376
383
|
declare class TemplateService {
|
|
377
|
-
private readonly options;
|
|
378
384
|
private readonly manifest;
|
|
385
|
+
private readonly pre;
|
|
386
|
+
private readonly post;
|
|
379
387
|
constructor(options: InertiaModuleOptions, manifest: ManifestService);
|
|
380
|
-
|
|
381
|
-
|
|
388
|
+
/**
|
|
389
|
+
* Compose the streamed HTML response: the document shell (head + opening
|
|
390
|
+
* `#app` wrapper) is flushed first, the React stream is piped verbatim, then
|
|
391
|
+
* the wrapper is closed and the trailing scripts are appended.
|
|
392
|
+
*
|
|
393
|
+
* Reproduces Inertia's `buildSSRBody` markup: a `<script data-page>` JSON tag
|
|
394
|
+
* (parsed before hydration) followed by `<div data-server-rendered id="app">`.
|
|
395
|
+
*/
|
|
396
|
+
renderStream(page: Page, head: string[], reactStream: ReadableStream<Uint8Array>): ReadableStream<Uint8Array>;
|
|
397
|
+
/**
|
|
398
|
+
* Buffered, client-only document used when SSR is disabled for the request.
|
|
399
|
+
* Emits an empty `#app` div for the client bundle to hydrate.
|
|
400
|
+
*/
|
|
401
|
+
renderClientOnly(page: Page, head: string[]): string;
|
|
402
|
+
private fillPlaceholders;
|
|
403
|
+
private serialize;
|
|
382
404
|
}
|
|
383
405
|
//#endregion
|
|
384
406
|
//#region src/services/inertia.service.d.ts
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/flash/flash-store.ts","../src/inertia.options.ts","../src/inertia.module.ts","../src/inertia.tokens.ts","../src/seo/build-seo-tags.ts","../src/flash/cookie-flash-store.ts","../src/augment/router-context.ts","../src/services/hreflang.service.ts","../src/services/seo.service.ts","../src/services/ssr-renderer.service.ts","../src/services/manifest.service.ts","../src/services/template.service.ts","../src/services/inertia.service.ts","../src/decorators/inertia.decorators.ts","../src/middleware/handle-precognitive-requests.middleware.ts","../src/middleware/inertia.middleware.ts","../src/augment/router-variables.ts"],"mappings":";;;;;;;;;;;;UAEiB,UAAA;EACf,IAAA,CAAK,GAAA,EAAK,aAAA,GAAgB,OAAA,CAAQ,MAAA;EAClC,KAAA,CAAM,GAAA,EAAK,aAAA,EAAe,IAAA,EAAM,MAAA,oBAA0B,OAAA;EAC1D,KAAA,CAAM,GAAA,EAAK,aAAA,GAAgB,OAAA;AAAA;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/flash/flash-store.ts","../src/inertia.options.ts","../src/inertia.module.ts","../src/inertia.tokens.ts","../src/seo/build-seo-tags.ts","../src/flash/cookie-flash-store.ts","../src/augment/router-context.ts","../src/services/hreflang.service.ts","../src/services/seo.service.ts","../src/services/ssr-renderer.service.ts","../src/services/manifest.service.ts","../src/services/template.service.ts","../src/services/inertia.service.ts","../src/decorators/inertia.decorators.ts","../src/middleware/handle-precognitive-requests.middleware.ts","../src/middleware/inertia.middleware.ts","../src/augment/router-variables.ts"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -5,7 +5,6 @@ import { Module } from "stratal/module";
|
|
|
5
5
|
import { Delete, Get, Patch, Post, Put, ROUTER_TOKENS, Route, RouterContext, SchemaValidationError, applyTrailingSlash } from "stratal/router";
|
|
6
6
|
import { CONTAINER_TOKEN, DI_TOKENS, Request, Singleton, Transient, inject } from "stratal/di";
|
|
7
7
|
import { I18N_TOKENS } from "stratal/i18n";
|
|
8
|
-
import { LOGGER_TOKENS } from "stratal/logger";
|
|
9
8
|
import { deleteCookie, getSignedCookie, setSignedCookie } from "hono/cookie";
|
|
10
9
|
import { z } from "stratal/validation";
|
|
11
10
|
//#region src/augment/router-context.ts
|
|
@@ -238,6 +237,7 @@ let InertiaService = class InertiaService {
|
|
|
238
237
|
async render(ctx, component, props = {}, renderOptions = {}) {
|
|
239
238
|
const reqUrl = new URL(ctx.c.req.url);
|
|
240
239
|
const url = reqUrl.search ? `${reqUrl.pathname}${reqUrl.search}` : reqUrl.pathname;
|
|
240
|
+
const pathname = reqUrl.pathname;
|
|
241
241
|
const isInertia = ctx.c.get("inertia");
|
|
242
242
|
const { shared: resolvedShared, sharedKeys } = await this.resolveSharedData(ctx);
|
|
243
243
|
const resolvedSeo = await this.seoService.resolve(ctx);
|
|
@@ -287,13 +287,17 @@ let InertiaService = class InertiaService {
|
|
|
287
287
|
"Vary": "X-Inertia"
|
|
288
288
|
}
|
|
289
289
|
});
|
|
290
|
-
const ssrResult = ctx.c.get("withoutSsr") || this.isSsrDisabled(url) ? {
|
|
291
|
-
head: [],
|
|
292
|
-
body: ""
|
|
293
|
-
} : await this.ssr.render(page);
|
|
294
290
|
const seoTags = this.seoService.tagsFor(resolvedSeo);
|
|
295
|
-
|
|
296
|
-
|
|
291
|
+
if (ctx.c.get("withoutSsr") || this.isSsrDisabled(pathname) || !this.options.ssr) {
|
|
292
|
+
const html = this.template.renderClientOnly(page, seoTags);
|
|
293
|
+
return new Response(html, {
|
|
294
|
+
status,
|
|
295
|
+
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
const { head, stream } = await this.ssr.render(page);
|
|
299
|
+
const body = this.template.renderStream(page, [...head, ...seoTags], stream);
|
|
300
|
+
return new Response(body, {
|
|
297
301
|
status,
|
|
298
302
|
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
299
303
|
});
|
|
@@ -483,12 +487,20 @@ let ManifestService = class ManifestService {
|
|
|
483
487
|
manifest;
|
|
484
488
|
entryClientPath;
|
|
485
489
|
isDev = Boolean(import.meta.env.DEV);
|
|
490
|
+
headTags = null;
|
|
491
|
+
scriptTags = null;
|
|
486
492
|
constructor(options) {
|
|
487
493
|
this.manifest = globalThis.__STRATAL_INERTIA_MANIFEST__ ?? null;
|
|
488
494
|
this.entryClientPath = (options.entryClientPath ?? DEFAULT_ENTRY_CLIENT_PATH).replace(/^\/+/, "");
|
|
489
495
|
if (!this.isDev && !this.manifest) throw new Error("@stratal/inertia: production build is missing the Vite client manifest. This is wired by stratalInertia() in vite.config.ts — confirm it is in your plugin list and that the client environment built successfully before the worker environment.");
|
|
490
496
|
}
|
|
491
497
|
getHeadTags() {
|
|
498
|
+
return this.headTags ??= this.buildHeadTags();
|
|
499
|
+
}
|
|
500
|
+
getScriptTags() {
|
|
501
|
+
return this.scriptTags ??= this.buildScriptTags();
|
|
502
|
+
}
|
|
503
|
+
buildHeadTags() {
|
|
492
504
|
if (this.isDev) return "<link rel=\"stylesheet\" href=\"/__inertia/ssr-css\" data-ssr-css />";
|
|
493
505
|
const tags = [];
|
|
494
506
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -499,7 +511,7 @@ let ManifestService = class ManifestService {
|
|
|
499
511
|
}
|
|
500
512
|
return tags.join("\n");
|
|
501
513
|
}
|
|
502
|
-
|
|
514
|
+
buildScriptTags() {
|
|
503
515
|
if (this.isDev) return [
|
|
504
516
|
"<script type=\"module\" src=\"/@vite/client\"><\/script>",
|
|
505
517
|
`<script type="module">
|
|
@@ -516,7 +528,7 @@ hot.on("vite:afterUpdate", () => {
|
|
|
516
528
|
return tags.join("\n");
|
|
517
529
|
}
|
|
518
530
|
};
|
|
519
|
-
ManifestService = __decorate([
|
|
531
|
+
ManifestService = __decorate([Singleton(), __decorateParam(0, inject(INERTIA_TOKENS.Options))], ManifestService);
|
|
520
532
|
//#endregion
|
|
521
533
|
//#region src/services/seo.service.ts
|
|
522
534
|
let SeoService = class SeoService {
|
|
@@ -585,23 +597,21 @@ function mergeSeo(a, b) {
|
|
|
585
597
|
//#region src/services/ssr-renderer.service.ts
|
|
586
598
|
let SsrRendererService = class SsrRendererService {
|
|
587
599
|
options;
|
|
588
|
-
logger;
|
|
589
600
|
bundle = null;
|
|
590
601
|
loadPromise = null;
|
|
591
|
-
constructor(options
|
|
602
|
+
constructor(options) {
|
|
592
603
|
this.options = options;
|
|
593
|
-
this.logger = logger;
|
|
594
604
|
}
|
|
605
|
+
/**
|
|
606
|
+
* Render a page to a streaming SSR result.
|
|
607
|
+
*
|
|
608
|
+
* The SSR bundle is imported once per worker (memoized). Bundle-load and render
|
|
609
|
+
* errors propagate — there is no silent client-side fallback. Callers must only
|
|
610
|
+
* invoke this when `options.ssr` is configured.
|
|
611
|
+
*/
|
|
595
612
|
async render(page) {
|
|
596
|
-
if (!this.options.ssr)
|
|
597
|
-
head: [],
|
|
598
|
-
body: ""
|
|
599
|
-
};
|
|
613
|
+
if (!this.options.ssr) throw new ApplicationError("[stratal:inertia] SSR bundle is not configured.");
|
|
600
614
|
await this.ensureBundle();
|
|
601
|
-
if (!this.bundle) return {
|
|
602
|
-
head: [],
|
|
603
|
-
body: ""
|
|
604
|
-
};
|
|
605
615
|
return this.bundle.render(page);
|
|
606
616
|
}
|
|
607
617
|
async ensureBundle() {
|
|
@@ -609,52 +619,84 @@ let SsrRendererService = class SsrRendererService {
|
|
|
609
619
|
this.loadPromise ??= this.loadBundle();
|
|
610
620
|
try {
|
|
611
621
|
await this.loadPromise;
|
|
612
|
-
} catch {}
|
|
613
|
-
}
|
|
614
|
-
async loadBundle() {
|
|
615
|
-
if (!this.options.ssr) return;
|
|
616
|
-
try {
|
|
617
|
-
const mod = await this.options.ssr.bundle();
|
|
618
|
-
const resolved = "default" in mod ? mod.default : mod;
|
|
619
|
-
this.bundle = resolved;
|
|
620
622
|
} catch (error) {
|
|
621
|
-
this.logger.warn("[stratal:inertia] Failed to load SSR bundle. Falling back to client-side rendering.", { error });
|
|
622
623
|
this.loadPromise = null;
|
|
624
|
+
throw error;
|
|
623
625
|
}
|
|
624
626
|
}
|
|
627
|
+
async loadBundle() {
|
|
628
|
+
const mod = await this.options.ssr.bundle();
|
|
629
|
+
this.bundle = "default" in mod ? mod.default : mod;
|
|
630
|
+
}
|
|
625
631
|
};
|
|
626
|
-
SsrRendererService = __decorate([
|
|
627
|
-
Singleton(),
|
|
628
|
-
__decorateParam(0, inject(INERTIA_TOKENS.Options)),
|
|
629
|
-
__decorateParam(1, inject(LOGGER_TOKENS.LoggerService))
|
|
630
|
-
], SsrRendererService);
|
|
632
|
+
SsrRendererService = __decorate([Singleton(), __decorateParam(0, inject(INERTIA_TOKENS.Options))], SsrRendererService);
|
|
631
633
|
//#endregion
|
|
632
634
|
//#region src/services/template.service.ts
|
|
635
|
+
const APP_ID = "app";
|
|
633
636
|
let TemplateService = class TemplateService {
|
|
634
|
-
options;
|
|
635
637
|
manifest;
|
|
638
|
+
pre;
|
|
639
|
+
post;
|
|
636
640
|
constructor(options, manifest) {
|
|
637
|
-
this.options = options;
|
|
638
641
|
this.manifest = manifest;
|
|
642
|
+
const match = /@inertia\b/.exec(options.rootView);
|
|
643
|
+
if (!match) throw new ApplicationError("[stratal:inertia] rootView template is missing the @inertia placeholder.");
|
|
644
|
+
this.pre = options.rootView.slice(0, match.index);
|
|
645
|
+
this.post = options.rootView.slice(match.index + 8);
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Compose the streamed HTML response: the document shell (head + opening
|
|
649
|
+
* `#app` wrapper) is flushed first, the React stream is piped verbatim, then
|
|
650
|
+
* the wrapper is closed and the trailing scripts are appended.
|
|
651
|
+
*
|
|
652
|
+
* Reproduces Inertia's `buildSSRBody` markup: a `<script data-page>` JSON tag
|
|
653
|
+
* (parsed before hydration) followed by `<div data-server-rendered id="app">`.
|
|
654
|
+
*/
|
|
655
|
+
renderStream(page, head, reactStream) {
|
|
656
|
+
const encoder = new TextEncoder();
|
|
657
|
+
const shellPre = this.fillPlaceholders(this.pre, head) + `<script data-page="${APP_ID}" type="application/json">${this.serialize(page)}<\/script><div data-server-rendered="true" id="${APP_ID}">`;
|
|
658
|
+
const shellPost = `</div>${this.fillPlaceholders(this.post, head)}`;
|
|
659
|
+
let reader;
|
|
660
|
+
return new ReadableStream({
|
|
661
|
+
async start(controller) {
|
|
662
|
+
controller.enqueue(encoder.encode(shellPre));
|
|
663
|
+
reader = reactStream.getReader();
|
|
664
|
+
try {
|
|
665
|
+
for (;;) {
|
|
666
|
+
const { done, value } = await reader.read();
|
|
667
|
+
if (done) break;
|
|
668
|
+
controller.enqueue(value);
|
|
669
|
+
}
|
|
670
|
+
controller.enqueue(encoder.encode(shellPost));
|
|
671
|
+
controller.close();
|
|
672
|
+
} catch (error) {
|
|
673
|
+
controller.error(error);
|
|
674
|
+
} finally {
|
|
675
|
+
reader.releaseLock();
|
|
676
|
+
reader = void 0;
|
|
677
|
+
}
|
|
678
|
+
},
|
|
679
|
+
cancel(reason) {
|
|
680
|
+
return reader?.cancel(reason) ?? reactStream.cancel(reason);
|
|
681
|
+
}
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* Buffered, client-only document used when SSR is disabled for the request.
|
|
686
|
+
* Emits an empty `#app` div for the client bundle to hydrate.
|
|
687
|
+
*/
|
|
688
|
+
renderClientOnly(page, head) {
|
|
689
|
+
return this.fillPlaceholders(this.pre, head) + `<script data-page="${APP_ID}" type="application/json">${this.serialize(page)}<\/script><div id="${APP_ID}"></div>` + this.fillPlaceholders(this.post, head);
|
|
639
690
|
}
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
let html = this.options.rootView;
|
|
646
|
-
html = html.replace("@inertiaHead", () => headTags);
|
|
647
|
-
html = html.replace("@inertia", () => appHtml);
|
|
648
|
-
html = html.replace("@viteHead", () => viteHead);
|
|
649
|
-
html = html.replace("@viteScripts", () => viteScripts);
|
|
650
|
-
return html;
|
|
651
|
-
}
|
|
652
|
-
buildClientOnlyBody(page) {
|
|
653
|
-
return `<script data-page="app" type="application/json">${JSON.stringify(page).replace(/\//g, "\\/")}<\/script><div id="app"></div>`;
|
|
691
|
+
fillPlaceholders(segment, head) {
|
|
692
|
+
return segment.replace("@inertiaHead", () => head.join("\n")).replace("@viteHead", () => this.manifest.getHeadTags()).replace("@viteScripts", () => this.manifest.getScriptTags());
|
|
693
|
+
}
|
|
694
|
+
serialize(page) {
|
|
695
|
+
return JSON.stringify(page).replace(/\//g, "\\/");
|
|
654
696
|
}
|
|
655
697
|
};
|
|
656
698
|
TemplateService = __decorate([
|
|
657
|
-
|
|
699
|
+
Singleton(),
|
|
658
700
|
__decorateParam(0, inject(INERTIA_TOKENS.Options)),
|
|
659
701
|
__decorateParam(1, inject(INERTIA_TOKENS.ManifestService))
|
|
660
702
|
], TemplateService);
|