vinext 0.0.28 → 0.0.30
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/build/report.d.ts +117 -0
- package/dist/build/report.d.ts.map +1 -0
- package/dist/build/report.js +303 -0
- package/dist/build/report.js.map +1 -0
- package/dist/check.d.ts.map +1 -1
- package/dist/check.js +11 -7
- package/dist/check.js.map +1 -1
- package/dist/cli.js +106 -9
- package/dist/cli.js.map +1 -1
- package/dist/cloudflare/kv-cache-handler.d.ts.map +1 -1
- package/dist/cloudflare/kv-cache-handler.js +58 -42
- package/dist/cloudflare/kv-cache-handler.js.map +1 -1
- package/dist/cloudflare/tpr.d.ts +10 -0
- package/dist/cloudflare/tpr.d.ts.map +1 -1
- package/dist/cloudflare/tpr.js +36 -41
- package/dist/cloudflare/tpr.js.map +1 -1
- package/dist/config/next-config.d.ts.map +1 -1
- package/dist/config/next-config.js +16 -0
- package/dist/config/next-config.js.map +1 -1
- package/dist/entries/app-rsc-entry.d.ts +1 -1
- package/dist/entries/app-rsc-entry.d.ts.map +1 -1
- package/dist/entries/app-rsc-entry.js +225 -186
- package/dist/entries/app-rsc-entry.js.map +1 -1
- package/dist/entries/pages-server-entry.d.ts.map +1 -1
- package/dist/entries/pages-server-entry.js +192 -91
- package/dist/entries/pages-server-entry.js.map +1 -1
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +121 -40
- package/dist/index.js.map +1 -1
- package/dist/routing/app-router.d.ts +2 -0
- package/dist/routing/app-router.d.ts.map +1 -1
- package/dist/routing/app-router.js +131 -104
- package/dist/routing/app-router.js.map +1 -1
- package/dist/routing/pages-router.d.ts.map +1 -1
- package/dist/routing/pages-router.js +17 -57
- package/dist/routing/pages-router.js.map +1 -1
- package/dist/routing/route-trie.d.ts +57 -0
- package/dist/routing/route-trie.d.ts.map +1 -0
- package/dist/routing/route-trie.js +160 -0
- package/dist/routing/route-trie.js.map +1 -0
- package/dist/routing/route-validation.d.ts.map +1 -1
- package/dist/routing/route-validation.js +13 -1
- package/dist/routing/route-validation.js.map +1 -1
- package/dist/routing/utils.d.ts +19 -0
- package/dist/routing/utils.d.ts.map +1 -1
- package/dist/routing/utils.js +47 -0
- package/dist/routing/utils.js.map +1 -1
- package/dist/server/api-handler.d.ts.map +1 -1
- package/dist/server/api-handler.js +28 -13
- package/dist/server/api-handler.js.map +1 -1
- package/dist/server/app-router-entry.js +1 -1
- package/dist/server/app-router-entry.js.map +1 -1
- package/dist/server/dev-server.d.ts +2 -1
- package/dist/server/dev-server.d.ts.map +1 -1
- package/dist/server/dev-server.js +167 -115
- package/dist/server/dev-server.js.map +1 -1
- package/dist/server/image-optimization.d.ts.map +1 -1
- package/dist/server/image-optimization.js +24 -12
- package/dist/server/image-optimization.js.map +1 -1
- package/dist/server/instrumentation.d.ts.map +1 -1
- package/dist/server/instrumentation.js +17 -8
- package/dist/server/instrumentation.js.map +1 -1
- package/dist/server/isr-cache.d.ts.map +1 -1
- package/dist/server/isr-cache.js +8 -3
- package/dist/server/isr-cache.js.map +1 -1
- package/dist/server/metadata-routes.d.ts.map +1 -1
- package/dist/server/metadata-routes.js +56 -18
- package/dist/server/metadata-routes.js.map +1 -1
- package/dist/server/middleware-codegen.d.ts +10 -0
- package/dist/server/middleware-codegen.d.ts.map +1 -1
- package/dist/server/middleware-codegen.js +76 -4
- package/dist/server/middleware-codegen.js.map +1 -1
- package/dist/server/middleware.d.ts.map +1 -1
- package/dist/server/middleware.js +52 -7
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/pages-i18n.d.ts +50 -0
- package/dist/server/pages-i18n.d.ts.map +1 -0
- package/dist/server/pages-i18n.js +152 -0
- package/dist/server/pages-i18n.js.map +1 -0
- package/dist/server/prod-server.d.ts +8 -2
- package/dist/server/prod-server.d.ts.map +1 -1
- package/dist/server/prod-server.js +60 -20
- package/dist/server/prod-server.js.map +1 -1
- package/dist/shims/cache-runtime.d.ts +3 -0
- package/dist/shims/cache-runtime.d.ts.map +1 -1
- package/dist/shims/cache-runtime.js +22 -5
- package/dist/shims/cache-runtime.js.map +1 -1
- package/dist/shims/cache.d.ts +3 -0
- package/dist/shims/cache.d.ts.map +1 -1
- package/dist/shims/cache.js +21 -12
- package/dist/shims/cache.js.map +1 -1
- package/dist/shims/constants.d.ts.map +1 -1
- package/dist/shims/constants.js +0 -1
- package/dist/shims/constants.js.map +1 -1
- package/dist/shims/fetch-cache.d.ts +14 -0
- package/dist/shims/fetch-cache.d.ts.map +1 -1
- package/dist/shims/fetch-cache.js +102 -37
- package/dist/shims/fetch-cache.js.map +1 -1
- package/dist/shims/head-state.d.ts +4 -0
- package/dist/shims/head-state.d.ts.map +1 -1
- package/dist/shims/head-state.js +14 -11
- package/dist/shims/head-state.js.map +1 -1
- package/dist/shims/head.d.ts +4 -2
- package/dist/shims/head.d.ts.map +1 -1
- package/dist/shims/head.js +162 -52
- package/dist/shims/head.js.map +1 -1
- package/dist/shims/headers.d.ts +8 -1
- package/dist/shims/headers.d.ts.map +1 -1
- package/dist/shims/headers.js +23 -34
- package/dist/shims/headers.js.map +1 -1
- package/dist/shims/i18n-context.d.ts +27 -0
- package/dist/shims/i18n-context.d.ts.map +1 -0
- package/dist/shims/i18n-context.js +57 -0
- package/dist/shims/i18n-context.js.map +1 -0
- package/dist/shims/i18n-state.d.ts +20 -0
- package/dist/shims/i18n-state.d.ts.map +1 -0
- package/dist/shims/i18n-state.js +53 -0
- package/dist/shims/i18n-state.js.map +1 -0
- package/dist/shims/image.d.ts +2 -0
- package/dist/shims/image.d.ts.map +1 -1
- package/dist/shims/image.js +14 -6
- package/dist/shims/image.js.map +1 -1
- package/dist/shims/internal/utils.d.ts +1 -0
- package/dist/shims/internal/utils.d.ts.map +1 -1
- package/dist/shims/internal/utils.js.map +1 -1
- package/dist/shims/link.d.ts.map +1 -1
- package/dist/shims/link.js +38 -54
- package/dist/shims/link.js.map +1 -1
- package/dist/shims/metadata.d.ts +78 -22
- package/dist/shims/metadata.d.ts.map +1 -1
- package/dist/shims/metadata.js +96 -28
- package/dist/shims/metadata.js.map +1 -1
- package/dist/shims/navigation-state.d.ts +14 -0
- package/dist/shims/navigation-state.d.ts.map +1 -1
- package/dist/shims/navigation-state.js +33 -15
- package/dist/shims/navigation-state.js.map +1 -1
- package/dist/shims/navigation.d.ts +2 -0
- package/dist/shims/navigation.d.ts.map +1 -1
- package/dist/shims/navigation.js +80 -51
- package/dist/shims/navigation.js.map +1 -1
- package/dist/shims/request-context.d.ts.map +1 -1
- package/dist/shims/request-context.js +9 -0
- package/dist/shims/request-context.js.map +1 -1
- package/dist/shims/request-state-types.d.ts +11 -0
- package/dist/shims/request-state-types.d.ts.map +1 -0
- package/dist/shims/request-state-types.js +2 -0
- package/dist/shims/request-state-types.js.map +1 -0
- package/dist/shims/router-state.d.ts +11 -0
- package/dist/shims/router-state.d.ts.map +1 -1
- package/dist/shims/router-state.js +10 -8
- package/dist/shims/router-state.js.map +1 -1
- package/dist/shims/router.d.ts +4 -0
- package/dist/shims/router.d.ts.map +1 -1
- package/dist/shims/router.js +130 -40
- package/dist/shims/router.js.map +1 -1
- package/dist/shims/server.d.ts +8 -1
- package/dist/shims/server.d.ts.map +1 -1
- package/dist/shims/server.js +52 -6
- package/dist/shims/server.js.map +1 -1
- package/dist/shims/unified-request-context.d.ts +66 -0
- package/dist/shims/unified-request-context.d.ts.map +1 -0
- package/dist/shims/unified-request-context.js +116 -0
- package/dist/shims/unified-request-context.js.map +1 -0
- package/dist/shims/url-utils.d.ts +20 -6
- package/dist/shims/url-utils.d.ts.map +1 -1
- package/dist/shims/url-utils.js +79 -0
- package/dist/shims/url-utils.js.map +1 -1
- package/dist/utils/domain-locale.d.ts +18 -0
- package/dist/utils/domain-locale.d.ts.map +1 -0
- package/dist/utils/domain-locale.js +64 -0
- package/dist/utils/domain-locale.js.map +1 -0
- package/package.json +2 -2
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { matchRoute, patternToNextFormat } from "../routing/pages-router.js";
|
|
2
2
|
import { isrGet, isrSet, isrCacheKey, buildPagesCacheValue, triggerBackgroundRegeneration, setRevalidateDuration, getRevalidateDuration, } from "./isr-cache.js";
|
|
3
|
-
import { runWithFetchCache } from "../shims/fetch-cache.js";
|
|
4
3
|
import { _runWithCacheState } from "../shims/cache.js";
|
|
5
4
|
import { runWithPrivateCache } from "../shims/cache-runtime.js";
|
|
5
|
+
import { ensureFetchPatch, runWithFetchCache } from "../shims/fetch-cache.js";
|
|
6
|
+
import { createRequestContext, runWithRequestContext } from "../shims/unified-request-context.js";
|
|
6
7
|
// Import server-only state modules to register ALS-backed accessors.
|
|
7
8
|
// These modules must be imported before any rendering occurs.
|
|
8
|
-
import
|
|
9
|
+
import "../shims/router-state.js";
|
|
9
10
|
import { runWithHeadState } from "../shims/head-state.js";
|
|
11
|
+
import { runWithServerInsertedHTMLState } from "../shims/navigation-state.js";
|
|
10
12
|
import { reportRequestError } from "./instrumentation.js";
|
|
11
13
|
import { safeJsonStringify } from "./html.js";
|
|
12
14
|
import { parseQueryString as parseQuery } from "../utils/query.js";
|
|
@@ -16,6 +18,7 @@ import React from "react";
|
|
|
16
18
|
import { renderToReadableStream } from "react-dom/server.edge";
|
|
17
19
|
import { logRequest, now } from "./request-log.js";
|
|
18
20
|
import { createValidFileMatcher } from "../routing/file-matcher.js";
|
|
21
|
+
import { extractLocaleFromUrl as extractLocaleFromUrlShared, detectLocaleFromAcceptLanguage, parseCookieLocaleFromHeader, resolvePagesI18nRequest, } from "./pages-i18n.js";
|
|
19
22
|
/**
|
|
20
23
|
* Render a React element to a string using renderToReadableStream.
|
|
21
24
|
*
|
|
@@ -28,6 +31,14 @@ async function renderToStringAsync(element) {
|
|
|
28
31
|
await stream.allReady;
|
|
29
32
|
return new Response(stream).text();
|
|
30
33
|
}
|
|
34
|
+
async function renderIsrPassToStringAsync(element) {
|
|
35
|
+
// The cache-fill render is a second render pass for the same request.
|
|
36
|
+
// Reset render-scoped state so it cannot leak from the streamed response
|
|
37
|
+
// render or affect async work that is still draining from that stream.
|
|
38
|
+
// Keep request identity state (pathname/query/locale/executionContext)
|
|
39
|
+
// intact: this second pass still belongs to the same request.
|
|
40
|
+
return await runWithServerInsertedHTMLState(() => runWithHeadState(() => _runWithCacheState(() => runWithPrivateCache(() => runWithFetchCache(async () => renderToStringAsync(element))))));
|
|
41
|
+
}
|
|
31
42
|
/** Body placeholder used to split the document shell for streaming. */
|
|
32
43
|
const STREAM_BODY_MARKER = "<!--VINEXT_STREAM_BODY-->";
|
|
33
44
|
/**
|
|
@@ -136,69 +147,21 @@ function findFileWithExtensions(basePath, matcher) {
|
|
|
136
147
|
* /about -> { locale: "en", url: "/about", hadPrefix: false } (defaultLocale)
|
|
137
148
|
*/
|
|
138
149
|
export function extractLocaleFromUrl(url, i18nConfig) {
|
|
139
|
-
|
|
140
|
-
const parts = pathname.split("/").filter(Boolean);
|
|
141
|
-
const query = url.includes("?") ? url.slice(url.indexOf("?")) : "";
|
|
142
|
-
if (parts.length > 0 && i18nConfig.locales.includes(parts[0])) {
|
|
143
|
-
const locale = parts[0];
|
|
144
|
-
const rest = "/" + parts.slice(1).join("/");
|
|
145
|
-
return { locale, url: (rest || "/") + query, hadPrefix: true };
|
|
146
|
-
}
|
|
147
|
-
return { locale: i18nConfig.defaultLocale, url, hadPrefix: false };
|
|
150
|
+
return extractLocaleFromUrlShared(url, i18nConfig);
|
|
148
151
|
}
|
|
149
152
|
/**
|
|
150
153
|
* Detect the preferred locale from the Accept-Language header.
|
|
151
154
|
* Returns the best matching locale or null.
|
|
152
155
|
*/
|
|
153
156
|
export function detectLocaleFromHeaders(req, i18nConfig) {
|
|
154
|
-
|
|
155
|
-
if (!acceptLang)
|
|
156
|
-
return null;
|
|
157
|
-
// Parse Accept-Language: en-US,en;q=0.9,fr;q=0.8
|
|
158
|
-
const langs = acceptLang
|
|
159
|
-
.split(",")
|
|
160
|
-
.map((part) => {
|
|
161
|
-
const [lang, qPart] = part.trim().split(";");
|
|
162
|
-
const q = qPart ? parseFloat(qPart.replace("q=", "")) : 1;
|
|
163
|
-
return { lang: lang.trim().toLowerCase(), q };
|
|
164
|
-
})
|
|
165
|
-
.sort((a, b) => b.q - a.q);
|
|
166
|
-
for (const { lang } of langs) {
|
|
167
|
-
// Exact match
|
|
168
|
-
const exactMatch = i18nConfig.locales.find((l) => l.toLowerCase() === lang);
|
|
169
|
-
if (exactMatch)
|
|
170
|
-
return exactMatch;
|
|
171
|
-
// Prefix match (e.g. "en-US" matches "en")
|
|
172
|
-
const prefix = lang.split("-")[0];
|
|
173
|
-
const prefixMatch = i18nConfig.locales.find((l) => l.toLowerCase() === prefix || l.toLowerCase().startsWith(prefix + "-"));
|
|
174
|
-
if (prefixMatch)
|
|
175
|
-
return prefixMatch;
|
|
176
|
-
}
|
|
177
|
-
return null;
|
|
157
|
+
return detectLocaleFromAcceptLanguage(req.headers["accept-language"], i18nConfig);
|
|
178
158
|
}
|
|
179
159
|
/**
|
|
180
160
|
* Parse the NEXT_LOCALE cookie from a request.
|
|
181
161
|
* Returns the cookie value if it matches a configured locale, otherwise null.
|
|
182
162
|
*/
|
|
183
163
|
export function parseCookieLocale(req, i18nConfig) {
|
|
184
|
-
|
|
185
|
-
if (!cookieHeader)
|
|
186
|
-
return null;
|
|
187
|
-
// Simple cookie parsing — find NEXT_LOCALE=value
|
|
188
|
-
const match = cookieHeader.match(/(?:^|;\s*)NEXT_LOCALE=([^;]*)/);
|
|
189
|
-
if (!match)
|
|
190
|
-
return null;
|
|
191
|
-
let value;
|
|
192
|
-
try {
|
|
193
|
-
value = decodeURIComponent(match[1].trim());
|
|
194
|
-
}
|
|
195
|
-
catch {
|
|
196
|
-
return null;
|
|
197
|
-
}
|
|
198
|
-
// Only return if it's a valid configured locale
|
|
199
|
-
if (i18nConfig.locales.includes(value))
|
|
200
|
-
return value;
|
|
201
|
-
return null;
|
|
164
|
+
return parseCookieLocaleFromHeader(req.headers.cookie, i18nConfig);
|
|
202
165
|
}
|
|
203
166
|
/**
|
|
204
167
|
* Create an SSR request handler for the Pages Router.
|
|
@@ -210,7 +173,7 @@ export function parseCookieLocale(req, i18nConfig) {
|
|
|
210
173
|
* 4. Render the component to HTML
|
|
211
174
|
* 5. Wrap in _document shell and send response
|
|
212
175
|
*/
|
|
213
|
-
export function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatcher) {
|
|
176
|
+
export function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatcher, basePath = "", trailingSlash = false) {
|
|
214
177
|
const matcher = fileMatcher ?? createValidFileMatcher();
|
|
215
178
|
return async (req, res, url,
|
|
216
179
|
/** Status code override — propagated from middleware rewrite status. */
|
|
@@ -238,30 +201,17 @@ export function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatch
|
|
|
238
201
|
// --- i18n: extract locale from URL prefix ---
|
|
239
202
|
let locale;
|
|
240
203
|
let localeStrippedUrl = url;
|
|
204
|
+
let currentDefaultLocale;
|
|
205
|
+
const domainLocales = i18nConfig?.domains;
|
|
241
206
|
if (i18nConfig) {
|
|
242
|
-
const
|
|
243
|
-
locale =
|
|
244
|
-
localeStrippedUrl =
|
|
245
|
-
|
|
246
|
-
if (
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
const redirectUrl = `/${cookieLocale}${url === "/" ? "" : url}`;
|
|
251
|
-
res.writeHead(307, { Location: redirectUrl });
|
|
252
|
-
res.end();
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
|
-
// If no cookie or cookie matches default, fall through to Accept-Language detection
|
|
256
|
-
if (!cookieLocale && i18nConfig.localeDetection !== false) {
|
|
257
|
-
const detectedLocale = detectLocaleFromHeaders(req, i18nConfig);
|
|
258
|
-
if (detectedLocale && detectedLocale !== i18nConfig.defaultLocale) {
|
|
259
|
-
const redirectUrl = `/${detectedLocale}${url === "/" ? "" : url}`;
|
|
260
|
-
res.writeHead(307, { Location: redirectUrl });
|
|
261
|
-
res.end();
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
}
|
|
207
|
+
const resolved = resolvePagesI18nRequest(url, i18nConfig, req.headers, req.headers.host, basePath, trailingSlash);
|
|
208
|
+
locale = resolved.locale;
|
|
209
|
+
localeStrippedUrl = resolved.url;
|
|
210
|
+
currentDefaultLocale = resolved.domainLocale?.defaultLocale ?? i18nConfig.defaultLocale;
|
|
211
|
+
if (resolved.redirectUrl) {
|
|
212
|
+
res.writeHead(307, { Location: resolved.redirectUrl });
|
|
213
|
+
res.end();
|
|
214
|
+
return;
|
|
265
215
|
}
|
|
266
216
|
}
|
|
267
217
|
const match = matchRoute(localeStrippedUrl, routes);
|
|
@@ -271,9 +221,10 @@ export function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatch
|
|
|
271
221
|
return;
|
|
272
222
|
}
|
|
273
223
|
const { route, params } = match;
|
|
274
|
-
// Wrap the entire request in
|
|
275
|
-
|
|
276
|
-
return
|
|
224
|
+
// Wrap the entire request in a single unified AsyncLocalStorage scope.
|
|
225
|
+
const requestContext = createRequestContext();
|
|
226
|
+
return runWithRequestContext(requestContext, async () => {
|
|
227
|
+
ensureFetchPatch();
|
|
277
228
|
try {
|
|
278
229
|
// Set SSR context for the router shim so useRouter() returns
|
|
279
230
|
// the correct URL and params during server-side rendering.
|
|
@@ -283,16 +234,30 @@ export function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatch
|
|
|
283
234
|
pathname: patternToNextFormat(route.pattern),
|
|
284
235
|
query: { ...params, ...parseQuery(url) },
|
|
285
236
|
asPath: url,
|
|
286
|
-
locale: locale ??
|
|
237
|
+
locale: locale ?? currentDefaultLocale,
|
|
287
238
|
locales: i18nConfig?.locales,
|
|
288
|
-
defaultLocale:
|
|
239
|
+
defaultLocale: currentDefaultLocale,
|
|
240
|
+
domainLocales,
|
|
289
241
|
});
|
|
290
242
|
}
|
|
291
|
-
// Set
|
|
243
|
+
// Set per-request i18n context for Link component locale
|
|
244
|
+
// prop support during SSR. Use ssrLoadModule to set it on
|
|
245
|
+
// the SSR environment's module instance (same pattern as
|
|
246
|
+
// setSSRContext above).
|
|
292
247
|
if (i18nConfig) {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
248
|
+
// Register ALS-backed i18n accessors in the SSR module graph so
|
|
249
|
+
// next/link and other SSR imports read from the unified store.
|
|
250
|
+
await server.ssrLoadModule("vinext/i18n-state");
|
|
251
|
+
const i18nCtx = await server.ssrLoadModule("vinext/i18n-context");
|
|
252
|
+
if (typeof i18nCtx.setI18nContext === "function") {
|
|
253
|
+
i18nCtx.setI18nContext({
|
|
254
|
+
locale: locale ?? currentDefaultLocale,
|
|
255
|
+
locales: i18nConfig.locales,
|
|
256
|
+
defaultLocale: currentDefaultLocale,
|
|
257
|
+
domainLocales,
|
|
258
|
+
hostname: req.headers.host?.split(":", 1)[0],
|
|
259
|
+
});
|
|
260
|
+
}
|
|
296
261
|
}
|
|
297
262
|
// Load the page module through Vite's SSR pipeline
|
|
298
263
|
// This gives us HMR and transform support for free
|
|
@@ -315,7 +280,7 @@ export function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatch
|
|
|
315
280
|
if (typeof pageModule.getStaticPaths === "function" && route.isDynamic) {
|
|
316
281
|
const pathsResult = await pageModule.getStaticPaths({
|
|
317
282
|
locales: i18nConfig?.locales ?? [],
|
|
318
|
-
defaultLocale:
|
|
283
|
+
defaultLocale: currentDefaultLocale ?? "",
|
|
319
284
|
});
|
|
320
285
|
const fallback = pathsResult?.fallback ?? false;
|
|
321
286
|
if (fallback === false) {
|
|
@@ -325,7 +290,7 @@ export function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatch
|
|
|
325
290
|
return Object.entries(p.params).every(([key, val]) => {
|
|
326
291
|
const actual = params[key];
|
|
327
292
|
if (Array.isArray(val)) {
|
|
328
|
-
return
|
|
293
|
+
return Array.isArray(actual) && val.join("/") === actual.join("/");
|
|
329
294
|
}
|
|
330
295
|
return String(val) === String(actual);
|
|
331
296
|
});
|
|
@@ -355,9 +320,9 @@ export function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatch
|
|
|
355
320
|
res,
|
|
356
321
|
query: parseQuery(url),
|
|
357
322
|
resolvedUrl: localeStrippedUrl,
|
|
358
|
-
locale: locale ??
|
|
323
|
+
locale: locale ?? currentDefaultLocale,
|
|
359
324
|
locales: i18nConfig?.locales,
|
|
360
|
-
defaultLocale:
|
|
325
|
+
defaultLocale: currentDefaultLocale,
|
|
361
326
|
};
|
|
362
327
|
const result = await pageModule.getServerSideProps(context);
|
|
363
328
|
// If gSSP called res.end() directly (short-circuit pattern),
|
|
@@ -464,18 +429,109 @@ export function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatch
|
|
|
464
429
|
const cachedPage = cached.value.value;
|
|
465
430
|
const cachedHtml = cachedPage.html;
|
|
466
431
|
const transformedHtml = await server.transformIndexHtml(url, cachedHtml);
|
|
467
|
-
// Trigger background regeneration: re-run getStaticProps
|
|
468
|
-
//
|
|
432
|
+
// Trigger background regeneration: re-run getStaticProps,
|
|
433
|
+
// re-render the page, and cache the fresh HTML.
|
|
469
434
|
triggerBackgroundRegeneration(cacheKey, async () => {
|
|
470
|
-
const
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
435
|
+
const regenContext = createRequestContext({
|
|
436
|
+
// Dev never has a Workers ExecutionContext. Set it
|
|
437
|
+
// explicitly so background regeneration cannot inherit
|
|
438
|
+
// a standalone execution-context scope from the caller.
|
|
439
|
+
executionContext: null,
|
|
440
|
+
});
|
|
441
|
+
return runWithRequestContext(regenContext, async () => {
|
|
442
|
+
ensureFetchPatch();
|
|
443
|
+
const freshResult = await pageModule.getStaticProps({
|
|
444
|
+
params,
|
|
445
|
+
locale: locale ?? currentDefaultLocale,
|
|
446
|
+
locales: i18nConfig?.locales,
|
|
447
|
+
defaultLocale: currentDefaultLocale,
|
|
448
|
+
});
|
|
449
|
+
if (freshResult && "props" in freshResult) {
|
|
450
|
+
const revalidate = typeof freshResult.revalidate === "number" ? freshResult.revalidate : 0;
|
|
451
|
+
if (revalidate > 0) {
|
|
452
|
+
const freshProps = freshResult.props;
|
|
453
|
+
if (typeof routerShim.setSSRContext === "function") {
|
|
454
|
+
routerShim.setSSRContext({
|
|
455
|
+
pathname: patternToNextFormat(route.pattern),
|
|
456
|
+
query: { ...params, ...parseQuery(url) },
|
|
457
|
+
asPath: url,
|
|
458
|
+
locale: locale ?? currentDefaultLocale,
|
|
459
|
+
locales: i18nConfig?.locales,
|
|
460
|
+
defaultLocale: currentDefaultLocale,
|
|
461
|
+
domainLocales,
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
if (i18nConfig) {
|
|
465
|
+
await server.ssrLoadModule("vinext/i18n-state");
|
|
466
|
+
const i18nCtx = await server.ssrLoadModule("vinext/i18n-context");
|
|
467
|
+
if (typeof i18nCtx.setI18nContext === "function") {
|
|
468
|
+
i18nCtx.setI18nContext({
|
|
469
|
+
locale: locale ?? currentDefaultLocale,
|
|
470
|
+
locales: i18nConfig.locales,
|
|
471
|
+
defaultLocale: currentDefaultLocale,
|
|
472
|
+
domainLocales,
|
|
473
|
+
hostname: req.headers.host?.split(":", 1)[0],
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
// Re-render the page with fresh props inside fresh
|
|
478
|
+
// render sub-scopes so head/cache state cannot leak.
|
|
479
|
+
let RegenApp = null;
|
|
480
|
+
const appPath = path.join(pagesDir, "_app");
|
|
481
|
+
if (findFileWithExtensions(appPath, matcher)) {
|
|
482
|
+
try {
|
|
483
|
+
const appMod = await server.ssrLoadModule(appPath);
|
|
484
|
+
RegenApp = appMod.default ?? null;
|
|
485
|
+
}
|
|
486
|
+
catch {
|
|
487
|
+
// _app failed to load
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
let el = RegenApp
|
|
491
|
+
? React.createElement(RegenApp, {
|
|
492
|
+
Component: pageModule.default,
|
|
493
|
+
pageProps: freshProps,
|
|
494
|
+
})
|
|
495
|
+
: React.createElement(pageModule.default, freshProps);
|
|
496
|
+
if (routerShim.wrapWithRouterContext) {
|
|
497
|
+
el = routerShim.wrapWithRouterContext(el);
|
|
498
|
+
}
|
|
499
|
+
const freshBody = await renderIsrPassToStringAsync(el);
|
|
500
|
+
// Rebuild __NEXT_DATA__ with fresh props. The hydration
|
|
501
|
+
// script (module URLs) is stable across regenerations —
|
|
502
|
+
// extract it from the cached HTML to avoid duplication.
|
|
503
|
+
const viteRoot = server.config?.root;
|
|
504
|
+
const regenPageUrl = viteRoot
|
|
505
|
+
? "/" + path.relative(viteRoot, route.filePath)
|
|
506
|
+
: route.filePath;
|
|
507
|
+
const regenAppUrl = RegenApp
|
|
508
|
+
? viteRoot
|
|
509
|
+
? "/" + path.relative(viteRoot, path.join(pagesDir, "_app"))
|
|
510
|
+
: path.join(pagesDir, "_app")
|
|
511
|
+
: null;
|
|
512
|
+
const freshNextData = `<script>window.__NEXT_DATA__ = ${safeJsonStringify({
|
|
513
|
+
props: { pageProps: freshProps },
|
|
514
|
+
page: patternToNextFormat(route.pattern),
|
|
515
|
+
query: params,
|
|
516
|
+
buildId: process.env.__VINEXT_BUILD_ID,
|
|
517
|
+
isFallback: false,
|
|
518
|
+
locale: locale ?? currentDefaultLocale,
|
|
519
|
+
locales: i18nConfig?.locales,
|
|
520
|
+
defaultLocale: currentDefaultLocale,
|
|
521
|
+
domainLocales,
|
|
522
|
+
__vinext: {
|
|
523
|
+
pageModuleUrl: regenPageUrl,
|
|
524
|
+
appModuleUrl: regenAppUrl,
|
|
525
|
+
},
|
|
526
|
+
})}${i18nConfig ? `;window.__VINEXT_LOCALE__=${safeJsonStringify(locale ?? currentDefaultLocale)};window.__VINEXT_LOCALES__=${safeJsonStringify(i18nConfig.locales)};window.__VINEXT_DEFAULT_LOCALE__=${safeJsonStringify(currentDefaultLocale)}` : ""}</script>`;
|
|
527
|
+
const hydrationMatch = cachedHtml.match(/<script type="module">[\s\S]*?<\/script>/);
|
|
528
|
+
const hydrationScript = hydrationMatch?.[0] ?? "";
|
|
529
|
+
const freshHtml = `<!DOCTYPE html><html><head></head><body><div id="__next">${freshBody}</div>${freshNextData}\n ${hydrationScript}</body></html>`;
|
|
530
|
+
await isrSet(cacheKey, buildPagesCacheValue(freshHtml, freshProps), revalidate);
|
|
531
|
+
setRevalidateDuration(cacheKey, revalidate);
|
|
532
|
+
}
|
|
477
533
|
}
|
|
478
|
-
}
|
|
534
|
+
});
|
|
479
535
|
});
|
|
480
536
|
const revalidateSecs = getRevalidateDuration(cacheKey) ?? 60;
|
|
481
537
|
const staleHeaders = {
|
|
@@ -492,9 +548,9 @@ export function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatch
|
|
|
492
548
|
// Cache miss — call getStaticProps normally
|
|
493
549
|
const context = {
|
|
494
550
|
params,
|
|
495
|
-
locale: locale ??
|
|
551
|
+
locale: locale ?? currentDefaultLocale,
|
|
496
552
|
locales: i18nConfig?.locales,
|
|
497
|
-
defaultLocale:
|
|
553
|
+
defaultLocale: currentDefaultLocale,
|
|
498
554
|
};
|
|
499
555
|
const result = await pageModule.getStaticProps(context);
|
|
500
556
|
if (result && "props" in result) {
|
|
@@ -579,9 +635,7 @@ export function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatch
|
|
|
579
635
|
if (typeof fontGoogle.getSSRFontLinks === "function") {
|
|
580
636
|
const fontUrls = fontGoogle.getSSRFontLinks();
|
|
581
637
|
for (const fontUrl of fontUrls) {
|
|
582
|
-
const safeFontUrl = fontUrl
|
|
583
|
-
.replace(/&/g, "&")
|
|
584
|
-
.replace(/"/g, """);
|
|
638
|
+
const safeFontUrl = fontUrl.replace(/&/g, "&").replace(/"/g, """);
|
|
585
639
|
fontHeadHTML += `<link rel="stylesheet" href="${safeFontUrl}" />\n `;
|
|
586
640
|
}
|
|
587
641
|
}
|
|
@@ -663,15 +717,16 @@ hydrate();
|
|
|
663
717
|
query: params,
|
|
664
718
|
buildId: process.env.__VINEXT_BUILD_ID,
|
|
665
719
|
isFallback: false,
|
|
666
|
-
locale: locale ??
|
|
720
|
+
locale: locale ?? currentDefaultLocale,
|
|
667
721
|
locales: i18nConfig?.locales,
|
|
668
|
-
defaultLocale:
|
|
722
|
+
defaultLocale: currentDefaultLocale,
|
|
723
|
+
domainLocales,
|
|
669
724
|
// Include module URLs so client navigation can import pages directly
|
|
670
725
|
__vinext: {
|
|
671
726
|
pageModuleUrl,
|
|
672
727
|
appModuleUrl,
|
|
673
728
|
},
|
|
674
|
-
})}${i18nConfig ? `;window.__VINEXT_LOCALE__=${safeJsonStringify(locale ??
|
|
729
|
+
})}${i18nConfig ? `;window.__VINEXT_LOCALE__=${safeJsonStringify(locale ?? currentDefaultLocale)};window.__VINEXT_LOCALES__=${safeJsonStringify(i18nConfig.locales)};window.__VINEXT_DEFAULT_LOCALE__=${safeJsonStringify(currentDefaultLocale)}` : ""}</script>`;
|
|
675
730
|
// Try to load custom _document.tsx
|
|
676
731
|
const docPath = path.join(pagesDir, "_document");
|
|
677
732
|
let DocumentComponent = null;
|
|
@@ -716,9 +771,7 @@ hydrate();
|
|
|
716
771
|
// Collect head HTML AFTER the shell renders (inside streamPageToResponse,
|
|
717
772
|
// after renderToReadableStream resolves). Head tags from Suspense
|
|
718
773
|
// children arrive late — this matches Next.js behavior.
|
|
719
|
-
getHeadHTML: () => typeof headShim.getSSRHeadHTML === "function"
|
|
720
|
-
? headShim.getSSRHeadHTML()
|
|
721
|
-
: "",
|
|
774
|
+
getHeadHTML: () => typeof headShim.getSSRHeadHTML === "function" ? headShim.getSSRHeadHTML() : "",
|
|
722
775
|
});
|
|
723
776
|
_renderEnd = now();
|
|
724
777
|
// Clear SSR context after rendering
|
|
@@ -738,7 +791,7 @@ hydrate();
|
|
|
738
791
|
if (wrapWithRouterContext) {
|
|
739
792
|
isrElement = wrapWithRouterContext(isrElement);
|
|
740
793
|
}
|
|
741
|
-
const isrBodyHtml = await
|
|
794
|
+
const isrBodyHtml = await renderIsrPassToStringAsync(isrElement);
|
|
742
795
|
const isrHtml = `<!DOCTYPE html><html><head></head><body><div id="__next">${isrBodyHtml}</div>${allScripts}</body></html>`;
|
|
743
796
|
const cacheKey = isrCacheKey("pages", url.split("?")[0],
|
|
744
797
|
// __VINEXT_BUILD_ID is a compile-time define — undefined in dev,
|
|
@@ -780,10 +833,9 @@ hydrate();
|
|
|
780
833
|
}
|
|
781
834
|
}
|
|
782
835
|
finally {
|
|
783
|
-
// Cleanup is handled by ALS scope unwinding
|
|
784
|
-
// each runWith*() scope is automatically cleaned up when it exits.
|
|
836
|
+
// Cleanup is handled by unified ALS scope unwinding.
|
|
785
837
|
}
|
|
786
|
-
})
|
|
838
|
+
});
|
|
787
839
|
};
|
|
788
840
|
}
|
|
789
841
|
/**
|