@nuxt/nitro-server-nightly 4.5.0-29673761.f253a70d → 4.5.0-29673775.d239dbcd
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/index.d.mts +25 -1
- package/dist/index.mjs +22 -6
- package/dist/runtime/handlers/renderer.mjs +466 -5
- package/dist/runtime/utils/renderer/build-files.d.mts +5 -0
- package/dist/runtime/utils/renderer/build-files.mjs +4 -2
- package/dist/runtime/utils/renderer/islands.d.mts +6 -0
- package/dist/runtime/utils/renderer/islands.mjs +37 -0
- package/package.json +5 -5
package/dist/index.d.mts
CHANGED
|
@@ -2,7 +2,7 @@ import { EventHandler, H3Event } from "h3";
|
|
|
2
2
|
import { Nuxt } from "@nuxt/schema";
|
|
3
3
|
import { Nitro, NitroConfig, NitroDevEventHandler, NitroEventHandler, NitroOptions, NitroRouteConfig, NitroRuntimeConfig, NitroRuntimeConfigApp } from "nitropack/types";
|
|
4
4
|
import { LogObject } from "consola";
|
|
5
|
-
import { NuxtIslandContext, NuxtIslandResponse, NuxtRenderHTMLContext } from "nuxt/app";
|
|
5
|
+
import { NuxtIslandContext, NuxtIslandResponse, NuxtRenderChunkContext, NuxtRenderCloseContext, NuxtRenderHTMLContext, NuxtRenderRouteContext } from "nuxt/app";
|
|
6
6
|
import { HookResult, RuntimeConfig, TSReference } from "nuxt/schema";
|
|
7
7
|
declare module "nitropack" {
|
|
8
8
|
interface NitroRuntimeConfigApp {
|
|
@@ -11,6 +11,7 @@ declare module "nitropack" {
|
|
|
11
11
|
}
|
|
12
12
|
interface NitroRouteRules {
|
|
13
13
|
ssr?: boolean;
|
|
14
|
+
streaming?: boolean;
|
|
14
15
|
noScripts?: boolean;
|
|
15
16
|
/** @deprecated Use `noScripts` instead */
|
|
16
17
|
experimentalNoScripts?: boolean;
|
|
@@ -25,6 +26,7 @@ declare module "nitropack/types" {
|
|
|
25
26
|
}
|
|
26
27
|
interface NitroRouteRules {
|
|
27
28
|
ssr?: boolean;
|
|
29
|
+
streaming?: boolean;
|
|
28
30
|
noScripts?: boolean;
|
|
29
31
|
/** @deprecated Use `noScripts` instead */
|
|
30
32
|
experimentalNoScripts?: boolean;
|
|
@@ -36,6 +38,7 @@ declare module "nitropack" {
|
|
|
36
38
|
interface NitroRuntimeConfig extends RuntimeConfig {}
|
|
37
39
|
interface NitroRouteConfig {
|
|
38
40
|
ssr?: boolean;
|
|
41
|
+
streaming?: boolean;
|
|
39
42
|
noScripts?: boolean;
|
|
40
43
|
/** @deprecated Use `noScripts` instead */
|
|
41
44
|
experimentalNoScripts?: boolean;
|
|
@@ -47,6 +50,16 @@ declare module "nitropack" {
|
|
|
47
50
|
}) => void | Promise<void>;
|
|
48
51
|
"render:html": (htmlContext: NuxtRenderHTMLContext, context: {
|
|
49
52
|
event: H3Event;
|
|
53
|
+
streaming?: boolean;
|
|
54
|
+
}) => void | Promise<void>;
|
|
55
|
+
"render:html:chunk": (chunkContext: NuxtRenderChunkContext, context: {
|
|
56
|
+
event: H3Event;
|
|
57
|
+
}) => void | Promise<void>;
|
|
58
|
+
"render:html:close": (closeContext: NuxtRenderCloseContext, context: {
|
|
59
|
+
event: H3Event;
|
|
60
|
+
}) => void | Promise<void>;
|
|
61
|
+
"render:route": (renderRouteContext: NuxtRenderRouteContext, context: {
|
|
62
|
+
event: H3Event;
|
|
50
63
|
}) => void | Promise<void>;
|
|
51
64
|
"render:island": (islandResponse: NuxtIslandResponse, context: {
|
|
52
65
|
event: H3Event;
|
|
@@ -58,6 +71,7 @@ declare module "nitropack/types" {
|
|
|
58
71
|
interface NitroRuntimeConfig extends RuntimeConfig {}
|
|
59
72
|
interface NitroRouteConfig {
|
|
60
73
|
ssr?: boolean;
|
|
74
|
+
streaming?: boolean;
|
|
61
75
|
noScripts?: boolean;
|
|
62
76
|
/** @deprecated Use `noScripts` instead */
|
|
63
77
|
experimentalNoScripts?: boolean;
|
|
@@ -69,6 +83,16 @@ declare module "nitropack/types" {
|
|
|
69
83
|
}) => void | Promise<void>;
|
|
70
84
|
"render:html": (htmlContext: NuxtRenderHTMLContext, context: {
|
|
71
85
|
event: H3Event;
|
|
86
|
+
streaming?: boolean;
|
|
87
|
+
}) => void | Promise<void>;
|
|
88
|
+
"render:html:chunk": (chunkContext: NuxtRenderChunkContext, context: {
|
|
89
|
+
event: H3Event;
|
|
90
|
+
}) => void | Promise<void>;
|
|
91
|
+
"render:html:close": (closeContext: NuxtRenderCloseContext, context: {
|
|
92
|
+
event: H3Event;
|
|
93
|
+
}) => void | Promise<void>;
|
|
94
|
+
"render:route": (renderRouteContext: NuxtRenderRouteContext, context: {
|
|
95
|
+
event: H3Event;
|
|
72
96
|
}) => void | Promise<void>;
|
|
73
97
|
"render:island": (islandResponse: NuxtIslandResponse, context: {
|
|
74
98
|
event: H3Event;
|
package/dist/index.mjs
CHANGED
|
@@ -21,7 +21,7 @@ import { hasTTY, isCI, isWindows } from "std-env";
|
|
|
21
21
|
import { ImpoundPlugin } from "impound";
|
|
22
22
|
import { resolveModulePath } from "exsolve";
|
|
23
23
|
import { runtimeDependencies } from "nitropack/runtime/meta";
|
|
24
|
-
var version = "4.5.0-
|
|
24
|
+
var version = "4.5.0-29673775.d239dbcd";
|
|
25
25
|
function toArray(value) {
|
|
26
26
|
return Array.isArray(value) ? value : [value];
|
|
27
27
|
}
|
|
@@ -106,7 +106,7 @@ ${[...references.map((ref) => renderReference(ref, typesDir)), ...declarations].
|
|
|
106
106
|
import type { RuntimeConfig } from 'nuxt/schema'
|
|
107
107
|
import type { H3Event } from 'h3'
|
|
108
108
|
import type { LogObject } from 'consola'
|
|
109
|
-
import type { NuxtIslandContext, NuxtIslandResponse, NuxtRenderHTMLContext } from 'nuxt/app'
|
|
109
|
+
import type { NuxtIslandContext, NuxtIslandResponse, NuxtRenderChunkContext, NuxtRenderCloseContext, NuxtRenderHTMLContext, NuxtRenderRouteContext } from 'nuxt/app'
|
|
110
110
|
|
|
111
111
|
declare module 'nitropack' {
|
|
112
112
|
interface NitroRuntimeConfigApp {
|
|
@@ -116,12 +116,14 @@ declare module 'nitropack' {
|
|
|
116
116
|
interface NitroRuntimeConfig extends RuntimeConfig {}
|
|
117
117
|
interface NitroRouteConfig {
|
|
118
118
|
ssr?: boolean
|
|
119
|
+
streaming?: boolean
|
|
119
120
|
noScripts?: boolean
|
|
120
121
|
/** @deprecated Use \`noScripts\` instead */
|
|
121
122
|
experimentalNoScripts?: boolean
|
|
122
123
|
}
|
|
123
124
|
interface NitroRouteRules {
|
|
124
125
|
ssr?: boolean
|
|
126
|
+
streaming?: boolean
|
|
125
127
|
noScripts?: boolean
|
|
126
128
|
/** @deprecated Use \`noScripts\` instead */
|
|
127
129
|
experimentalNoScripts?: boolean
|
|
@@ -130,7 +132,10 @@ declare module 'nitropack' {
|
|
|
130
132
|
}
|
|
131
133
|
interface NitroRuntimeHooks {
|
|
132
134
|
'dev:ssr-logs': (ctx: { logs: LogObject[], path: string }) => void | Promise<void>
|
|
133
|
-
'render:html': (htmlContext: NuxtRenderHTMLContext, context: { event: H3Event }) => void | Promise<void>
|
|
135
|
+
'render:html': (htmlContext: NuxtRenderHTMLContext, context: { event: H3Event, streaming?: boolean }) => void | Promise<void>
|
|
136
|
+
'render:html:chunk': (chunkContext: NuxtRenderChunkContext, context: { event: H3Event }) => void | Promise<void>
|
|
137
|
+
'render:html:close': (closeContext: NuxtRenderCloseContext, context: { event: H3Event }) => void | Promise<void>
|
|
138
|
+
'render:route': (renderRouteContext: NuxtRenderRouteContext, context: { event: H3Event }) => void | Promise<void>
|
|
134
139
|
'render:island': (islandResponse: NuxtIslandResponse, context: { event: H3Event, islandContext: NuxtIslandContext }) => void | Promise<void>
|
|
135
140
|
}
|
|
136
141
|
}
|
|
@@ -142,12 +147,14 @@ declare module 'nitropack/types' {
|
|
|
142
147
|
interface NitroRuntimeConfig extends RuntimeConfig {}
|
|
143
148
|
interface NitroRouteConfig {
|
|
144
149
|
ssr?: boolean
|
|
150
|
+
streaming?: boolean
|
|
145
151
|
noScripts?: boolean
|
|
146
152
|
/** @deprecated Use \`noScripts\` instead */
|
|
147
153
|
experimentalNoScripts?: boolean
|
|
148
154
|
}
|
|
149
155
|
interface NitroRouteRules {
|
|
150
156
|
ssr?: boolean
|
|
157
|
+
streaming?: boolean
|
|
151
158
|
noScripts?: boolean
|
|
152
159
|
/** @deprecated Use \`noScripts\` instead */
|
|
153
160
|
experimentalNoScripts?: boolean
|
|
@@ -156,7 +163,10 @@ declare module 'nitropack/types' {
|
|
|
156
163
|
}
|
|
157
164
|
interface NitroRuntimeHooks {
|
|
158
165
|
'dev:ssr-logs': (ctx: { logs: LogObject[], path: string }) => void | Promise<void>
|
|
159
|
-
'render:html': (htmlContext: NuxtRenderHTMLContext, context: { event: H3Event }) => void | Promise<void>
|
|
166
|
+
'render:html': (htmlContext: NuxtRenderHTMLContext, context: { event: H3Event, streaming?: boolean }) => void | Promise<void>
|
|
167
|
+
'render:html:chunk': (chunkContext: NuxtRenderChunkContext, context: { event: H3Event }) => void | Promise<void>
|
|
168
|
+
'render:html:close': (closeContext: NuxtRenderCloseContext, context: { event: H3Event }) => void | Promise<void>
|
|
169
|
+
'render:route': (renderRouteContext: NuxtRenderRouteContext, context: { event: H3Event }) => void | Promise<void>
|
|
160
170
|
'render:island': (islandResponse: NuxtIslandResponse, context: { event: H3Event, islandContext: NuxtIslandContext }) => void | Promise<void>
|
|
161
171
|
}
|
|
162
172
|
}
|
|
@@ -292,6 +302,7 @@ async function bundle(nuxt) {
|
|
|
292
302
|
"#internal/nuxt/app-config": () => nuxt.vfs["#build/app.config.mjs"]?.replace(/\/\*\* client \*\*\/[\s\S]*\/\*\* client-end \*\*\//, "") || "",
|
|
293
303
|
"#spa-template": async () => `export const template = ${JSON.stringify(await spaLoadingTemplate(nuxt))}`,
|
|
294
304
|
"#internal/entry-chunk.mjs": () => `export const entryFileName = undefined`,
|
|
305
|
+
"#internal/streaming-iife-chunk.mjs": () => `export const iifeChunkFileName = undefined`,
|
|
295
306
|
"#internal/nuxt/entry-ids.mjs": () => `export default []`,
|
|
296
307
|
"#internal/nuxt/nitro-config.mjs": () => {
|
|
297
308
|
const hasCachedRoutes = Object.values(nitro.options.routeRules).some((r) => r.isr || r.cache);
|
|
@@ -306,11 +317,16 @@ async function bundle(nuxt) {
|
|
|
306
317
|
`export const NUXT_SHARED_DATA = ${!!nuxt.options.experimental.sharedPrerenderData}`,
|
|
307
318
|
`export const NUXT_PAYLOAD_EXTRACTION = ${nuxt.options.experimental.payloadExtraction !== false}`,
|
|
308
319
|
`export const NUXT_PAYLOAD_INLINE = ${nuxt.options.experimental.payloadExtraction !== true}`,
|
|
309
|
-
`export const NUXT_RUNTIME_PAYLOAD_EXTRACTION = ${hasCachedRoutes}
|
|
320
|
+
`export const NUXT_RUNTIME_PAYLOAD_EXTRACTION = ${hasCachedRoutes}`,
|
|
321
|
+
`export const NUXT_SSR_STREAMING = ${!!(typeof nuxt.options.experimental.ssrStreaming === "object" && nuxt.options.experimental.ssrStreaming.enabled)}`,
|
|
322
|
+
`export const NUXT_SSR_STREAMING_BOT_RE = ${typeof nuxt.options.experimental.ssrStreaming === "object" && nuxt.options.experimental.ssrStreaming.botRegex instanceof RegExp ? String(nuxt.options.experimental.ssrStreaming.botRegex) : "/^$/"}`
|
|
310
323
|
].join("\n");
|
|
311
324
|
}
|
|
312
325
|
},
|
|
313
|
-
routeRules: {
|
|
326
|
+
routeRules: {
|
|
327
|
+
...typeof nuxt.options.experimental.ssrStreaming === "object" && nuxt.options.experimental.ssrStreaming.enabled ? { "/**": { streaming: true } } : {},
|
|
328
|
+
"/__nuxt_error": { cache: false }
|
|
329
|
+
},
|
|
314
330
|
appConfig: nuxt.options.appConfig,
|
|
315
331
|
appConfigFiles: layerDirs.map((dirs) => join(dirs.app, "app.config")),
|
|
316
332
|
typescript: {
|
|
@@ -1,26 +1,31 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
2
|
import { getPrefetchLinks, getPreloadLinks, getRequestDependencies, renderResourceHeaders } from "vue-bundle-renderer/runtime";
|
|
3
|
-
import {
|
|
3
|
+
import { renderToWebStream } from "vue/server-renderer";
|
|
4
|
+
import { appendResponseHeader, createError, getQuery, getRequestHeader, getResponseStatus, getResponseStatusText, removeResponseHeader, setResponseHeader, writeEarlyHints } from "h3";
|
|
4
5
|
import { getQuery as getURLQuery, joinURL } from "ufo";
|
|
5
6
|
import { propsToString, renderSSRHead } from "@unhead/vue/server";
|
|
7
|
+
import { createBootstrapScript, renderSSRHeadSuspenseChunk, renderShell } from "@unhead/vue/stream/server";
|
|
8
|
+
import { streamingIifeCode } from "@unhead/vue/stream/iife";
|
|
6
9
|
import destr from "destr";
|
|
7
10
|
import { defineRenderHandler, getRouteRules, useNitroApp } from "nitropack/runtime";
|
|
8
|
-
import { getRenderer } from "../utils/renderer/build-files.mjs";
|
|
11
|
+
import { APP_ROOT_CLOSE_TAG, APP_ROOT_OPEN_TAG, getRenderer, getServerApp } from "../utils/renderer/build-files.mjs";
|
|
9
12
|
import { payloadCache, prerenderRenderingURLs } from "../utils/cache.mjs";
|
|
10
13
|
import { renderPayloadJsonScript, renderPayloadResponse, renderPayloadScript, splitPayload } from "../utils/renderer/payload.mjs";
|
|
11
14
|
import { createSSRContext, setSSRError } from "../utils/renderer/app.mjs";
|
|
12
15
|
import { renderInlineStyles } from "../utils/renderer/inline-styles.mjs";
|
|
13
|
-
import { replaceIslandTeleports } from "../utils/renderer/islands.mjs";
|
|
16
|
+
import { renderStreamedIslandTeleports, replaceIslandTeleports } from "../utils/renderer/islands.mjs";
|
|
14
17
|
// @ts-expect-error virtual file
|
|
15
18
|
import { renderSSRHeadOptions } from "#internal/unhead.config.mjs";
|
|
16
19
|
// @ts-expect-error virtual file
|
|
17
|
-
import { NUXT_ASYNC_CONTEXT, NUXT_EARLY_HINTS, NUXT_INLINE_STYLES, NUXT_JSON_PAYLOADS, NUXT_NO_SCRIPTS, NUXT_PAYLOAD_EXTRACTION, NUXT_PAYLOAD_INLINE, NUXT_RUNTIME_PAYLOAD_EXTRACTION, PARSE_ERROR_DATA } from "#internal/nuxt/nitro-config.mjs";
|
|
20
|
+
import { NUXT_ASYNC_CONTEXT, NUXT_EARLY_HINTS, NUXT_INLINE_STYLES, NUXT_JSON_PAYLOADS, NUXT_NO_SCRIPTS, NUXT_PAYLOAD_EXTRACTION, NUXT_PAYLOAD_INLINE, NUXT_RUNTIME_PAYLOAD_EXTRACTION, NUXT_SSR_STREAMING, NUXT_SSR_STREAMING_BOT_RE, PARSE_ERROR_DATA } from "#internal/nuxt/nitro-config.mjs";
|
|
18
21
|
// @ts-expect-error virtual file
|
|
19
|
-
import { appHead, appTeleportAttrs, appTeleportTag, componentIslands } from "#internal/nuxt.config.mjs";
|
|
22
|
+
import { appHead, appTeleportAttrs, appTeleportTag, componentIslands, componentIslandsActive } from "#internal/nuxt.config.mjs";
|
|
20
23
|
// @ts-expect-error virtual file
|
|
21
24
|
import entryIds from "#internal/nuxt/entry-ids.mjs";
|
|
22
25
|
// @ts-expect-error virtual file
|
|
23
26
|
import { entryFileName } from "#internal/entry-chunk.mjs";
|
|
27
|
+
// @ts-expect-error virtual file
|
|
28
|
+
import { iifeChunkFileName } from "#internal/streaming-iife-chunk.mjs";
|
|
24
29
|
import { buildAssetsURL, publicAssetsURL } from "../utils/paths.mjs";
|
|
25
30
|
import { relative } from "pathe";
|
|
26
31
|
// @ts-expect-error private property consumed by vite-generated url helpers
|
|
@@ -37,6 +42,8 @@ const APP_TELEPORT_CLOSE_TAG = HAS_APP_TELEPORTS ? `</${appTeleportTag}>` : "";
|
|
|
37
42
|
const PAYLOAD_URL_RE = NUXT_JSON_PAYLOADS ? /^[^?]*\/_payload.json(?:\?.*)?$/ : /^[^?]*\/_payload.js(?:\?.*)?$/;
|
|
38
43
|
const PAYLOAD_FILENAME = NUXT_JSON_PAYLOADS ? "_payload.json" : "_payload.js";
|
|
39
44
|
let entryPath;
|
|
45
|
+
|
|
46
|
+
const SSR_BOT_RE = NUXT_SSR_STREAMING_BOT_RE;
|
|
40
47
|
const handler = defineRenderHandler((event) => {
|
|
41
48
|
|
|
42
49
|
const ssrError = event.path.startsWith("/__nuxt_error") ? getQuery(event) : null;
|
|
@@ -119,6 +126,27 @@ async function renderRoute(event, ssrError) {
|
|
|
119
126
|
ssrContext.modules.add(id);
|
|
120
127
|
}
|
|
121
128
|
}
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
const canStream = NUXT_SSR_STREAMING && !ssrContext.noSSR && !ssrError && !isRenderingPayload && !import.meta.prerender && !(NUXT_NO_SCRIPTS && componentIslandsActive) && !routeOptions.noScripts && !!routeOptions.streaming && !routeOptions.cache && !routeOptions.isr && !routeOptions.redirect;
|
|
133
|
+
const renderRouteContext = {
|
|
134
|
+
canStream,
|
|
135
|
+
prefersStream: !!(NUXT_SSR_STREAMING && !SSR_BOT_RE.test(getRequestHeader(event, "user-agent") || ""))
|
|
136
|
+
};
|
|
137
|
+
await nitroApp.hooks.callHook("render:route", renderRouteContext, { event });
|
|
138
|
+
if (NUXT_SSR_STREAMING && canStream && renderRouteContext.prefersStream) {
|
|
139
|
+
return renderStreamedResponse({
|
|
140
|
+
event,
|
|
141
|
+
ssrContext,
|
|
142
|
+
renderer,
|
|
143
|
+
routeOptions,
|
|
144
|
+
ssrError,
|
|
145
|
+
_PAYLOAD_EXTRACTION,
|
|
146
|
+
_PAYLOAD_INLINE,
|
|
147
|
+
payloadURL
|
|
148
|
+
});
|
|
149
|
+
}
|
|
122
150
|
const _rendered = await renderer.renderToString(ssrContext).catch(async (error) => {
|
|
123
151
|
|
|
124
152
|
|
|
@@ -299,6 +327,439 @@ async function renderRoute(event, ssrError) {
|
|
|
299
327
|
};
|
|
300
328
|
}
|
|
301
329
|
export default handler;
|
|
330
|
+
async function renderStreamedResponse(ctx) {
|
|
331
|
+
const { event, ssrContext, renderer, routeOptions, ssrError, _PAYLOAD_EXTRACTION, _PAYLOAD_INLINE, payloadURL } = ctx;
|
|
332
|
+
const nitroApp = useNitroApp();
|
|
333
|
+
const NO_SCRIPTS = NUXT_NO_SCRIPTS || !!routeOptions?.noScripts;
|
|
334
|
+
|
|
335
|
+
const { link: linkHeader } = renderResourceHeaders({}, renderer.rendererContext);
|
|
336
|
+
if (linkHeader) {
|
|
337
|
+
appendResponseHeader(event, "link", linkHeader);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const entryInlineStyles = NUXT_INLINE_STYLES ? await renderInlineStyles(new Set(entryIds)) : [];
|
|
341
|
+
|
|
342
|
+
if (entryInlineStyles.length) {
|
|
343
|
+
ssrContext.head.push({ style: entryInlineStyles });
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const { styles: entryStyles, scripts: entryScripts } = getRequestDependencies({}, renderer.rendererContext);
|
|
347
|
+
const shellLinks = [];
|
|
348
|
+
for (const resource of Object.values(entryStyles)) {
|
|
349
|
+
if (import.meta.dev && "inline" in getURLQuery(resource.file)) {
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
shellLinks.push({
|
|
353
|
+
rel: "stylesheet",
|
|
354
|
+
href: renderer.rendererContext.buildAssetsURL(resource.file),
|
|
355
|
+
crossorigin: ""
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
if (shellLinks.length) {
|
|
359
|
+
ssrContext.head.push({ link: shellLinks });
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (entryFileName && !NO_SCRIPTS) {
|
|
363
|
+
let path = entryPath;
|
|
364
|
+
if (!path) {
|
|
365
|
+
path = buildAssetsURL(entryFileName);
|
|
366
|
+
if (ssrContext.runtimeConfig.app.cdnURL || /^(?:\/|\.+\/)/.test(path)) {
|
|
367
|
+
entryPath = path;
|
|
368
|
+
} else {
|
|
369
|
+
path = relative(event.path.replace(/\/[^/]+$/, "/"), joinURL("/", path));
|
|
370
|
+
if (!/^(?:\/|\.+\/)/.test(path)) {
|
|
371
|
+
path = `./${path}`;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
ssrContext.head.push({ script: [{
|
|
376
|
+
tagPosition: "head",
|
|
377
|
+
tagPriority: "critical",
|
|
378
|
+
type: "importmap",
|
|
379
|
+
|
|
380
|
+
innerHTML: { imports: { "#entry": path } }
|
|
381
|
+
}] });
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (_PAYLOAD_EXTRACTION && !_PAYLOAD_INLINE && !NO_SCRIPTS) {
|
|
385
|
+
ssrContext.head.push({ link: [NUXT_JSON_PAYLOADS ? {
|
|
386
|
+
rel: "preload",
|
|
387
|
+
as: "fetch",
|
|
388
|
+
crossorigin: "anonymous",
|
|
389
|
+
href: payloadURL
|
|
390
|
+
} : {
|
|
391
|
+
rel: "modulepreload",
|
|
392
|
+
crossorigin: "",
|
|
393
|
+
href: payloadURL
|
|
394
|
+
}] });
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (!NO_SCRIPTS) {
|
|
398
|
+
ssrContext.head.push({ link: getPreloadLinks({}, renderer.rendererContext) });
|
|
399
|
+
ssrContext.head.push({ link: getPrefetchLinks({}, renderer.rendererContext) });
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (!NO_SCRIPTS) {
|
|
403
|
+
ssrContext.head.push({ script: Object.values(entryScripts).map((resource) => ({
|
|
404
|
+
type: resource.module ? "module" : null,
|
|
405
|
+
src: renderer.rendererContext.buildAssetsURL(resource.file),
|
|
406
|
+
defer: resource.module ? null : true,
|
|
407
|
+
tagPosition: "head",
|
|
408
|
+
crossorigin: ""
|
|
409
|
+
})) });
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (!NO_SCRIPTS && !import.meta.dev && iifeChunkFileName) {
|
|
413
|
+
ssrContext.head.push({ link: [{
|
|
414
|
+
rel: "preload",
|
|
415
|
+
as: "script",
|
|
416
|
+
href: buildAssetsURL(iifeChunkFileName)
|
|
417
|
+
}] });
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
const createSSRApp = await getServerApp();
|
|
425
|
+
let vueApp;
|
|
426
|
+
try {
|
|
427
|
+
vueApp = await createSSRApp(ssrContext);
|
|
428
|
+
} catch (error) {
|
|
429
|
+
|
|
430
|
+
if ((ssrContext["~renderResponse"] || ssrContext._renderResponse) && error?.message === "skipping render") {
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
removeResponseHeader(event, "link");
|
|
434
|
+
return ssrContext["~renderResponse"] || ssrContext._renderResponse;
|
|
435
|
+
}
|
|
436
|
+
await ssrContext.nuxt?.hooks.callHook("app:error", error);
|
|
437
|
+
throw error;
|
|
438
|
+
}
|
|
439
|
+
if (ssrContext["~renderResponse"] || ssrContext._renderResponse) {
|
|
440
|
+
removeResponseHeader(event, "link");
|
|
441
|
+
return ssrContext["~renderResponse"] || ssrContext._renderResponse;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
const { headTags, bodyTags, bodyTagsOpen, htmlAttrs, bodyAttrs } = renderShell(ssrContext.head);
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
const cspNonce = headTags.match(/<script[^>]+\bnonce="([^"]*)"/)?.[1];
|
|
452
|
+
const nonceAttr = cspNonce ? ` nonce="${cspNonce}"` : "";
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
const bootstrapScript = NO_SCRIPTS ? "" : createBootstrapScript(undefined, cspNonce);
|
|
459
|
+
let iifeScript = "";
|
|
460
|
+
if (!NO_SCRIPTS) {
|
|
461
|
+
if (!import.meta.dev && iifeChunkFileName) {
|
|
462
|
+
iifeScript = `<script async${nonceAttr} src="${buildAssetsURL(iifeChunkFileName)}"><\/script>`;
|
|
463
|
+
} else {
|
|
464
|
+
iifeScript = `<script${nonceAttr}>${streamingIifeCode}<\/script>`;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
const shellContext = {
|
|
468
|
+
htmlAttrs: htmlAttrs ? [htmlAttrs] : [],
|
|
469
|
+
head: normalizeChunks([bootstrapScript, headTags]),
|
|
470
|
+
bodyAttrs: bodyAttrs ? [bodyAttrs] : [],
|
|
471
|
+
bodyPrepend: normalizeChunks([iifeScript, bodyTagsOpen]),
|
|
472
|
+
body: [],
|
|
473
|
+
bodyAppend: []
|
|
474
|
+
};
|
|
475
|
+
if (import.meta.dev) {
|
|
476
|
+
const initialBodyLen = shellContext.body.length;
|
|
477
|
+
const initialAppendLen = shellContext.bodyAppend.length;
|
|
478
|
+
await nitroApp.hooks.callHook("render:html", shellContext, {
|
|
479
|
+
event,
|
|
480
|
+
streaming: true
|
|
481
|
+
});
|
|
482
|
+
if (shellContext.body.length !== initialBodyLen || shellContext.bodyAppend.length !== initialAppendLen) {
|
|
483
|
+
console.warn(`[nuxt] \`render:html\` mutated \`body\`/\`bodyAppend\` while streaming (${event.path}). These fields are silently dropped because the body is about to stream - use the \`render:html:close\` hook instead.`);
|
|
484
|
+
}
|
|
485
|
+
} else {
|
|
486
|
+
await nitroApp.hooks.callHook("render:html", shellContext, {
|
|
487
|
+
event,
|
|
488
|
+
streaming: true
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
const shellHtml = "<!DOCTYPE html>" + `<html${joinAttrs(shellContext.htmlAttrs)}>` + `<head>${joinTags(shellContext.head)}</head>` + `<body${joinAttrs(shellContext.bodyAttrs)}>` + joinTags(shellContext.bodyPrepend);
|
|
492
|
+
|
|
493
|
+
const vueStream = renderToWebStream(vueApp, ssrContext);
|
|
494
|
+
const reader = vueStream.getReader();
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
let firstChunk;
|
|
505
|
+
try {
|
|
506
|
+
const { done, value } = await reader.read();
|
|
507
|
+
if (!done) {
|
|
508
|
+
firstChunk = value;
|
|
509
|
+
}
|
|
510
|
+
} catch (error) {
|
|
511
|
+
reader.releaseLock();
|
|
512
|
+
removeResponseHeader(event, "link");
|
|
513
|
+
if (ssrContext["~renderResponse"] || ssrContext._renderResponse) {
|
|
514
|
+
return ssrContext["~renderResponse"] || ssrContext._renderResponse;
|
|
515
|
+
}
|
|
516
|
+
const _err = !ssrError && ssrContext.payload?.error || error;
|
|
517
|
+
await ssrContext.nuxt?.hooks.callHook("app:error", _err);
|
|
518
|
+
throw _err;
|
|
519
|
+
}
|
|
520
|
+
if (ssrContext["~renderResponse"] || ssrContext._renderResponse) {
|
|
521
|
+
reader.cancel().catch(() => {});
|
|
522
|
+
removeResponseHeader(event, "link");
|
|
523
|
+
return ssrContext["~renderResponse"] || ssrContext._renderResponse;
|
|
524
|
+
}
|
|
525
|
+
if (ssrContext.payload?.error && !ssrError) {
|
|
526
|
+
reader.cancel().catch(() => {});
|
|
527
|
+
removeResponseHeader(event, "link");
|
|
528
|
+
throw ssrContext.payload.error;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
const committedSnapshot = import.meta.dev ? {
|
|
535
|
+
status: getResponseStatus(event),
|
|
536
|
+
statusText: getResponseStatusText(event),
|
|
537
|
+
headers: snapshotResponseHeaders(event)
|
|
538
|
+
} : null;
|
|
539
|
+
|
|
540
|
+
const encoder = new TextEncoder();
|
|
541
|
+
let chunkIndex = 0;
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
const enqueueChunk = async (controller, chunk) => {
|
|
546
|
+
const chunkContext = {
|
|
547
|
+
chunk,
|
|
548
|
+
index: chunkIndex++
|
|
549
|
+
};
|
|
550
|
+
await nitroApp.hooks.callHook("render:html:chunk", chunkContext, { event });
|
|
551
|
+
controller.enqueue(chunkContext.chunk);
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
const emittedStyles = new Set(Object.values(entryStyles).map((r) => r.file));
|
|
561
|
+
const inlinedCss = new Set(entryInlineStyles.map((s) => String(s.innerHTML)));
|
|
562
|
+
const renderRouteStyles = async () => {
|
|
563
|
+
let tags = "";
|
|
564
|
+
if (NUXT_INLINE_STYLES) {
|
|
565
|
+
for (const style of await renderInlineStyles(ssrContext.modules ?? [])) {
|
|
566
|
+
const css = String(style.innerHTML);
|
|
567
|
+
if (!css || inlinedCss.has(css)) {
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
inlinedCss.add(css);
|
|
571
|
+
tags += `<style${nonceAttr}>${css}</style>`;
|
|
572
|
+
}
|
|
573
|
+
return tags;
|
|
574
|
+
}
|
|
575
|
+
for (const resource of Object.values(getRequestDependencies(ssrContext, renderer.rendererContext).styles)) {
|
|
576
|
+
if (emittedStyles.has(resource.file)) {
|
|
577
|
+
continue;
|
|
578
|
+
}
|
|
579
|
+
if (import.meta.dev && "inline" in getURLQuery(resource.file)) {
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
582
|
+
emittedStyles.add(resource.file);
|
|
583
|
+
tags += `<link rel="stylesheet" crossorigin href="${renderer.rendererContext.buildAssetsURL(resource.file)}">`;
|
|
584
|
+
}
|
|
585
|
+
return tags;
|
|
586
|
+
};
|
|
587
|
+
const outputStream = new ReadableStream({
|
|
588
|
+
async start(controller) {
|
|
589
|
+
try {
|
|
590
|
+
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
await enqueueChunk(controller, encoder.encode(shellHtml));
|
|
594
|
+
await enqueueChunk(controller, encoder.encode(await renderRouteStyles() + APP_ROOT_OPEN_TAG));
|
|
595
|
+
if (firstChunk) {
|
|
596
|
+
await enqueueChunk(controller, firstChunk);
|
|
597
|
+
const headChunk = renderSSRHeadSuspenseChunk(ssrContext.head);
|
|
598
|
+
if (headChunk && !NO_SCRIPTS) {
|
|
599
|
+
await enqueueChunk(controller, encoder.encode(`<script${nonceAttr}>${headChunk};document.currentScript.remove()<\/script>`));
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
try {
|
|
604
|
+
while (true) {
|
|
605
|
+
const { done, value } = await reader.read();
|
|
606
|
+
if (done) {
|
|
607
|
+
break;
|
|
608
|
+
}
|
|
609
|
+
await enqueueChunk(controller, value);
|
|
610
|
+
|
|
611
|
+
const headChunk = renderSSRHeadSuspenseChunk(ssrContext.head);
|
|
612
|
+
if (headChunk && !NO_SCRIPTS) {
|
|
613
|
+
await enqueueChunk(controller, encoder.encode(`<script${nonceAttr}>${headChunk};document.currentScript.remove()<\/script>`));
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
} finally {
|
|
617
|
+
reader.releaseLock();
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
if (!NO_SCRIPTS) {
|
|
623
|
+
const finalHeadChunk = renderSSRHeadSuspenseChunk(ssrContext.head);
|
|
624
|
+
if (finalHeadChunk) {
|
|
625
|
+
await enqueueChunk(controller, encoder.encode(`<script${nonceAttr}>${finalHeadChunk};document.currentScript.remove()<\/script>`));
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
await ssrContext.nuxt?.hooks.callHook("app:rendered", {
|
|
630
|
+
ssrContext,
|
|
631
|
+
renderResult: {}
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
if (ssrContext.payload?.error && !ssrError) {
|
|
637
|
+
await ssrContext.nuxt?.hooks.callHook("app:error", ssrContext.payload.error);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
if (!NO_SCRIPTS) {
|
|
641
|
+
ssrContext.head.push({ script: _PAYLOAD_INLINE ? NUXT_JSON_PAYLOADS ? renderPayloadJsonScript({
|
|
642
|
+
ssrContext,
|
|
643
|
+
data: ssrContext.payload
|
|
644
|
+
}) : renderPayloadScript({
|
|
645
|
+
ssrContext,
|
|
646
|
+
data: ssrContext.payload,
|
|
647
|
+
routeOptions
|
|
648
|
+
}) : NUXT_JSON_PAYLOADS ? renderPayloadJsonScript({
|
|
649
|
+
ssrContext,
|
|
650
|
+
data: splitPayload(ssrContext).initial,
|
|
651
|
+
src: payloadURL
|
|
652
|
+
}) : renderPayloadScript({
|
|
653
|
+
ssrContext,
|
|
654
|
+
data: splitPayload(ssrContext).initial,
|
|
655
|
+
routeOptions,
|
|
656
|
+
src: payloadURL
|
|
657
|
+
}) }, {
|
|
658
|
+
tagPosition: "bodyClose",
|
|
659
|
+
tagPriority: "high"
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
|
|
665
|
+
const closingHead = applyRenderOptions(ssrContext.head.render(), renderSSRHeadOptions);
|
|
666
|
+
const closeContext = { bodyAppend: normalizeChunks([bodyTags, closingHead.bodyTags]) };
|
|
667
|
+
await nitroApp.hooks.callHook("render:html:close", closeContext, { event });
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
const teleportHtml = APP_TELEPORT_OPEN_TAG + (HAS_APP_TELEPORTS ? joinTags([ssrContext.teleports?.[`#${appTeleportAttrs.id}`]]) : "") + APP_TELEPORT_CLOSE_TAG;
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
const islandTeleports = NO_SCRIPTS ? "" : renderStreamedIslandTeleports(ssrContext, nonceAttr);
|
|
680
|
+
const closingHtml = APP_ROOT_CLOSE_TAG + await renderRouteStyles() + teleportHtml + (ssrContext.teleports?.body || "") + islandTeleports + joinTags(closeContext.bodyAppend) + "</body></html>";
|
|
681
|
+
await enqueueChunk(controller, encoder.encode(closingHtml));
|
|
682
|
+
controller.close();
|
|
683
|
+
if (committedSnapshot) {
|
|
684
|
+
const currentStatus = getResponseStatus(event);
|
|
685
|
+
const currentStatusText = getResponseStatusText(event);
|
|
686
|
+
const currentHeaders = snapshotResponseHeaders(event);
|
|
687
|
+
const lateMutations = [];
|
|
688
|
+
if (currentStatus !== committedSnapshot.status) {
|
|
689
|
+
lateMutations.push(`response status changed from ${committedSnapshot.status || 200} to ${currentStatus} (e.g. \`setResponseStatus\`)`);
|
|
690
|
+
}
|
|
691
|
+
if (currentStatusText !== committedSnapshot.statusText) {
|
|
692
|
+
lateMutations.push(`response statusText changed (e.g. \`setResponseStatus\`)`);
|
|
693
|
+
}
|
|
694
|
+
if (currentHeaders !== committedSnapshot.headers) {
|
|
695
|
+
lateMutations.push(`response headers changed during render (e.g. \`useCookie\`, \`useResponseHeader\`, \`setHeader\`)`);
|
|
696
|
+
}
|
|
697
|
+
if (lateMutations.length) {
|
|
698
|
+
console.warn(`[nuxt] SSR streaming committed the response before render completed. The following mutations did not reach the client and were dropped:\n - ${lateMutations.join("\n - ")}\n Path: ${event.path}\n Move the mutation into a plugin (which runs before the shell is flushed), or opt this route out of streaming with \`routeRules: { '${event.path}': { streaming: false } }\` or the \`render:route\` hook.`);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
} catch (error) {
|
|
702
|
+
|
|
703
|
+
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
|
|
707
|
+
await Promise.resolve(ssrContext.nuxt?.hooks.callHook("app:error", error)).catch(() => {});
|
|
708
|
+
ssrContext.payload ||= {};
|
|
709
|
+
ssrContext.payload.error ||= error;
|
|
710
|
+
try {
|
|
711
|
+
if (!NO_SCRIPTS) {
|
|
712
|
+
ssrContext.head.push({ script: NUXT_JSON_PAYLOADS ? renderPayloadJsonScript({
|
|
713
|
+
ssrContext,
|
|
714
|
+
data: ssrContext.payload
|
|
715
|
+
}) : renderPayloadScript({
|
|
716
|
+
ssrContext,
|
|
717
|
+
data: ssrContext.payload,
|
|
718
|
+
routeOptions
|
|
719
|
+
}) }, {
|
|
720
|
+
tagPosition: "bodyClose",
|
|
721
|
+
tagPriority: "high"
|
|
722
|
+
});
|
|
723
|
+
const tail = applyRenderOptions(ssrContext.head.render(), renderSSRHeadOptions);
|
|
724
|
+
controller.enqueue(encoder.encode(tail.bodyTags));
|
|
725
|
+
}
|
|
726
|
+
} catch {}
|
|
727
|
+
controller.enqueue(encoder.encode(APP_ROOT_CLOSE_TAG + "</body></html>"));
|
|
728
|
+
controller.close();
|
|
729
|
+
}
|
|
730
|
+
},
|
|
731
|
+
cancel(reason) {
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
|
|
735
|
+
reader.cancel(reason).catch(() => {});
|
|
736
|
+
}
|
|
737
|
+
});
|
|
738
|
+
setResponseHeader(event, "content-type", "text/html;charset=utf-8");
|
|
739
|
+
setResponseHeader(event, "x-powered-by", "Nuxt");
|
|
740
|
+
return { body: outputStream };
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
|
|
744
|
+
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
function applyRenderOptions(payload, options) {
|
|
748
|
+
if (!options.omitLineBreaks) {
|
|
749
|
+
return payload;
|
|
750
|
+
}
|
|
751
|
+
return {
|
|
752
|
+
headTags: payload.headTags.replaceAll("\n", ""),
|
|
753
|
+
bodyTags: payload.bodyTags.replaceAll("\n", ""),
|
|
754
|
+
bodyTagsOpen: payload.bodyTagsOpen.replaceAll("\n", ""),
|
|
755
|
+
htmlAttrs: payload.htmlAttrs,
|
|
756
|
+
bodyAttrs: payload.bodyAttrs
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
function snapshotResponseHeaders(event) {
|
|
760
|
+
const headers = event.node.res.getHeaders();
|
|
761
|
+
return Object.entries(headers).map(([k, v]) => `${k}: ${Array.isArray(v) ? v.join(", ") : v}`).sort().join("\n");
|
|
762
|
+
}
|
|
302
763
|
function normalizeChunks(chunks) {
|
|
303
764
|
const result = [];
|
|
304
765
|
for (const _chunk of chunks) {
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { RendererContext } from "vue-bundle-renderer/runtime";
|
|
2
|
+
import type { App } from "vue";
|
|
2
3
|
import type { NuxtSSRContext } from "nuxt/app";
|
|
4
|
+
type Entry = (ssrContext?: NuxtSSRContext) => Promise<App>;
|
|
5
|
+
export declare const APP_ROOT_OPEN_TAG: string;
|
|
6
|
+
export declare const APP_ROOT_CLOSE_TAG: string;
|
|
3
7
|
interface Renderer {
|
|
4
8
|
rendererContext: RendererContext;
|
|
5
9
|
renderToString(ssrContext: NuxtSSRContext): Promise<{
|
|
@@ -12,5 +16,6 @@ interface Renderer {
|
|
|
12
16
|
}
|
|
13
17
|
export declare const getSSRRenderer: () => Promise<Renderer>;
|
|
14
18
|
export declare function getRenderer(ssrContext: NuxtSSRContext): Promise<Renderer>;
|
|
19
|
+
export declare const getServerApp: () => Promise<Entry>;
|
|
15
20
|
export declare const getSSRStyles: () => Promise<Record<string, () => Promise<string[]>>>;
|
|
16
21
|
export {};
|
|
@@ -11,8 +11,8 @@ import { buildAssetsURL, publicAssetsURL } from "../paths.mjs";
|
|
|
11
11
|
globalThis.__buildAssetsURL = buildAssetsURL;
|
|
12
12
|
// @ts-expect-error private property consumed by vite-generated url helpers
|
|
13
13
|
globalThis.__publicAssetsURL = publicAssetsURL;
|
|
14
|
-
const APP_ROOT_OPEN_TAG = `<${appRootTag}${propsToString(appRootAttrs)}>`;
|
|
15
|
-
const APP_ROOT_CLOSE_TAG = `</${appRootTag}>`;
|
|
14
|
+
export const APP_ROOT_OPEN_TAG = `<${appRootTag}${propsToString(appRootAttrs)}>`;
|
|
15
|
+
export const APP_ROOT_CLOSE_TAG = `</${appRootTag}>`;
|
|
16
16
|
// @ts-expect-error file will be produced after app build
|
|
17
17
|
const getServerEntry = () => import("#build/dist/server/server.mjs").then((r) => r.default || r);
|
|
18
18
|
// @ts-expect-error file will be produced after app build
|
|
@@ -99,5 +99,7 @@ function lazyCachedFunction(fn) {
|
|
|
99
99
|
export function getRenderer(ssrContext) {
|
|
100
100
|
return NUXT_NO_SSR || ssrContext.noSSR ? getSPARenderer() : getSSRRenderer();
|
|
101
101
|
}
|
|
102
|
+
|
|
103
|
+
export const getServerApp = lazyCachedFunction(getServerEntry);
|
|
102
104
|
// @ts-expect-error file will be produced after app build
|
|
103
105
|
export const getSSRStyles = lazyCachedFunction(() => import("#build/dist/server/styles.mjs").then((r) => r.default || r));
|
|
@@ -6,4 +6,10 @@ export declare function getServerComponentHTML(body: string): string;
|
|
|
6
6
|
export declare function getSlotIslandResponse(ssrContext: NuxtSSRContext): NuxtIslandResponse["slots"];
|
|
7
7
|
export declare function getClientIslandResponse(ssrContext: NuxtSSRContext): NuxtIslandResponse["components"];
|
|
8
8
|
export declare function getComponentSlotTeleport(clientUid: string, teleports: Record<string, string>): Record<string, string>;
|
|
9
|
+
/**
|
|
10
|
+
* Emit each island teleport as an inert `<template>` keyed by its anchor,
|
|
11
|
+
* followed by a relocation script that moves the content into place before
|
|
12
|
+
* hydration. Returns an empty string when there are no island teleports.
|
|
13
|
+
*/
|
|
14
|
+
export declare function renderStreamedIslandTeleports(ssrContext: NuxtSSRContext, nonceAttr?: string): string;
|
|
9
15
|
export declare function replaceIslandTeleports(ssrContext: NuxtSSRContext, html: string): string;
|
|
@@ -53,6 +53,43 @@ export function getComponentSlotTeleport(clientUid, teleports) {
|
|
|
53
53
|
}
|
|
54
54
|
return slots;
|
|
55
55
|
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
const ISLAND_TELEPORT_RELOCATE_SCRIPT = `(()=>{for(const t of document.querySelectorAll('template[data-island-uid]')){const u=t.getAttribute('data-island-uid'),s=t.getAttribute('data-island-slot'),c=t.getAttribute('data-island-component'),a=document.querySelector('[data-island-uid="'+u+'"]'+(s!==null?'[data-island-slot="'+s+'"]':'[data-island-component="'+c+'"]'));if(a){a.insertBefore(t.content,a.firstChild)}t.remove()}})()`;
|
|
62
|
+
|
|
63
|
+
export function renderStreamedIslandTeleports(ssrContext, nonceAttr = "") {
|
|
64
|
+
const { teleports, islandContext } = ssrContext;
|
|
65
|
+
if (islandContext || !teleports) {
|
|
66
|
+
return "";
|
|
67
|
+
}
|
|
68
|
+
let templates = "";
|
|
69
|
+
for (const key in teleports) {
|
|
70
|
+
const matchClientComp = key.match(SSR_CLIENT_TELEPORT_MARKER);
|
|
71
|
+
if (matchClientComp) {
|
|
72
|
+
const [, uid, clientId] = matchClientComp;
|
|
73
|
+
if (!uid || !clientId) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
templates += `<template data-island-uid="${uid}" data-island-component="${clientId}">${teleports[key]}</template>`;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const matchSlot = key.match(SSR_SLOT_TELEPORT_MARKER);
|
|
80
|
+
if (matchSlot) {
|
|
81
|
+
const [, uid, slot] = matchSlot;
|
|
82
|
+
if (!uid || !slot) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
templates += `<template data-island-uid="${uid}" data-island-slot="${slot}">${teleports[key]}</template>`;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (!templates) {
|
|
89
|
+
return "";
|
|
90
|
+
}
|
|
91
|
+
return templates + `<script${nonceAttr}>${ISLAND_TELEPORT_RELOCATE_SCRIPT}<\/script>`;
|
|
92
|
+
}
|
|
56
93
|
export function replaceIslandTeleports(ssrContext, html) {
|
|
57
94
|
const { teleports, islandContext } = ssrContext;
|
|
58
95
|
if (islandContext || !teleports) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nuxt/nitro-server-nightly",
|
|
3
|
-
"version": "4.5.0-
|
|
3
|
+
"version": "4.5.0-29673775.d239dbcd",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/nuxt/nuxt.git",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@nuxt/devalue": "^2.0.2",
|
|
30
|
-
"@nuxt/kit": "npm:@nuxt/kit-nightly@4.5.0-
|
|
30
|
+
"@nuxt/kit": "npm:@nuxt/kit-nightly@4.5.0-29673775.d239dbcd",
|
|
31
31
|
"@unhead/vue": "^3.1.0",
|
|
32
32
|
"@vue/shared": "^3.5.35",
|
|
33
33
|
"consola": "^3.4.2",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"@babel/plugin-proposal-decorators": "^7.25.0",
|
|
59
59
|
"@babel/plugin-syntax-typescript": "^7.25.0",
|
|
60
60
|
"@rollup/plugin-babel": "^6.0.0 || ^7.0.0",
|
|
61
|
-
"nuxt": "npm:nuxt-nightly@4.5.0-
|
|
61
|
+
"nuxt": "npm:nuxt-nightly@4.5.0-29673775.d239dbcd"
|
|
62
62
|
},
|
|
63
63
|
"peerDependenciesMeta": {
|
|
64
64
|
"@babel/plugin-proposal-decorators": {
|
|
@@ -74,9 +74,9 @@
|
|
|
74
74
|
"devDependencies": {
|
|
75
75
|
"@babel/plugin-proposal-decorators": "7.29.7",
|
|
76
76
|
"@babel/plugin-syntax-typescript": "7.29.7",
|
|
77
|
-
"@nuxt/schema": "npm:@nuxt/schema-nightly@4.5.0-
|
|
77
|
+
"@nuxt/schema": "npm:@nuxt/schema-nightly@4.5.0-29673775.d239dbcd",
|
|
78
78
|
"@rollup/plugin-babel": "7.1.0",
|
|
79
|
-
"nuxt": "npm:nuxt-nightly@4.5.0-
|
|
79
|
+
"nuxt": "npm:nuxt-nightly@4.5.0-29673775.d239dbcd",
|
|
80
80
|
"obuild": "0.4.36",
|
|
81
81
|
"vitest": "4.1.7"
|
|
82
82
|
},
|