vinext 0.0.29 → 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/check.d.ts.map +1 -1
- package/dist/check.js +11 -7
- package/dist/check.js.map +1 -1
- package/dist/cloudflare/kv-cache-handler.d.ts.map +1 -1
- package/dist/cloudflare/kv-cache-handler.js +44 -30
- package/dist/cloudflare/kv-cache-handler.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 +208 -164
- 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 +151 -100
- package/dist/entries/pages-server-entry.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -2
- package/dist/index.js.map +1 -1
- package/dist/routing/app-router.d.ts.map +1 -1
- package/dist/routing/app-router.js +43 -29
- package/dist/routing/app-router.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 +163 -163
- 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 +23 -11
- package/dist/server/image-optimization.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.map +1 -1
- package/dist/server/middleware-codegen.js +37 -4
- package/dist/server/middleware-codegen.js.map +1 -1
- package/dist/server/middleware.d.ts.map +1 -1
- package/dist/server/middleware.js +50 -6
- 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/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 +21 -29
- 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 +27 -11
- package/dist/shims/link.js.map +1 -1
- package/dist/shims/metadata.d.ts +22 -22
- package/dist/shims/metadata.d.ts.map +1 -1
- package/dist/shims/metadata.js +34 -32
- 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.map +1 -1
- package/dist/shims/navigation.js +39 -22
- 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 +117 -21
- 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/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),
|
|
@@ -467,69 +432,106 @@ export function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatch
|
|
|
467
432
|
// Trigger background regeneration: re-run getStaticProps,
|
|
468
433
|
// re-render the page, and cache the fresh HTML.
|
|
469
434
|
triggerBackgroundRegeneration(cacheKey, async () => {
|
|
470
|
-
const
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
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,
|
|
475
440
|
});
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
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
|
+
});
|
|
489
463
|
}
|
|
490
|
-
|
|
491
|
-
|
|
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
|
+
}
|
|
492
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);
|
|
493
532
|
}
|
|
494
|
-
let el = RegenApp
|
|
495
|
-
? React.createElement(RegenApp, {
|
|
496
|
-
Component: pageModule.default,
|
|
497
|
-
pageProps: freshProps,
|
|
498
|
-
})
|
|
499
|
-
: React.createElement(pageModule.default, freshProps);
|
|
500
|
-
if (routerShim.wrapWithRouterContext) {
|
|
501
|
-
el = routerShim.wrapWithRouterContext(el);
|
|
502
|
-
}
|
|
503
|
-
const freshBody = await renderToStringAsync(el);
|
|
504
|
-
// Rebuild __NEXT_DATA__ with fresh props. The hydration
|
|
505
|
-
// script (module URLs) is stable across regenerations —
|
|
506
|
-
// extract it from the cached HTML to avoid duplication.
|
|
507
|
-
const viteRoot = server.config.root;
|
|
508
|
-
const regenPageUrl = "/" + path.relative(viteRoot, route.filePath);
|
|
509
|
-
const regenAppUrl = RegenApp
|
|
510
|
-
? "/" + path.relative(viteRoot, 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 ?? i18nConfig?.defaultLocale,
|
|
519
|
-
locales: i18nConfig?.locales,
|
|
520
|
-
defaultLocale: i18nConfig?.defaultLocale,
|
|
521
|
-
__vinext: {
|
|
522
|
-
pageModuleUrl: regenPageUrl,
|
|
523
|
-
appModuleUrl: regenAppUrl,
|
|
524
|
-
},
|
|
525
|
-
})}${i18nConfig ? `;window.__VINEXT_LOCALE__=${safeJsonStringify(locale ?? i18nConfig.defaultLocale)};window.__VINEXT_LOCALES__=${safeJsonStringify(i18nConfig.locales)};window.__VINEXT_DEFAULT_LOCALE__=${safeJsonStringify(i18nConfig.defaultLocale)}` : ""}</script>`;
|
|
526
|
-
const hydrationMatch = cachedHtml.match(/<script type="module">[\s\S]*?<\/script>/);
|
|
527
|
-
const hydrationScript = hydrationMatch?.[0] ?? "";
|
|
528
|
-
const freshHtml = `<!DOCTYPE html><html><head></head><body><div id="__next">${freshBody}</div>${freshNextData}\n ${hydrationScript}</body></html>`;
|
|
529
|
-
await isrSet(cacheKey, buildPagesCacheValue(freshHtml, freshProps), revalidate);
|
|
530
|
-
setRevalidateDuration(cacheKey, revalidate);
|
|
531
533
|
}
|
|
532
|
-
}
|
|
534
|
+
});
|
|
533
535
|
});
|
|
534
536
|
const revalidateSecs = getRevalidateDuration(cacheKey) ?? 60;
|
|
535
537
|
const staleHeaders = {
|
|
@@ -546,9 +548,9 @@ export function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatch
|
|
|
546
548
|
// Cache miss — call getStaticProps normally
|
|
547
549
|
const context = {
|
|
548
550
|
params,
|
|
549
|
-
locale: locale ??
|
|
551
|
+
locale: locale ?? currentDefaultLocale,
|
|
550
552
|
locales: i18nConfig?.locales,
|
|
551
|
-
defaultLocale:
|
|
553
|
+
defaultLocale: currentDefaultLocale,
|
|
552
554
|
};
|
|
553
555
|
const result = await pageModule.getStaticProps(context);
|
|
554
556
|
if (result && "props" in result) {
|
|
@@ -633,9 +635,7 @@ export function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatch
|
|
|
633
635
|
if (typeof fontGoogle.getSSRFontLinks === "function") {
|
|
634
636
|
const fontUrls = fontGoogle.getSSRFontLinks();
|
|
635
637
|
for (const fontUrl of fontUrls) {
|
|
636
|
-
const safeFontUrl = fontUrl
|
|
637
|
-
.replace(/&/g, "&")
|
|
638
|
-
.replace(/"/g, """);
|
|
638
|
+
const safeFontUrl = fontUrl.replace(/&/g, "&").replace(/"/g, """);
|
|
639
639
|
fontHeadHTML += `<link rel="stylesheet" href="${safeFontUrl}" />\n `;
|
|
640
640
|
}
|
|
641
641
|
}
|
|
@@ -717,15 +717,16 @@ hydrate();
|
|
|
717
717
|
query: params,
|
|
718
718
|
buildId: process.env.__VINEXT_BUILD_ID,
|
|
719
719
|
isFallback: false,
|
|
720
|
-
locale: locale ??
|
|
720
|
+
locale: locale ?? currentDefaultLocale,
|
|
721
721
|
locales: i18nConfig?.locales,
|
|
722
|
-
defaultLocale:
|
|
722
|
+
defaultLocale: currentDefaultLocale,
|
|
723
|
+
domainLocales,
|
|
723
724
|
// Include module URLs so client navigation can import pages directly
|
|
724
725
|
__vinext: {
|
|
725
726
|
pageModuleUrl,
|
|
726
727
|
appModuleUrl,
|
|
727
728
|
},
|
|
728
|
-
})}${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>`;
|
|
729
730
|
// Try to load custom _document.tsx
|
|
730
731
|
const docPath = path.join(pagesDir, "_document");
|
|
731
732
|
let DocumentComponent = null;
|
|
@@ -770,9 +771,7 @@ hydrate();
|
|
|
770
771
|
// Collect head HTML AFTER the shell renders (inside streamPageToResponse,
|
|
771
772
|
// after renderToReadableStream resolves). Head tags from Suspense
|
|
772
773
|
// children arrive late — this matches Next.js behavior.
|
|
773
|
-
getHeadHTML: () => typeof headShim.getSSRHeadHTML === "function"
|
|
774
|
-
? headShim.getSSRHeadHTML()
|
|
775
|
-
: "",
|
|
774
|
+
getHeadHTML: () => typeof headShim.getSSRHeadHTML === "function" ? headShim.getSSRHeadHTML() : "",
|
|
776
775
|
});
|
|
777
776
|
_renderEnd = now();
|
|
778
777
|
// Clear SSR context after rendering
|
|
@@ -792,7 +791,7 @@ hydrate();
|
|
|
792
791
|
if (wrapWithRouterContext) {
|
|
793
792
|
isrElement = wrapWithRouterContext(isrElement);
|
|
794
793
|
}
|
|
795
|
-
const isrBodyHtml = await
|
|
794
|
+
const isrBodyHtml = await renderIsrPassToStringAsync(isrElement);
|
|
796
795
|
const isrHtml = `<!DOCTYPE html><html><head></head><body><div id="__next">${isrBodyHtml}</div>${allScripts}</body></html>`;
|
|
797
796
|
const cacheKey = isrCacheKey("pages", url.split("?")[0],
|
|
798
797
|
// __VINEXT_BUILD_ID is a compile-time define — undefined in dev,
|
|
@@ -818,6 +817,8 @@ hydrate();
|
|
|
818
817
|
routerKind: "Pages Router",
|
|
819
818
|
routePath: route.pattern,
|
|
820
819
|
routeType: "render",
|
|
820
|
+
}).catch(() => {
|
|
821
|
+
/* ignore reporting errors */
|
|
821
822
|
});
|
|
822
823
|
// Try to render custom 500 error page
|
|
823
824
|
try {
|
|
@@ -832,10 +833,9 @@ hydrate();
|
|
|
832
833
|
}
|
|
833
834
|
}
|
|
834
835
|
finally {
|
|
835
|
-
// Cleanup is handled by ALS scope unwinding
|
|
836
|
-
// each runWith*() scope is automatically cleaned up when it exits.
|
|
836
|
+
// Cleanup is handled by unified ALS scope unwinding.
|
|
837
837
|
}
|
|
838
|
-
})
|
|
838
|
+
});
|
|
839
839
|
};
|
|
840
840
|
}
|
|
841
841
|
/**
|