vinext 0.0.31 → 0.0.33
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 +10 -7
- package/dist/build/report.d.ts +1 -1
- package/dist/build/report.js +334 -0
- package/dist/build/report.js.map +1 -1
- package/dist/build/run-prerender.js +2 -2
- package/dist/build/run-prerender.js.map +1 -1
- package/dist/check.js +93 -3
- package/dist/check.js.map +1 -1
- package/dist/cli.js +3 -1
- package/dist/cli.js.map +1 -1
- package/dist/config/next-config.d.ts +2 -0
- package/dist/config/next-config.js +9 -3
- package/dist/config/next-config.js.map +1 -1
- package/dist/entries/app-browser-entry.js +3 -330
- package/dist/entries/app-browser-entry.js.map +1 -1
- package/dist/entries/app-rsc-entry.js +286 -644
- package/dist/entries/app-rsc-entry.js.map +1 -1
- package/dist/entries/app-ssr-entry.js +4 -460
- package/dist/entries/app-ssr-entry.js.map +1 -1
- package/dist/entries/pages-server-entry.js +2 -1
- package/dist/entries/pages-server-entry.js.map +1 -1
- package/dist/entries/runtime-entry-module.d.ts +13 -0
- package/dist/entries/runtime-entry-module.js +27 -0
- package/dist/entries/runtime-entry-module.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +16 -35
- package/dist/index.js.map +1 -1
- package/dist/plugins/optimize-imports.d.ts +38 -0
- package/dist/plugins/optimize-imports.js +557 -0
- package/dist/plugins/optimize-imports.js.map +1 -0
- package/dist/routing/pages-router.js +1 -1
- package/dist/routing/pages-router.js.map +1 -1
- package/dist/server/api-handler.d.ts +2 -2
- package/dist/server/api-handler.js +3 -4
- package/dist/server/api-handler.js.map +1 -1
- package/dist/server/app-browser-entry.d.ts +1 -0
- package/dist/server/app-browser-entry.js +161 -0
- package/dist/server/app-browser-entry.js.map +1 -0
- package/dist/server/app-browser-stream.d.ts +33 -0
- package/dist/server/app-browser-stream.js +54 -0
- package/dist/server/app-browser-stream.js.map +1 -0
- package/dist/server/app-page-cache.d.ts +61 -0
- package/dist/server/app-page-cache.js +133 -0
- package/dist/server/app-page-cache.js.map +1 -0
- package/dist/server/app-page-response.d.ts +51 -0
- package/dist/server/app-page-response.js +90 -0
- package/dist/server/app-page-response.js.map +1 -0
- package/dist/server/app-route-handler-cache.d.ts +42 -0
- package/dist/server/app-route-handler-cache.js +69 -0
- package/dist/server/app-route-handler-cache.js.map +1 -0
- package/dist/server/app-route-handler-execution.d.ts +64 -0
- package/dist/server/app-route-handler-execution.js +100 -0
- package/dist/server/app-route-handler-execution.js.map +1 -0
- package/dist/server/app-route-handler-policy.d.ts +51 -0
- package/dist/server/app-route-handler-policy.js +57 -0
- package/dist/server/app-route-handler-policy.js.map +1 -0
- package/dist/server/app-route-handler-response.d.ts +26 -0
- package/dist/server/app-route-handler-response.js +61 -0
- package/dist/server/app-route-handler-response.js.map +1 -0
- package/dist/server/app-route-handler-runtime.d.ts +27 -0
- package/dist/server/app-route-handler-runtime.js +99 -0
- package/dist/server/app-route-handler-runtime.js.map +1 -0
- package/dist/server/app-ssr-entry.d.ts +19 -0
- package/dist/server/app-ssr-entry.js +105 -0
- package/dist/server/app-ssr-entry.js.map +1 -0
- package/dist/server/app-ssr-stream.d.ts +30 -0
- package/dist/server/app-ssr-stream.js +116 -0
- package/dist/server/app-ssr-stream.js.map +1 -0
- package/dist/server/dev-server.d.ts +3 -2
- package/dist/server/dev-server.js +29 -30
- package/dist/server/dev-server.js.map +1 -1
- package/dist/server/instrumentation.d.ts +11 -39
- package/dist/server/instrumentation.js +14 -15
- package/dist/server/instrumentation.js.map +1 -1
- package/dist/server/middleware.d.ts +3 -2
- package/dist/server/middleware.js +12 -29
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/prod-server.js +3 -2
- package/dist/server/prod-server.js.map +1 -1
- package/dist/shims/compat-router.d.ts +3 -1
- package/dist/shims/compat-router.js.map +1 -1
- package/dist/shims/error-boundary.d.ts +1 -1
- package/dist/shims/fetch-cache.js +2 -0
- package/dist/shims/fetch-cache.js.map +1 -1
- package/dist/shims/head.d.ts +2 -1
- package/dist/shims/head.js +27 -5
- package/dist/shims/head.js.map +1 -1
- package/dist/shims/internal/router-context.d.ts +2 -1
- package/dist/shims/internal/router-context.js.map +1 -1
- package/dist/shims/metadata.js +3 -3
- package/dist/shims/metadata.js.map +1 -1
- package/dist/shims/request-state-types.d.ts +2 -2
- package/dist/shims/router.d.ts +1 -1
- package/dist/shims/router.js.map +1 -1
- package/dist/shims/server.d.ts +41 -3
- package/dist/shims/server.js +90 -7
- package/dist/shims/server.js.map +1 -1
- package/dist/shims/unified-request-context.d.ts +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-ssr-entry.js","names":["createReactElement"],"sources":["../../src/server/app-ssr-entry.ts"],"sourcesContent":["/// <reference types=\"@vitejs/plugin-rsc/types\" />\n\nimport type { ReactNode } from \"react\";\nimport { Fragment, createElement as createReactElement } from \"react\";\nimport { createFromReadableStream } from \"@vitejs/plugin-rsc/ssr\";\nimport { renderToReadableStream, renderToStaticMarkup } from \"react-dom/server.edge\";\nimport * as clientReferences from \"virtual:vite-rsc/client-references\";\nimport type { NavigationContext } from \"../shims/navigation.js\";\nimport {\n ServerInsertedHTMLContext,\n clearServerInsertedHTML,\n flushServerInsertedHTML,\n setNavigationContext,\n useServerInsertedHTML,\n} from \"../shims/navigation.js\";\nimport { runWithNavigationContext } from \"../shims/navigation-state.js\";\nimport { safeJsonStringify } from \"./html.js\";\nimport { createRscEmbedTransform, createTickBufferedTransform } from \"./app-ssr-stream.js\";\n\nexport interface FontPreload {\n href: string;\n type: string;\n}\n\nexport interface FontData {\n links?: string[];\n styles?: string[];\n preloads?: FontPreload[];\n}\n\ntype ClientRequire = (id: string) => Promise<unknown>;\n\nlet clientRefsPreloaded = false;\n\nfunction getClientReferenceRequire(): ClientRequire | undefined {\n return (\n globalThis as typeof globalThis & {\n __vite_rsc_client_require__?: ClientRequire;\n }\n ).__vite_rsc_client_require__;\n}\n\nasync function preloadClientReferences(): Promise<void> {\n if (clientRefsPreloaded) return;\n\n const refs = (clientReferences as { default?: Record<string, unknown> }).default;\n const clientRequire = getClientReferenceRequire();\n if (!refs || !clientRequire) return;\n\n await Promise.all(\n Object.keys(refs).map((id) =>\n clientRequire(id).catch((error) => {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\"[vinext] failed to preload client ref:\", id, error);\n }\n }),\n ),\n );\n\n clientRefsPreloaded = true;\n}\n\nfunction escapeHtmlAttr(value: string): string {\n return value.replace(/&/g, \"&\").replace(/\"/g, \""\");\n}\n\nfunction ssrErrorDigest(input: string): string {\n let hash = 5381;\n for (let i = input.length - 1; i >= 0; i--) {\n hash = (hash * 33) ^ input.charCodeAt(i);\n }\n return (hash >>> 0).toString();\n}\n\nfunction getErrorMessage(error: unknown): string {\n if (error instanceof Error) return error.message;\n if (typeof error === \"string\") return error;\n return Object.prototype.toString.call(error);\n}\n\nfunction renderInsertedHtml(insertedElements: readonly unknown[]): string {\n let insertedHTML = \"\";\n\n for (const element of insertedElements) {\n try {\n insertedHTML += renderToStaticMarkup(\n createReactElement(Fragment, null, element as ReactNode),\n );\n } catch {\n // Ignore individual callback failures so the rest of the page can render.\n }\n }\n\n return insertedHTML;\n}\n\nfunction renderFontHtml(fontData?: FontData): string {\n if (!fontData) return \"\";\n\n let fontHTML = \"\";\n\n for (const url of fontData.links ?? []) {\n fontHTML += `<link rel=\"stylesheet\" href=\"${escapeHtmlAttr(url)}\" />\\n`;\n }\n\n for (const preload of fontData.preloads ?? []) {\n fontHTML += `<link rel=\"preload\" href=\"${escapeHtmlAttr(preload.href)}\" as=\"font\" type=\"${escapeHtmlAttr(preload.type)}\" crossorigin />\\n`;\n }\n\n if (fontData.styles && fontData.styles.length > 0) {\n fontHTML += `<style data-vinext-fonts>${fontData.styles.join(\"\\n\")}</style>\\n`;\n }\n\n return fontHTML;\n}\n\nfunction extractModulePreloadHtml(bootstrapScriptContent?: string): string {\n if (!bootstrapScriptContent) return \"\";\n\n const match = bootstrapScriptContent.match(/import\\(\"([^\"]+)\"\\)/);\n if (!match?.[1]) return \"\";\n\n return `<link rel=\"modulepreload\" href=\"${escapeHtmlAttr(match[1])}\" />\\n`;\n}\n\nfunction buildHeadInjectionHtml(\n navContext: NavigationContext | null,\n bootstrapScriptContent: string | undefined,\n insertedHTML: string,\n fontHTML: string,\n): string {\n const paramsScript =\n \"<script>self.__VINEXT_RSC_PARAMS__=\" +\n safeJsonStringify(navContext?.params ?? {}) +\n \"</script>\";\n const navPayload = {\n pathname: navContext?.pathname ?? \"/\",\n searchParams: navContext?.searchParams ? [...navContext.searchParams.entries()] : [],\n };\n const navScript =\n \"<script>self.__VINEXT_RSC_NAV__=\" + safeJsonStringify(navPayload) + \"</script>\";\n\n return (\n paramsScript +\n navScript +\n extractModulePreloadHtml(bootstrapScriptContent) +\n insertedHTML +\n fontHTML\n );\n}\n\nexport async function handleSsr(\n rscStream: ReadableStream<Uint8Array>,\n navContext: NavigationContext | null,\n fontData?: FontData,\n): Promise<ReadableStream<Uint8Array>> {\n return runWithNavigationContext(async () => {\n await preloadClientReferences();\n\n if (navContext) {\n setNavigationContext(navContext);\n }\n\n clearServerInsertedHTML();\n\n try {\n const [ssrStream, embedStream] = rscStream.tee();\n const rscEmbed = createRscEmbedTransform(embedStream);\n\n let flightRoot: Promise<unknown> | null = null;\n\n function VinextFlightRoot(): ReactNode {\n if (!flightRoot) {\n flightRoot = createFromReadableStream(ssrStream);\n }\n return flightRoot as unknown as ReactNode;\n }\n\n const root = createReactElement(VinextFlightRoot);\n const ssrRoot = ServerInsertedHTMLContext\n ? createReactElement(\n ServerInsertedHTMLContext.Provider,\n { value: useServerInsertedHTML },\n root,\n )\n : root;\n\n const bootstrapScriptContent = await import.meta.viteRsc.loadBootstrapScriptContent(\"index\");\n\n const htmlStream = await renderToReadableStream(ssrRoot, {\n bootstrapScriptContent,\n onError(error) {\n if (error && typeof error === \"object\" && \"digest\" in error) {\n return String(error.digest);\n }\n\n if (process.env.NODE_ENV === \"production\" && error) {\n const message = getErrorMessage(error);\n const stack = error instanceof Error ? (error.stack ?? \"\") : \"\";\n return ssrErrorDigest(message + stack);\n }\n\n return undefined;\n },\n });\n\n const insertedHTML = renderInsertedHtml(flushServerInsertedHTML());\n const fontHTML = renderFontHtml(fontData);\n const injectHTML = buildHeadInjectionHtml(\n navContext,\n bootstrapScriptContent,\n insertedHTML,\n fontHTML,\n );\n\n return htmlStream.pipeThrough(createTickBufferedTransform(rscEmbed, injectHTML));\n } finally {\n setNavigationContext(null);\n clearServerInsertedHTML();\n }\n }) as Promise<ReadableStream<Uint8Array>>;\n}\n\nexport default {\n async fetch(request: Request): Promise<Response> {\n const url = new URL(request.url);\n if (url.pathname.startsWith(\"//\")) {\n return new Response(\"404 Not Found\", { status: 404 });\n }\n\n const rscModule = await import.meta.viteRsc.loadModule<{\n default(request: Request): Promise<Response | string | null | undefined>;\n }>(\"rsc\", \"index\");\n const result = await rscModule.default(request);\n\n if (result instanceof Response) {\n return result;\n }\n\n if (result == null) {\n return new Response(\"Not Found\", { status: 404 });\n }\n\n return new Response(String(result), { status: 200 });\n },\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//#region src/server/app-ssr-stream.d.ts
|
|
2
|
+
interface RscEmbedTransform {
|
|
3
|
+
flush(): string;
|
|
4
|
+
finalize(): Promise<string>;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Fix invalid preload "as" values in RSC Flight hint lines before they reach
|
|
8
|
+
* the client. React Flight emits HL hints with as="stylesheet" for CSS, but
|
|
9
|
+
* the HTML spec requires as="style" for <link rel="preload">.
|
|
10
|
+
*/
|
|
11
|
+
declare function fixFlightHints(text: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* Create a helper that progressively embeds RSC chunks as inline <script> tags.
|
|
14
|
+
* The browser entry turns the embedded text chunks back into Uint8Array data.
|
|
15
|
+
*/
|
|
16
|
+
declare function createRscEmbedTransform(embedStream: ReadableStream<Uint8Array>): RscEmbedTransform;
|
|
17
|
+
/**
|
|
18
|
+
* Fix invalid preload "as" values in server-rendered HTML.
|
|
19
|
+
* React Fizz emits <link rel="preload" as="stylesheet"> for CSS, but the
|
|
20
|
+
* HTML spec requires as="style" for <link rel="preload">.
|
|
21
|
+
*/
|
|
22
|
+
declare function fixPreloadAs(html: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Create the tick-buffered HTML transform that injects RSC scripts between
|
|
25
|
+
* React Fizz flush cycles without corrupting split HTML chunks.
|
|
26
|
+
*/
|
|
27
|
+
declare function createTickBufferedTransform(rscEmbed: RscEmbedTransform, injectHTML?: string): TransformStream<Uint8Array, Uint8Array>;
|
|
28
|
+
//#endregion
|
|
29
|
+
export { RscEmbedTransform, createRscEmbedTransform, createTickBufferedTransform, fixFlightHints, fixPreloadAs };
|
|
30
|
+
//# sourceMappingURL=app-ssr-stream.d.ts.map
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { safeJsonStringify } from "./html.js";
|
|
2
|
+
//#region src/server/app-ssr-stream.ts
|
|
3
|
+
/**
|
|
4
|
+
* Fix invalid preload "as" values in RSC Flight hint lines before they reach
|
|
5
|
+
* the client. React Flight emits HL hints with as="stylesheet" for CSS, but
|
|
6
|
+
* the HTML spec requires as="style" for <link rel="preload">.
|
|
7
|
+
*/
|
|
8
|
+
function fixFlightHints(text) {
|
|
9
|
+
return text.replace(/(\d+:HL\[.*?),"stylesheet"(\]|,)/g, "$1,\"style\"$2");
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Create a helper that progressively embeds RSC chunks as inline <script> tags.
|
|
13
|
+
* The browser entry turns the embedded text chunks back into Uint8Array data.
|
|
14
|
+
*/
|
|
15
|
+
function createRscEmbedTransform(embedStream) {
|
|
16
|
+
const reader = embedStream.getReader();
|
|
17
|
+
const decoder = new TextDecoder();
|
|
18
|
+
let pendingChunks = [];
|
|
19
|
+
let reading = false;
|
|
20
|
+
async function pumpReader() {
|
|
21
|
+
if (reading) return;
|
|
22
|
+
reading = true;
|
|
23
|
+
try {
|
|
24
|
+
while (true) {
|
|
25
|
+
const result = await reader.read();
|
|
26
|
+
if (result.done) break;
|
|
27
|
+
const text = decoder.decode(result.value, { stream: true });
|
|
28
|
+
pendingChunks.push(fixFlightHints(text));
|
|
29
|
+
}
|
|
30
|
+
} catch (error) {
|
|
31
|
+
if (process.env.NODE_ENV !== "production") console.warn("[vinext] RSC embed stream read error:", error);
|
|
32
|
+
} finally {
|
|
33
|
+
reading = false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const pumpPromise = pumpReader();
|
|
37
|
+
return {
|
|
38
|
+
flush() {
|
|
39
|
+
if (pendingChunks.length === 0) return "";
|
|
40
|
+
const chunks = pendingChunks;
|
|
41
|
+
pendingChunks = [];
|
|
42
|
+
let scripts = "";
|
|
43
|
+
for (const chunk of chunks) scripts += "<script>self.__VINEXT_RSC_CHUNKS__=self.__VINEXT_RSC_CHUNKS__||[];self.__VINEXT_RSC_CHUNKS__.push(" + safeJsonStringify(chunk) + ")<\/script>";
|
|
44
|
+
return scripts;
|
|
45
|
+
},
|
|
46
|
+
async finalize() {
|
|
47
|
+
await pumpPromise;
|
|
48
|
+
let scripts = this.flush();
|
|
49
|
+
scripts += "<script>self.__VINEXT_RSC_DONE__=true<\/script>";
|
|
50
|
+
return scripts;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Fix invalid preload "as" values in server-rendered HTML.
|
|
56
|
+
* React Fizz emits <link rel="preload" as="stylesheet"> for CSS, but the
|
|
57
|
+
* HTML spec requires as="style" for <link rel="preload">.
|
|
58
|
+
*/
|
|
59
|
+
function fixPreloadAs(html) {
|
|
60
|
+
return html.replace(/<link(?=[^>]*\srel="preload")[^>]*>/g, (tag) => {
|
|
61
|
+
return tag.replace(" as=\"stylesheet\"", " as=\"style\"");
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Create the tick-buffered HTML transform that injects RSC scripts between
|
|
66
|
+
* React Fizz flush cycles without corrupting split HTML chunks.
|
|
67
|
+
*/
|
|
68
|
+
function createTickBufferedTransform(rscEmbed, injectHTML = "") {
|
|
69
|
+
const decoder = new TextDecoder();
|
|
70
|
+
const encoder = new TextEncoder();
|
|
71
|
+
let injected = false;
|
|
72
|
+
let buffered = [];
|
|
73
|
+
let timeoutId = null;
|
|
74
|
+
const flushBuffered = (controller) => {
|
|
75
|
+
for (const chunk of buffered) {
|
|
76
|
+
if (!injected) {
|
|
77
|
+
const headEnd = chunk.indexOf("</head>");
|
|
78
|
+
if (headEnd !== -1) {
|
|
79
|
+
const before = chunk.slice(0, headEnd);
|
|
80
|
+
const after = chunk.slice(headEnd);
|
|
81
|
+
controller.enqueue(encoder.encode(before + injectHTML + after));
|
|
82
|
+
injected = true;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
controller.enqueue(encoder.encode(chunk));
|
|
87
|
+
}
|
|
88
|
+
buffered = [];
|
|
89
|
+
};
|
|
90
|
+
return new TransformStream({
|
|
91
|
+
transform(chunk, controller) {
|
|
92
|
+
buffered.push(fixPreloadAs(decoder.decode(chunk, { stream: true })));
|
|
93
|
+
if (timeoutId !== null) return;
|
|
94
|
+
timeoutId = setTimeout(() => {
|
|
95
|
+
flushBuffered(controller);
|
|
96
|
+
const rscScripts = rscEmbed.flush();
|
|
97
|
+
if (rscScripts) controller.enqueue(encoder.encode(rscScripts));
|
|
98
|
+
timeoutId = null;
|
|
99
|
+
}, 0);
|
|
100
|
+
},
|
|
101
|
+
async flush(controller) {
|
|
102
|
+
if (timeoutId !== null) {
|
|
103
|
+
clearTimeout(timeoutId);
|
|
104
|
+
timeoutId = null;
|
|
105
|
+
}
|
|
106
|
+
flushBuffered(controller);
|
|
107
|
+
if (!injected && injectHTML) controller.enqueue(encoder.encode(injectHTML));
|
|
108
|
+
const finalScripts = await rscEmbed.finalize();
|
|
109
|
+
if (finalScripts) controller.enqueue(encoder.encode(finalScripts));
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
//#endregion
|
|
114
|
+
export { createRscEmbedTransform, createTickBufferedTransform, fixFlightHints, fixPreloadAs };
|
|
115
|
+
|
|
116
|
+
//# sourceMappingURL=app-ssr-stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-ssr-stream.js","names":[],"sources":["../../src/server/app-ssr-stream.ts"],"sourcesContent":["import { safeJsonStringify } from \"./html.js\";\n\nexport interface RscEmbedTransform {\n flush(): string;\n finalize(): Promise<string>;\n}\n\n/**\n * Fix invalid preload \"as\" values in RSC Flight hint lines before they reach\n * the client. React Flight emits HL hints with as=\"stylesheet\" for CSS, but\n * the HTML spec requires as=\"style\" for <link rel=\"preload\">.\n */\nexport function fixFlightHints(text: string): string {\n return text.replace(/(\\d+:HL\\[.*?),\"stylesheet\"(\\]|,)/g, '$1,\"style\"$2');\n}\n\n/**\n * Create a helper that progressively embeds RSC chunks as inline <script> tags.\n * The browser entry turns the embedded text chunks back into Uint8Array data.\n */\nexport function createRscEmbedTransform(\n embedStream: ReadableStream<Uint8Array>,\n): RscEmbedTransform {\n const reader = embedStream.getReader();\n const decoder = new TextDecoder();\n let pendingChunks: string[] = [];\n let reading = false;\n\n async function pumpReader(): Promise<void> {\n if (reading) return;\n reading = true;\n try {\n while (true) {\n const result = await reader.read();\n if (result.done) break;\n const text = decoder.decode(result.value, { stream: true });\n // The RSC entry already fixes HL hints at the source. Keep this second\n // pass as defense in depth for any embed stream that bypasses that\n // wrapper; the rewrite is idempotent, so double-application is safe.\n pendingChunks.push(fixFlightHints(text));\n }\n } catch (error) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\"[vinext] RSC embed stream read error:\", error);\n }\n } finally {\n reading = false;\n }\n }\n\n const pumpPromise = pumpReader();\n\n return {\n flush(): string {\n if (pendingChunks.length === 0) return \"\";\n\n const chunks = pendingChunks;\n pendingChunks = [];\n\n let scripts = \"\";\n for (const chunk of chunks) {\n scripts +=\n \"<script>self.__VINEXT_RSC_CHUNKS__=self.__VINEXT_RSC_CHUNKS__||[];self.__VINEXT_RSC_CHUNKS__.push(\" +\n safeJsonStringify(chunk) +\n \")</script>\";\n }\n return scripts;\n },\n\n async finalize(): Promise<string> {\n await pumpPromise;\n let scripts = this.flush();\n scripts += \"<script>self.__VINEXT_RSC_DONE__=true</script>\";\n return scripts;\n },\n };\n}\n\n/**\n * Fix invalid preload \"as\" values in server-rendered HTML.\n * React Fizz emits <link rel=\"preload\" as=\"stylesheet\"> for CSS, but the\n * HTML spec requires as=\"style\" for <link rel=\"preload\">.\n */\nexport function fixPreloadAs(html: string): string {\n return html.replace(/<link(?=[^>]*\\srel=\"preload\")[^>]*>/g, (tag) => {\n return tag.replace(' as=\"stylesheet\"', ' as=\"style\"');\n });\n}\n\n/**\n * Create the tick-buffered HTML transform that injects RSC scripts between\n * React Fizz flush cycles without corrupting split HTML chunks.\n */\nexport function createTickBufferedTransform(\n rscEmbed: RscEmbedTransform,\n injectHTML = \"\",\n): TransformStream<Uint8Array, Uint8Array> {\n const decoder = new TextDecoder();\n const encoder = new TextEncoder();\n let injected = false;\n let buffered: string[] = [];\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n const flushBuffered = (controller: TransformStreamDefaultController<Uint8Array>): void => {\n for (const chunk of buffered) {\n if (!injected) {\n const headEnd = chunk.indexOf(\"</head>\");\n if (headEnd !== -1) {\n const before = chunk.slice(0, headEnd);\n const after = chunk.slice(headEnd);\n controller.enqueue(encoder.encode(before + injectHTML + after));\n injected = true;\n continue;\n }\n }\n controller.enqueue(encoder.encode(chunk));\n }\n buffered = [];\n };\n\n return new TransformStream<Uint8Array, Uint8Array>({\n transform(chunk, controller) {\n buffered.push(fixPreloadAs(decoder.decode(chunk, { stream: true })));\n\n if (timeoutId !== null) return;\n\n timeoutId = setTimeout(() => {\n flushBuffered(controller);\n\n const rscScripts = rscEmbed.flush();\n if (rscScripts) {\n controller.enqueue(encoder.encode(rscScripts));\n }\n\n timeoutId = null;\n }, 0);\n },\n\n async flush(controller) {\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n\n flushBuffered(controller);\n\n if (!injected && injectHTML) {\n controller.enqueue(encoder.encode(injectHTML));\n }\n\n const finalScripts = await rscEmbed.finalize();\n if (finalScripts) {\n controller.enqueue(encoder.encode(finalScripts));\n }\n },\n });\n}\n"],"mappings":";;;;;;;AAYA,SAAgB,eAAe,MAAsB;AACnD,QAAO,KAAK,QAAQ,qCAAqC,iBAAe;;;;;;AAO1E,SAAgB,wBACd,aACmB;CACnB,MAAM,SAAS,YAAY,WAAW;CACtC,MAAM,UAAU,IAAI,aAAa;CACjC,IAAI,gBAA0B,EAAE;CAChC,IAAI,UAAU;CAEd,eAAe,aAA4B;AACzC,MAAI,QAAS;AACb,YAAU;AACV,MAAI;AACF,UAAO,MAAM;IACX,MAAM,SAAS,MAAM,OAAO,MAAM;AAClC,QAAI,OAAO,KAAM;IACjB,MAAM,OAAO,QAAQ,OAAO,OAAO,OAAO,EAAE,QAAQ,MAAM,CAAC;AAI3D,kBAAc,KAAK,eAAe,KAAK,CAAC;;WAEnC,OAAO;AACd,OAAI,QAAQ,IAAI,aAAa,aAC3B,SAAQ,KAAK,yCAAyC,MAAM;YAEtD;AACR,aAAU;;;CAId,MAAM,cAAc,YAAY;AAEhC,QAAO;EACL,QAAgB;AACd,OAAI,cAAc,WAAW,EAAG,QAAO;GAEvC,MAAM,SAAS;AACf,mBAAgB,EAAE;GAElB,IAAI,UAAU;AACd,QAAK,MAAM,SAAS,OAClB,YACE,uGACA,kBAAkB,MAAM,GACxB;AAEJ,UAAO;;EAGT,MAAM,WAA4B;AAChC,SAAM;GACN,IAAI,UAAU,KAAK,OAAO;AAC1B,cAAW;AACX,UAAO;;EAEV;;;;;;;AAQH,SAAgB,aAAa,MAAsB;AACjD,QAAO,KAAK,QAAQ,yCAAyC,QAAQ;AACnE,SAAO,IAAI,QAAQ,sBAAoB,gBAAc;GACrD;;;;;;AAOJ,SAAgB,4BACd,UACA,aAAa,IAC4B;CACzC,MAAM,UAAU,IAAI,aAAa;CACjC,MAAM,UAAU,IAAI,aAAa;CACjC,IAAI,WAAW;CACf,IAAI,WAAqB,EAAE;CAC3B,IAAI,YAAkD;CAEtD,MAAM,iBAAiB,eAAmE;AACxF,OAAK,MAAM,SAAS,UAAU;AAC5B,OAAI,CAAC,UAAU;IACb,MAAM,UAAU,MAAM,QAAQ,UAAU;AACxC,QAAI,YAAY,IAAI;KAClB,MAAM,SAAS,MAAM,MAAM,GAAG,QAAQ;KACtC,MAAM,QAAQ,MAAM,MAAM,QAAQ;AAClC,gBAAW,QAAQ,QAAQ,OAAO,SAAS,aAAa,MAAM,CAAC;AAC/D,gBAAW;AACX;;;AAGJ,cAAW,QAAQ,QAAQ,OAAO,MAAM,CAAC;;AAE3C,aAAW,EAAE;;AAGf,QAAO,IAAI,gBAAwC;EACjD,UAAU,OAAO,YAAY;AAC3B,YAAS,KAAK,aAAa,QAAQ,OAAO,OAAO,EAAE,QAAQ,MAAM,CAAC,CAAC,CAAC;AAEpE,OAAI,cAAc,KAAM;AAExB,eAAY,iBAAiB;AAC3B,kBAAc,WAAW;IAEzB,MAAM,aAAa,SAAS,OAAO;AACnC,QAAI,WACF,YAAW,QAAQ,QAAQ,OAAO,WAAW,CAAC;AAGhD,gBAAY;MACX,EAAE;;EAGP,MAAM,MAAM,YAAY;AACtB,OAAI,cAAc,MAAM;AACtB,iBAAa,UAAU;AACvB,gBAAY;;AAGd,iBAAc,WAAW;AAEzB,OAAI,CAAC,YAAY,WACf,YAAW,QAAQ,QAAQ,OAAO,WAAW,CAAC;GAGhD,MAAM,eAAe,MAAM,SAAS,UAAU;AAC9C,OAAI,aACF,YAAW,QAAQ,QAAQ,OAAO,aAAa,CAAC;;EAGrD,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ValidFileMatcher } from "../routing/file-matcher.js";
|
|
2
2
|
import { Route } from "../routing/pages-router.js";
|
|
3
3
|
import { NextI18nConfig } from "../config/next-config.js";
|
|
4
|
+
import { ModuleImporter } from "./instrumentation.js";
|
|
4
5
|
import { ViteDevServer } from "vite";
|
|
5
6
|
import { IncomingMessage, ServerResponse } from "node:http";
|
|
6
7
|
|
|
@@ -30,12 +31,12 @@ declare function parseCookieLocale(req: IncomingMessage, i18nConfig: NextI18nCon
|
|
|
30
31
|
*
|
|
31
32
|
* For each request:
|
|
32
33
|
* 1. Match the URL against discovered routes
|
|
33
|
-
* 2. Load the page module via
|
|
34
|
+
* 2. Load the page module via the ModuleRunner
|
|
34
35
|
* 3. Call getServerSideProps/getStaticProps if present
|
|
35
36
|
* 4. Render the component to HTML
|
|
36
37
|
* 5. Wrap in _document shell and send response
|
|
37
38
|
*/
|
|
38
|
-
declare function createSSRHandler(server: ViteDevServer, routes: Route[], pagesDir: string, i18nConfig?: NextI18nConfig | null, fileMatcher?: ValidFileMatcher, basePath?: string, trailingSlash?: boolean): (req: IncomingMessage, res: ServerResponse, url: string, /** Status code override — propagated from middleware rewrite status. */
|
|
39
|
+
declare function createSSRHandler(server: ViteDevServer, runner: ModuleImporter, routes: Route[], pagesDir: string, i18nConfig?: NextI18nConfig | null, fileMatcher?: ValidFileMatcher, basePath?: string, trailingSlash?: boolean): (req: IncomingMessage, res: ServerResponse, url: string, /** Status code override — propagated from middleware rewrite status. */
|
|
39
40
|
|
|
40
41
|
statusCode?: number) => Promise<void>;
|
|
41
42
|
//#endregion
|
|
@@ -2,6 +2,7 @@ import { createValidFileMatcher } from "../routing/file-matcher.js";
|
|
|
2
2
|
import { patternToNextFormat } from "../routing/route-validation.js";
|
|
3
3
|
import { matchRoute } from "../routing/pages-router.js";
|
|
4
4
|
import { createRequestContext, runWithRequestContext } from "../shims/unified-request-context.js";
|
|
5
|
+
import { importModule, reportRequestError } from "./instrumentation.js";
|
|
5
6
|
import { _runWithCacheState } from "../shims/cache.js";
|
|
6
7
|
import { buildPagesCacheValue, getRevalidateDuration, isrCacheKey, isrGet, isrSet, setRevalidateDuration, triggerBackgroundRegeneration } from "./isr-cache.js";
|
|
7
8
|
import { runWithPrivateCache } from "../shims/cache-runtime.js";
|
|
@@ -10,7 +11,6 @@ import { parseQueryString } from "../utils/query.js";
|
|
|
10
11
|
import "../shims/router-state.js";
|
|
11
12
|
import { runWithHeadState } from "../shims/head-state.js";
|
|
12
13
|
import { runWithServerInsertedHTMLState } from "../shims/navigation-state.js";
|
|
13
|
-
import { reportRequestError } from "./instrumentation.js";
|
|
14
14
|
import { safeJsonStringify } from "./html.js";
|
|
15
15
|
import { logRequest, now } from "./request-log.js";
|
|
16
16
|
import { detectLocaleFromAcceptLanguage, extractLocaleFromUrl as extractLocaleFromUrl$1, parseCookieLocaleFromHeader, resolvePagesI18nRequest } from "./pages-i18n.js";
|
|
@@ -128,14 +128,14 @@ function parseCookieLocale(req, i18nConfig) {
|
|
|
128
128
|
*
|
|
129
129
|
* For each request:
|
|
130
130
|
* 1. Match the URL against discovered routes
|
|
131
|
-
* 2. Load the page module via
|
|
131
|
+
* 2. Load the page module via the ModuleRunner
|
|
132
132
|
* 3. Call getServerSideProps/getStaticProps if present
|
|
133
133
|
* 4. Render the component to HTML
|
|
134
134
|
* 5. Wrap in _document shell and send response
|
|
135
135
|
*/
|
|
136
|
-
function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatcher, basePath = "", trailingSlash = false) {
|
|
136
|
+
function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatcher, basePath = "", trailingSlash = false) {
|
|
137
137
|
const matcher = fileMatcher ?? createValidFileMatcher();
|
|
138
|
-
const _alsRegistration = Promise.all([
|
|
138
|
+
const _alsRegistration = Promise.all([runner.import("vinext/head-state"), runner.import("vinext/router-state")]);
|
|
139
139
|
_alsRegistration.catch(() => {});
|
|
140
140
|
return async (req, res, url, statusCode) => {
|
|
141
141
|
const _reqStart = now();
|
|
@@ -171,7 +171,7 @@ function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatcher, bas
|
|
|
171
171
|
}
|
|
172
172
|
const match = matchRoute(localeStrippedUrl, routes);
|
|
173
173
|
if (!match) {
|
|
174
|
-
await renderErrorPage(server, req, res, url, pagesDir, 404, void 0, matcher);
|
|
174
|
+
await renderErrorPage(server, runner, req, res, url, pagesDir, 404, void 0, matcher);
|
|
175
175
|
return;
|
|
176
176
|
}
|
|
177
177
|
const { route, params } = match;
|
|
@@ -179,7 +179,7 @@ function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatcher, bas
|
|
|
179
179
|
ensureFetchPatch();
|
|
180
180
|
try {
|
|
181
181
|
await _alsRegistration;
|
|
182
|
-
const routerShim = await
|
|
182
|
+
const routerShim = await importModule(runner, "next/router");
|
|
183
183
|
if (typeof routerShim.setSSRContext === "function") routerShim.setSSRContext({
|
|
184
184
|
pathname: patternToNextFormat(route.pattern),
|
|
185
185
|
query: {
|
|
@@ -193,8 +193,8 @@ function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatcher, bas
|
|
|
193
193
|
domainLocales
|
|
194
194
|
});
|
|
195
195
|
if (i18nConfig) {
|
|
196
|
-
await
|
|
197
|
-
const i18nCtx = await
|
|
196
|
+
await runner.import("vinext/i18n-state");
|
|
197
|
+
const i18nCtx = await importModule(runner, "vinext/i18n-context");
|
|
198
198
|
if (typeof i18nCtx.setI18nContext === "function") i18nCtx.setI18nContext({
|
|
199
199
|
locale: locale ?? currentDefaultLocale,
|
|
200
200
|
locales: i18nConfig.locales,
|
|
@@ -203,7 +203,7 @@ function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatcher, bas
|
|
|
203
203
|
hostname: req.headers.host?.split(":", 1)[0]
|
|
204
204
|
});
|
|
205
205
|
}
|
|
206
|
-
const pageModule = await
|
|
206
|
+
const pageModule = await importModule(runner, route.filePath);
|
|
207
207
|
_compileEnd = now();
|
|
208
208
|
const PageComponent = pageModule.default;
|
|
209
209
|
if (!PageComponent) {
|
|
@@ -227,7 +227,7 @@ function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatcher, bas
|
|
|
227
227
|
return String(val) === String(actual);
|
|
228
228
|
});
|
|
229
229
|
})) {
|
|
230
|
-
await renderErrorPage(server, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext, matcher);
|
|
230
|
+
await renderErrorPage(server, runner, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext, matcher);
|
|
231
231
|
return;
|
|
232
232
|
}
|
|
233
233
|
}
|
|
@@ -258,7 +258,7 @@ function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatcher, bas
|
|
|
258
258
|
return;
|
|
259
259
|
}
|
|
260
260
|
if (result && "notFound" in result && result.notFound) {
|
|
261
|
-
await renderErrorPage(server, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext);
|
|
261
|
+
await renderErrorPage(server, runner, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext);
|
|
262
262
|
return;
|
|
263
263
|
}
|
|
264
264
|
if (!statusCode && res.statusCode !== 200) statusCode = res.statusCode;
|
|
@@ -273,9 +273,9 @@ function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatcher, bas
|
|
|
273
273
|
let earlyFontLinkHeader = "";
|
|
274
274
|
try {
|
|
275
275
|
const earlyPreloads = [];
|
|
276
|
-
const fontGoogleEarly = await
|
|
276
|
+
const fontGoogleEarly = await importModule(runner, "next/font/google");
|
|
277
277
|
if (typeof fontGoogleEarly.getSSRFontPreloads === "function") earlyPreloads.push(...fontGoogleEarly.getSSRFontPreloads());
|
|
278
|
-
const fontLocalEarly = await
|
|
278
|
+
const fontLocalEarly = await importModule(runner, "next/font/local");
|
|
279
279
|
if (typeof fontLocalEarly.getSSRFontPreloads === "function") earlyPreloads.push(...fontLocalEarly.getSSRFontPreloads());
|
|
280
280
|
if (earlyPreloads.length > 0) earlyFontLinkHeader = earlyPreloads.map((p) => `<${p.href}>; rel=preload; as=font; type=${p.type}; crossorigin`).join(", ");
|
|
281
281
|
} catch {}
|
|
@@ -325,8 +325,8 @@ function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatcher, bas
|
|
|
325
325
|
domainLocales
|
|
326
326
|
});
|
|
327
327
|
if (i18nConfig) {
|
|
328
|
-
await
|
|
329
|
-
const i18nCtx = await
|
|
328
|
+
await runner.import("vinext/i18n-state");
|
|
329
|
+
const i18nCtx = await importModule(runner, "vinext/i18n-context");
|
|
330
330
|
if (typeof i18nCtx.setI18nContext === "function") i18nCtx.setI18nContext({
|
|
331
331
|
locale: locale ?? currentDefaultLocale,
|
|
332
332
|
locales: i18nConfig.locales,
|
|
@@ -338,7 +338,7 @@ function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatcher, bas
|
|
|
338
338
|
let RegenApp = null;
|
|
339
339
|
const appPath = path.join(pagesDir, "_app");
|
|
340
340
|
if (findFileWithExtensions(appPath, matcher)) try {
|
|
341
|
-
RegenApp = (await
|
|
341
|
+
RegenApp = (await runner.import(appPath)).default ?? null;
|
|
342
342
|
} catch {}
|
|
343
343
|
let el = RegenApp ? React.createElement(RegenApp, {
|
|
344
344
|
Component: pageModule.default,
|
|
@@ -398,7 +398,7 @@ function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatcher, bas
|
|
|
398
398
|
return;
|
|
399
399
|
}
|
|
400
400
|
if (result && "notFound" in result && result.notFound) {
|
|
401
|
-
await renderErrorPage(server, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext);
|
|
401
|
+
await renderErrorPage(server, runner, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext);
|
|
402
402
|
return;
|
|
403
403
|
}
|
|
404
404
|
if (typeof result?.revalidate === "number" && result.revalidate > 0) isrRevalidateSeconds = result.revalidate;
|
|
@@ -406,7 +406,7 @@ function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatcher, bas
|
|
|
406
406
|
let AppComponent = null;
|
|
407
407
|
const appPath = path.join(pagesDir, "_app");
|
|
408
408
|
if (findFileWithExtensions(appPath, matcher)) try {
|
|
409
|
-
AppComponent = (await
|
|
409
|
+
AppComponent = (await importModule(runner, appPath)).default ?? null;
|
|
410
410
|
} catch {}
|
|
411
411
|
const createElement = React.createElement;
|
|
412
412
|
let element;
|
|
@@ -417,15 +417,15 @@ function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatcher, bas
|
|
|
417
417
|
});
|
|
418
418
|
else element = createElement(PageComponent, pageProps);
|
|
419
419
|
if (wrapWithRouterContext) element = wrapWithRouterContext(element);
|
|
420
|
-
const headShim = await
|
|
420
|
+
const headShim = await importModule(runner, "next/head");
|
|
421
421
|
if (typeof headShim.resetSSRHead === "function") headShim.resetSSRHead();
|
|
422
|
-
const dynamicShim = await
|
|
422
|
+
const dynamicShim = await importModule(runner, "next/dynamic");
|
|
423
423
|
if (typeof dynamicShim.flushPreloads === "function") await dynamicShim.flushPreloads();
|
|
424
424
|
let fontHeadHTML = "";
|
|
425
425
|
const allFontStyles = [];
|
|
426
426
|
const allFontPreloads = [];
|
|
427
427
|
try {
|
|
428
|
-
const fontGoogle = await
|
|
428
|
+
const fontGoogle = await importModule(runner, "next/font/google");
|
|
429
429
|
if (typeof fontGoogle.getSSRFontLinks === "function") {
|
|
430
430
|
const fontUrls = fontGoogle.getSSRFontLinks();
|
|
431
431
|
for (const fontUrl of fontUrls) {
|
|
@@ -437,7 +437,7 @@ function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatcher, bas
|
|
|
437
437
|
if (typeof fontGoogle.getSSRFontPreloads === "function") allFontPreloads.push(...fontGoogle.getSSRFontPreloads());
|
|
438
438
|
} catch {}
|
|
439
439
|
try {
|
|
440
|
-
const fontLocal = await
|
|
440
|
+
const fontLocal = await importModule(runner, "next/font/local");
|
|
441
441
|
if (typeof fontLocal.getSSRFontStyles === "function") allFontStyles.push(...fontLocal.getSSRFontStyles());
|
|
442
442
|
if (typeof fontLocal.getSSRFontPreloads === "function") allFontPreloads.push(...fontLocal.getSSRFontPreloads());
|
|
443
443
|
} catch {}
|
|
@@ -495,7 +495,7 @@ hydrate();
|
|
|
495
495
|
const docPath = path.join(pagesDir, "_document");
|
|
496
496
|
let DocumentComponent = null;
|
|
497
497
|
if (findFileWithExtensions(docPath, matcher)) try {
|
|
498
|
-
DocumentComponent = (await
|
|
498
|
+
DocumentComponent = (await runner.import(docPath)).default ?? null;
|
|
499
499
|
} catch {}
|
|
500
500
|
const allScripts = `${nextDataScript}\n ${hydrationScript}`;
|
|
501
501
|
const extraHeaders = { ...gsspExtraHeaders };
|
|
@@ -528,7 +528,6 @@ hydrate();
|
|
|
528
528
|
setRevalidateDuration(cacheKey, isrRevalidateSeconds);
|
|
529
529
|
}
|
|
530
530
|
} catch (e) {
|
|
531
|
-
server.ssrFixStacktrace?.(e);
|
|
532
531
|
console.error(e);
|
|
533
532
|
reportRequestError(e instanceof Error ? e : new Error(String(e)), {
|
|
534
533
|
path: url,
|
|
@@ -540,7 +539,7 @@ hydrate();
|
|
|
540
539
|
routeType: "render"
|
|
541
540
|
}).catch(() => {});
|
|
542
541
|
try {
|
|
543
|
-
await renderErrorPage(server, req, res, url, pagesDir, 500, void 0, matcher);
|
|
542
|
+
await renderErrorPage(server, runner, req, res, url, pagesDir, 500, void 0, matcher);
|
|
544
543
|
} catch (fallbackErr) {
|
|
545
544
|
res.statusCode = 500;
|
|
546
545
|
res.end(`Internal Server Error: ${fallbackErr.message}`);
|
|
@@ -557,24 +556,24 @@ hydrate();
|
|
|
557
556
|
* - 500: pages/500.tsx -> pages/_error.tsx -> default
|
|
558
557
|
* - other: pages/_error.tsx -> default
|
|
559
558
|
*/
|
|
560
|
-
async function renderErrorPage(server, _req, res, url, pagesDir, statusCode, wrapWithRouterContext, fileMatcher) {
|
|
559
|
+
async function renderErrorPage(server, runner, _req, res, url, pagesDir, statusCode, wrapWithRouterContext, fileMatcher) {
|
|
561
560
|
const matcher = fileMatcher ?? createValidFileMatcher();
|
|
562
561
|
const candidates = statusCode === 404 ? ["404", "_error"] : statusCode === 500 ? ["500", "_error"] : ["_error"];
|
|
563
562
|
for (const candidate of candidates) try {
|
|
564
563
|
const candidatePath = path.join(pagesDir, candidate);
|
|
565
564
|
if (!findFileWithExtensions(candidatePath, matcher)) continue;
|
|
566
|
-
const ErrorComponent = (await
|
|
565
|
+
const ErrorComponent = (await importModule(runner, candidatePath)).default;
|
|
567
566
|
if (!ErrorComponent) continue;
|
|
568
567
|
let AppComponent = null;
|
|
569
568
|
const appPathErr = path.join(pagesDir, "_app");
|
|
570
569
|
if (findFileWithExtensions(appPathErr, matcher)) try {
|
|
571
|
-
AppComponent = (await
|
|
570
|
+
AppComponent = (await importModule(runner, appPathErr)).default ?? null;
|
|
572
571
|
} catch {}
|
|
573
572
|
const createElement = React.createElement;
|
|
574
573
|
const errorProps = { statusCode };
|
|
575
574
|
let wrapFn = wrapWithRouterContext;
|
|
576
575
|
if (!wrapFn) try {
|
|
577
|
-
wrapFn = (await
|
|
576
|
+
wrapFn = (await importModule(runner, "next/router")).wrapWithRouterContext;
|
|
578
577
|
} catch {}
|
|
579
578
|
let element;
|
|
580
579
|
if (AppComponent) element = createElement(AppComponent, {
|
|
@@ -588,7 +587,7 @@ async function renderErrorPage(server, _req, res, url, pagesDir, statusCode, wra
|
|
|
588
587
|
let DocumentComponent = null;
|
|
589
588
|
const docPathErr = path.join(pagesDir, "_document");
|
|
590
589
|
if (findFileWithExtensions(docPathErr, matcher)) try {
|
|
591
|
-
DocumentComponent = (await
|
|
590
|
+
DocumentComponent = (await importModule(runner, docPathErr)).default ?? null;
|
|
592
591
|
} catch {}
|
|
593
592
|
if (DocumentComponent) {
|
|
594
593
|
let docHtml = await renderToStringAsync(createElement(DocumentComponent));
|