vinext 0.0.20 → 0.0.22
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/deploy.d.ts.map +1 -1
- package/dist/deploy.js +6 -3
- package/dist/deploy.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +233 -22
- package/dist/index.js.map +1 -1
- package/dist/routing/app-router.d.ts.map +1 -1
- package/dist/routing/app-router.js +1 -41
- 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 +1 -27
- package/dist/routing/pages-router.js.map +1 -1
- package/dist/routing/utils.d.ts +25 -0
- package/dist/routing/utils.d.ts.map +1 -0
- package/dist/routing/utils.js +70 -0
- package/dist/routing/utils.js.map +1 -0
- package/dist/server/app-dev-server.d.ts.map +1 -1
- package/dist/server/app-dev-server.js +140 -6
- package/dist/server/app-dev-server.js.map +1 -1
- package/dist/server/dev-server.d.ts.map +1 -1
- package/dist/server/dev-server.js +77 -4
- package/dist/server/dev-server.js.map +1 -1
- package/dist/server/middleware.d.ts.map +1 -1
- package/dist/server/middleware.js +13 -4
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/prod-server.d.ts.map +1 -1
- package/dist/server/prod-server.js +7 -0
- package/dist/server/prod-server.js.map +1 -1
- package/dist/server/request-log.d.ts +34 -0
- package/dist/server/request-log.d.ts.map +1 -0
- package/dist/server/request-log.js +65 -0
- package/dist/server/request-log.js.map +1 -0
- package/dist/shims/cache-runtime.d.ts.map +1 -1
- package/dist/shims/cache-runtime.js +5 -1
- package/dist/shims/cache-runtime.js.map +1 -1
- package/dist/shims/cache.d.ts +6 -0
- package/dist/shims/cache.d.ts.map +1 -1
- package/dist/shims/cache.js +22 -2
- package/dist/shims/cache.js.map +1 -1
- package/dist/shims/head.d.ts +11 -0
- package/dist/shims/head.d.ts.map +1 -1
- package/dist/shims/head.js +21 -0
- package/dist/shims/head.js.map +1 -1
- package/dist/shims/headers.d.ts +8 -0
- package/dist/shims/headers.d.ts.map +1 -1
- package/dist/shims/headers.js +41 -0
- package/dist/shims/headers.js.map +1 -1
- package/dist/shims/script.d.ts.map +1 -1
- package/dist/shims/script.js +7 -1
- package/dist/shims/script.js.map +1 -1
- package/dist/shims/server.d.ts.map +1 -1
- package/dist/shims/server.js +2 -1
- package/dist/shims/server.js.map +1 -1
- package/package.json +1 -1
|
@@ -294,6 +294,48 @@ function rscOnError(error) {
|
|
|
294
294
|
if (error && typeof error === "object" && "digest" in error) {
|
|
295
295
|
return String(error.digest);
|
|
296
296
|
}
|
|
297
|
+
|
|
298
|
+
// In dev, detect the "Only plain objects" RSC serialization error and emit
|
|
299
|
+
// an actionable hint. This error occurs when a Server Component passes a
|
|
300
|
+
// class instance, ES module namespace object, or null-prototype object as a
|
|
301
|
+
// prop to a Client Component.
|
|
302
|
+
//
|
|
303
|
+
// Root cause: Vite bundles modules as true ESM (module namespace objects
|
|
304
|
+
// have a null-like internal slot), while Next.js's webpack build produces
|
|
305
|
+
// plain CJS-wrapped objects with __esModule:true. React's RSC serializer
|
|
306
|
+
// accepts the latter as plain objects but rejects the former — which means
|
|
307
|
+
// code that accidentally passes "import * as X" works in webpack/Next.js
|
|
308
|
+
// but correctly fails in vinext.
|
|
309
|
+
//
|
|
310
|
+
// Common triggers:
|
|
311
|
+
// - "import * as utils from './utils'" passed as a prop
|
|
312
|
+
// - class instances (new Foo()) passed as props
|
|
313
|
+
// - Date / Map / Set instances passed as props
|
|
314
|
+
// - Objects with Object.create(null) (null prototype)
|
|
315
|
+
if (
|
|
316
|
+
process.env.NODE_ENV !== "production" &&
|
|
317
|
+
error instanceof Error &&
|
|
318
|
+
error.message.includes("Only plain objects, and a few built-ins, can be passed to Client Components")
|
|
319
|
+
) {
|
|
320
|
+
console.error(
|
|
321
|
+
"[vinext] RSC serialization error: a non-plain object was passed from a Server Component to a Client Component.\\n" +
|
|
322
|
+
"\\n" +
|
|
323
|
+
"Common causes:\\n" +
|
|
324
|
+
" * Passing a module namespace (import * as X) directly as a prop.\\n" +
|
|
325
|
+
" Unlike Next.js (webpack), Vite produces real ESM module namespace objects\\n" +
|
|
326
|
+
" which are not serializable. Fix: pass individual values instead,\\n" +
|
|
327
|
+
" e.g. <Comp value={module.value} />\\n" +
|
|
328
|
+
" * Passing a class instance (new Foo()) as a prop.\\n" +
|
|
329
|
+
" Fix: convert to a plain object, e.g. { id: foo.id, name: foo.name }\\n" +
|
|
330
|
+
" * Passing a Date, Map, or Set. Use .toISOString(), [...map.entries()], etc.\\n" +
|
|
331
|
+
" * Passing Object.create(null). Use { ...obj } to restore a prototype.\\n" +
|
|
332
|
+
"\\n" +
|
|
333
|
+
"Original error:",
|
|
334
|
+
error.message,
|
|
335
|
+
);
|
|
336
|
+
return undefined;
|
|
337
|
+
}
|
|
338
|
+
|
|
297
339
|
// In production, generate a digest hash for non-navigation errors
|
|
298
340
|
if (process.env.NODE_ENV === "production" && error) {
|
|
299
341
|
const msg = error instanceof Error ? error.message : String(error);
|
|
@@ -1212,6 +1254,12 @@ export default async function handler(request) {
|
|
|
1212
1254
|
}
|
|
1213
1255
|
|
|
1214
1256
|
async function _handleRequest(request, __reqCtx) {
|
|
1257
|
+
const __reqStart = process.env.NODE_ENV !== "production" ? performance.now() : 0;
|
|
1258
|
+
let __compileEnd;
|
|
1259
|
+
let __renderEnd;
|
|
1260
|
+
// __reqStart is included in the timing header so the Node logging middleware
|
|
1261
|
+
// can compute true compile time as: handlerStart - middlewareStart.
|
|
1262
|
+
// Format: "handlerStart,compileMs,renderMs" - all as integers (ms). Dev-only.
|
|
1215
1263
|
const url = new URL(request.url);
|
|
1216
1264
|
|
|
1217
1265
|
// ── Cross-origin request protection ─────────────────────────────────
|
|
@@ -1258,7 +1306,11 @@ async function _handleRequest(request, __reqCtx) {
|
|
|
1258
1306
|
|
|
1259
1307
|
// ── Apply redirects from next.config.js ───────────────────────────────
|
|
1260
1308
|
if (__configRedirects.length) {
|
|
1261
|
-
|
|
1309
|
+
// Strip .rsc suffix before matching redirect rules - RSC (client-side nav) requests
|
|
1310
|
+
// arrive as /some/path.rsc but redirect patterns are defined without it (e.g.
|
|
1311
|
+
// /some/path). Without this, soft-nav fetches bypass all config redirects.
|
|
1312
|
+
const __redirPathname = pathname.endsWith(".rsc") ? pathname.slice(0, -4) : pathname;
|
|
1313
|
+
const __redir = __applyConfigRedirects(__redirPathname, __reqCtx);
|
|
1262
1314
|
if (__redir) {
|
|
1263
1315
|
const __redirDest = __sanitizeDestination(
|
|
1264
1316
|
__basePath && !__redir.destination.startsWith(__basePath)
|
|
@@ -1274,7 +1326,9 @@ async function _handleRequest(request, __reqCtx) {
|
|
|
1274
1326
|
|
|
1275
1327
|
// ── Apply beforeFiles rewrites from next.config.js ────────────────────
|
|
1276
1328
|
if (__configRewrites.beforeFiles && __configRewrites.beforeFiles.length) {
|
|
1277
|
-
|
|
1329
|
+
// Strip .rsc suffix before matching rewrite rules — same reason as redirects above.
|
|
1330
|
+
const __rewritePathname = pathname.endsWith(".rsc") ? pathname.slice(0, -4) : pathname;
|
|
1331
|
+
const __rewritten = __applyConfigRewrites(__rewritePathname, __configRewrites.beforeFiles, __reqCtx);
|
|
1278
1332
|
if (__rewritten) {
|
|
1279
1333
|
if (__isExternalUrl(__rewritten)) {
|
|
1280
1334
|
setHeadersContext(null);
|
|
@@ -2056,6 +2110,9 @@ async function _handleRequest(request, __reqCtx) {
|
|
|
2056
2110
|
console.error = _origConsoleError;
|
|
2057
2111
|
}
|
|
2058
2112
|
|
|
2113
|
+
// Mark end of compile phase: route matching, middleware, tree building are done.
|
|
2114
|
+
if (process.env.NODE_ENV !== "production") __compileEnd = performance.now();
|
|
2115
|
+
|
|
2059
2116
|
// Render to RSC stream
|
|
2060
2117
|
const rscStream = renderToReadableStream(element, { onError: rscOnError });
|
|
2061
2118
|
|
|
@@ -2084,6 +2141,21 @@ async function _handleRequest(request, __reqCtx) {
|
|
|
2084
2141
|
responseHeaders[key] = value;
|
|
2085
2142
|
}
|
|
2086
2143
|
}
|
|
2144
|
+
// Attach internal timing header so the dev server middleware can log it.
|
|
2145
|
+
// Format: "handlerStart,compileMs,renderMs"
|
|
2146
|
+
// handlerStart - absolute performance.now() when _handleRequest began,
|
|
2147
|
+
// used by the logging middleware to compute true compile
|
|
2148
|
+
// time as (handlerStart - middlewareReqStart).
|
|
2149
|
+
// compileMs - time inside the handler before renderToReadableStream.
|
|
2150
|
+
// -1 sentinel means compile time is not measured.
|
|
2151
|
+
// renderMs - -1 sentinel for RSC-only (soft-nav) responses, since
|
|
2152
|
+
// rendering is handled asynchronously by the client. The
|
|
2153
|
+
// logging middleware computes render time as totalMs - compileMs.
|
|
2154
|
+
if (process.env.NODE_ENV !== "production") {
|
|
2155
|
+
const handlerStart = Math.round(__reqStart);
|
|
2156
|
+
const compileMs = __compileEnd !== undefined ? Math.round(__compileEnd - __reqStart) : -1;
|
|
2157
|
+
responseHeaders["x-vinext-timing"] = handlerStart + "," + compileMs + ",-1";
|
|
2158
|
+
}
|
|
2087
2159
|
return new Response(rscStream, { status: _middlewareRewriteStatus || 200, headers: responseHeaders });
|
|
2088
2160
|
}
|
|
2089
2161
|
|
|
@@ -2110,6 +2182,8 @@ async function _handleRequest(request, __reqCtx) {
|
|
|
2110
2182
|
try {
|
|
2111
2183
|
const ssrEntry = await import.meta.viteRsc.loadModule("ssr", "index");
|
|
2112
2184
|
htmlStream = await ssrEntry.handleSsr(rscStream, _getNavigationContext(), fontData);
|
|
2185
|
+
// Shell render complete; Suspense boundaries stream asynchronously
|
|
2186
|
+
if (process.env.NODE_ENV !== "production") __renderEnd = performance.now();
|
|
2113
2187
|
} catch (ssrErr) {
|
|
2114
2188
|
const specialResponse = await handleRenderError(ssrErr);
|
|
2115
2189
|
if (specialResponse) return specialResponse;
|
|
@@ -2140,6 +2214,22 @@ async function _handleRequest(request, __reqCtx) {
|
|
|
2140
2214
|
response.headers.append(key, value);
|
|
2141
2215
|
}
|
|
2142
2216
|
}
|
|
2217
|
+
// Attach internal timing header so the dev server middleware can log it.
|
|
2218
|
+
// Format: "handlerStart,compileMs,renderMs"
|
|
2219
|
+
// handlerStart - absolute performance.now() when _handleRequest began,
|
|
2220
|
+
// used by the logging middleware to compute true compile
|
|
2221
|
+
// time as (handlerStart - middlewareReqStart).
|
|
2222
|
+
// compileMs - time inside the handler before renderToReadableStream.
|
|
2223
|
+
// renderMs - time from renderToReadableStream to handleSsr completion,
|
|
2224
|
+
// or -1 sentinel if not measured (falls back to totalMs - compileMs).
|
|
2225
|
+
if (process.env.NODE_ENV !== "production") {
|
|
2226
|
+
const handlerStart = Math.round(__reqStart);
|
|
2227
|
+
const compileMs = __compileEnd !== undefined ? Math.round(__compileEnd - __reqStart) : -1;
|
|
2228
|
+
const renderMs = __renderEnd !== undefined && __compileEnd !== undefined
|
|
2229
|
+
? Math.round(__renderEnd - __compileEnd)
|
|
2230
|
+
: -1;
|
|
2231
|
+
response.headers.set("x-vinext-timing", handlerStart + "," + compileMs + "," + renderMs);
|
|
2232
|
+
}
|
|
2143
2233
|
// Apply custom status code from middleware rewrite
|
|
2144
2234
|
if (_middlewareRewriteStatus) {
|
|
2145
2235
|
return new Response(response.body, {
|
|
@@ -2257,6 +2347,20 @@ async function collectStreamChunks(stream) {
|
|
|
2257
2347
|
return chunks;
|
|
2258
2348
|
}
|
|
2259
2349
|
|
|
2350
|
+
// React 19 dev-mode workaround (see VinextFlightRoot in handleSsr):
|
|
2351
|
+
//
|
|
2352
|
+
// In dev, Flight error decoding in react-server-dom-webpack/client.edge
|
|
2353
|
+
// can hit resolveErrorDev() which (via React's dev error stack capture)
|
|
2354
|
+
// expects a non-null hooks dispatcher.
|
|
2355
|
+
//
|
|
2356
|
+
// Vinext previously called createFromReadableStream() outside of any React render.
|
|
2357
|
+
// When an RSC stream contains an error, dev-mode decoding could crash with:
|
|
2358
|
+
// - "Invalid hook call"
|
|
2359
|
+
// - "Cannot read properties of null (reading 'useContext')"
|
|
2360
|
+
//
|
|
2361
|
+
// Fix: call createFromReadableStream() lazily inside a React component render.
|
|
2362
|
+
// This mirrors Next.js behavior and ensures the dispatcher is set.
|
|
2363
|
+
|
|
2260
2364
|
/**
|
|
2261
2365
|
* Create a TransformStream that appends RSC chunks as inline <script> tags
|
|
2262
2366
|
* to the HTML stream. This allows progressive hydration — the browser receives
|
|
@@ -2391,7 +2495,18 @@ export async function handleSsr(rscStream, navContext, fontData) {
|
|
|
2391
2495
|
// immediately in the HTML shell, then stream in resolved content as RSC
|
|
2392
2496
|
// chunks arrive. Awaiting here would block until all async server components
|
|
2393
2497
|
// complete, collapsing the streaming behavior.
|
|
2394
|
-
|
|
2498
|
+
// Lazily create the Flight root inside render so React's hook dispatcher is set
|
|
2499
|
+
// (avoids React 19 dev-mode resolveErrorDev() crash). VinextFlightRoot returns
|
|
2500
|
+
// a thenable (not a ReactNode), which React 19 consumes via its internal
|
|
2501
|
+
// thenable-as-child suspend/resume behavior. This matches Next.js's approach.
|
|
2502
|
+
let flightRoot;
|
|
2503
|
+
function VinextFlightRoot() {
|
|
2504
|
+
if (!flightRoot) {
|
|
2505
|
+
flightRoot = createFromReadableStream(ssrStream);
|
|
2506
|
+
}
|
|
2507
|
+
return flightRoot;
|
|
2508
|
+
}
|
|
2509
|
+
const root = _ssrCE(VinextFlightRoot);
|
|
2395
2510
|
|
|
2396
2511
|
// Wrap with ServerInsertedHTMLContext.Provider so libraries that use
|
|
2397
2512
|
// useContext(ServerInsertedHTMLContext) (Apollo Client, styled-components,
|
|
@@ -2446,11 +2561,11 @@ export async function handleSsr(rscStream, navContext, fontData) {
|
|
|
2446
2561
|
const insertedElements = flushServerInsertedHTML();
|
|
2447
2562
|
|
|
2448
2563
|
// Render the inserted elements to HTML strings
|
|
2449
|
-
const {
|
|
2564
|
+
const { Fragment } = await import("react");
|
|
2450
2565
|
let insertedHTML = "";
|
|
2451
2566
|
for (const el of insertedElements) {
|
|
2452
2567
|
try {
|
|
2453
|
-
insertedHTML += renderToStaticMarkup(
|
|
2568
|
+
insertedHTML += renderToStaticMarkup(_ssrCE(Fragment, null, el));
|
|
2454
2569
|
} catch {
|
|
2455
2570
|
// Skip elements that can't be rendered
|
|
2456
2571
|
}
|
|
@@ -2865,7 +2980,12 @@ async function main() {
|
|
|
2865
2980
|
// Checks the prefetch cache (populated by <Link> IntersectionObserver and
|
|
2866
2981
|
// router.prefetch()) before making a network request. This makes navigation
|
|
2867
2982
|
// near-instant for prefetched routes.
|
|
2868
|
-
window.__VINEXT_RSC_NAVIGATE__ = async function navigateRsc(href) {
|
|
2983
|
+
window.__VINEXT_RSC_NAVIGATE__ = async function navigateRsc(href, __redirectDepth) {
|
|
2984
|
+
if ((__redirectDepth || 0) > 10) {
|
|
2985
|
+
console.error("[vinext] Too many RSC redirects — aborting navigation to prevent infinite loop.");
|
|
2986
|
+
window.location.href = href;
|
|
2987
|
+
return;
|
|
2988
|
+
}
|
|
2869
2989
|
try {
|
|
2870
2990
|
const url = new URL(href, window.location.origin);
|
|
2871
2991
|
const rscUrl = toRscUrl(url.pathname + url.search);
|
|
@@ -2891,6 +3011,20 @@ async function main() {
|
|
|
2891
3011
|
});
|
|
2892
3012
|
}
|
|
2893
3013
|
|
|
3014
|
+
// Detect if fetch followed a redirect: compare the final response URL to
|
|
3015
|
+
// what we requested. If they differ, the server issued a 3xx — push the
|
|
3016
|
+
// canonical destination URL into history before rendering.
|
|
3017
|
+
const __finalUrl = new URL(navResponse.url);
|
|
3018
|
+
const __requestedUrl = new URL(rscUrl, window.location.origin);
|
|
3019
|
+
if (__finalUrl.pathname !== __requestedUrl.pathname) {
|
|
3020
|
+
// Strip .rsc suffix from the final URL to get the page path for history.
|
|
3021
|
+
// Use replaceState instead of pushState: the caller (navigateImpl) already
|
|
3022
|
+
// pushed the pre-redirect URL; replacing it avoids a stale history entry.
|
|
3023
|
+
const __destPath = __finalUrl.pathname.replace(/\\.rsc$/, "") + __finalUrl.search;
|
|
3024
|
+
window.history.replaceState(null, "", __destPath);
|
|
3025
|
+
return window.__VINEXT_RSC_NAVIGATE__(__destPath, (__redirectDepth || 0) + 1);
|
|
3026
|
+
}
|
|
3027
|
+
|
|
2894
3028
|
// Update useParams() with route params from the server before re-rendering
|
|
2895
3029
|
const navParamsHeader = navResponse.headers.get("X-Vinext-Params");
|
|
2896
3030
|
if (navParamsHeader) {
|