vinext 1.4.0 → 1.4.1
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 +20 -20
- package/dist/build/prerender.js +3 -2
- package/dist/check.js +3 -6
- package/dist/client/dev-error-overlay-store.d.ts +1 -0
- package/dist/client/dev-error-overlay.d.ts +2 -0
- package/dist/client/dev-error-overlay.js +19 -3
- package/dist/config/config-matchers.js +2 -0
- package/dist/config/destination-params.d.ts +2 -1
- package/dist/config/destination-params.js +52 -2
- package/dist/config/next-config.d.ts +6 -0
- package/dist/config/next-config.js +18 -3
- package/dist/entries/app-rsc-entry.js +0 -1
- package/dist/entries/pages-server-entry.js +3 -1
- package/dist/index.js +29 -20
- package/dist/plugins/strip-server-exports.js +2 -1
- package/dist/server/api-handler.js +2 -1
- package/dist/server/app-browser-entry.js +7 -3
- package/dist/server/app-browser-rsc-redirect.js +3 -3
- package/dist/server/app-browser-server-action-client.js +2 -2
- package/dist/server/app-browser-visible-commit.js +2 -2
- package/dist/server/app-optimistic-routing.js +2 -3
- package/dist/server/app-page-boundary-render.js +5 -10
- package/dist/server/app-page-dispatch.d.ts +0 -1
- package/dist/server/app-page-dispatch.js +0 -1
- package/dist/server/app-page-element-builder.js +3 -3
- package/dist/server/app-page-execution.js +9 -6
- package/dist/server/app-page-render.d.ts +0 -1
- package/dist/server/app-page-render.js +14 -14
- package/dist/server/app-page-response.js +1 -1
- package/dist/server/app-page-route-wiring.js +22 -5
- package/dist/server/app-page-stream.d.ts +10 -8
- package/dist/server/app-page-stream.js +2 -1
- package/dist/server/app-request-stage-independent-entry.js +1 -1
- package/dist/server/app-route-handler-execution.js +3 -2
- package/dist/server/app-route-handler-runtime.d.ts +1 -1
- package/dist/server/app-route-handler-runtime.js +11 -7
- package/dist/server/app-router-entry.js +1 -1
- package/dist/server/app-rsc-cache-busting.d.ts +10 -10
- package/dist/server/app-rsc-cache-busting.js +27 -68
- package/dist/server/app-rsc-error-handler.js +1 -1
- package/dist/server/app-rsc-errors.js +4 -1
- package/dist/server/app-rsc-handler.js +53 -62
- package/dist/server/app-rsc-request-normalization.d.ts +10 -12
- package/dist/server/app-rsc-request-normalization.js +11 -14
- package/dist/server/app-rsc-response-stage.js +4 -12
- package/dist/server/app-server-action-execution.js +8 -4
- package/dist/server/app-ssr-entry.d.ts +6 -0
- package/dist/server/app-ssr-entry.js +13 -8
- package/dist/server/app-worker-stages.d.ts +12 -3
- package/dist/server/app-worker-stages.js +37 -4
- package/dist/server/bfcache-identity.d.ts +7 -0
- package/dist/server/bfcache-identity.js +10 -1
- package/dist/server/cacheability-manifest.js +1 -2
- package/dist/server/dev-origin-check.d.ts +2 -1
- package/dist/server/dev-origin-check.js +2 -1
- package/dist/server/dev-server.js +2 -1
- package/dist/server/image-optimization.d.ts +5 -0
- package/dist/server/image-optimization.js +6 -1
- package/dist/server/isr-cache.js +4 -1
- package/dist/server/metadata-route-response.js +1 -1
- package/dist/server/middleware-matcher.js +19 -6
- package/dist/server/middleware-runtime.js +6 -2
- package/dist/server/multi-stage.d.ts +7 -3
- package/dist/server/pages-node-compat.d.ts +1 -1
- package/dist/server/pages-node-compat.js +2 -0
- package/dist/server/pages-page-data.js +2 -1
- package/dist/server/pages-page-handler.js +17 -6
- package/dist/server/pages-page-response.d.ts +9 -0
- package/dist/server/pages-page-response.js +18 -4
- package/dist/server/pages-request-stage-entry.js +24 -4
- package/dist/server/pages-router-entry.js +3 -2
- package/dist/server/prod-server.d.ts +2 -2
- package/dist/server/prod-server.js +42 -8
- package/dist/server/request-error-log.d.ts +9 -0
- package/dist/server/request-error-log.js +13 -0
- package/dist/server/request-pipeline.d.ts +9 -4
- package/dist/server/request-pipeline.js +23 -15
- package/dist/server/rsc-completion-metadata.js +3 -0
- package/dist/server/static-file-cache.d.ts +9 -0
- package/dist/server/static-file-cache.js +13 -2
- package/dist/shims/cache-request-state.d.ts +6 -6
- package/dist/shims/cache-request-state.js +6 -36
- package/dist/shims/cache-runtime.d.ts +3 -1
- package/dist/shims/cache-runtime.js +77 -47
- package/dist/shims/cache.d.ts +10 -5
- package/dist/shims/cache.js +35 -22
- package/dist/shims/client-hook-error.d.ts +2 -0
- package/dist/shims/client-hook-error.js +16 -1
- package/dist/shims/fetch-cache.js +6 -2
- package/dist/shims/headers.d.ts +2 -1
- package/dist/shims/headers.js +5 -3
- package/dist/shims/internal/cache-context-accessor.d.ts +1 -1
- package/dist/shims/link.js +5 -10
- package/dist/shims/navigation.js +2 -3
- package/dist/shims/request-state-types.d.ts +2 -1
- package/dist/shims/slot.d.ts +6 -2
- package/dist/shims/slot.js +10 -3
- package/dist/shims/unified-request-context.d.ts +6 -0
- package/dist/shims/unified-request-context.js +1 -0
- package/dist/utils/cache-life-profiles.d.ts +33 -0
- package/dist/utils/cache-life-profiles.js +111 -0
- package/dist/utils/middleware-request-headers.d.ts +14 -3
- package/dist/utils/middleware-request-headers.js +54 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -659,26 +659,26 @@ Every `next/*` import is shimmed to a Vite-compatible implementation.
|
|
|
659
659
|
|
|
660
660
|
### Server features
|
|
661
661
|
|
|
662
|
-
| Feature | | Notes
|
|
663
|
-
| ------------------------------------------ | --- |
|
|
664
|
-
| SSR (Pages Router) | ✅ | Streaming, `_app`/`_document`, `__NEXT_DATA__`, hydration
|
|
665
|
-
| SSR (App Router) | ✅ | RSC pipeline, nested layouts, streaming, nav context for client components
|
|
666
|
-
| `getStaticProps` | ✅ | Props, redirect, notFound, revalidate
|
|
667
|
-
| `getStaticPaths` | ✅ | `fallback: false`, `true`, `"blocking"`
|
|
668
|
-
| `getServerSideProps` | ✅ | Full context including locale
|
|
669
|
-
| ISR | ✅ | Stale-while-revalidate, pluggable `CacheHandler`, background regeneration
|
|
670
|
-
| Server Actions (`"use server"`) | ✅ | Action execution, FormData, re-render after mutation, `redirect()` in actions
|
|
671
|
-
| React Server Components | ✅ | Via `@vitejs/plugin-rsc`. `"use client"` boundaries work correctly
|
|
672
|
-
| Streaming SSR | ✅ | Both routers
|
|
673
|
-
| Metadata API | ✅ | `metadata`, `generateMetadata`, `viewport`, `generateViewport`, title templates
|
|
674
|
-
| `generateStaticParams` | ✅ | With `dynamicParams` enforcement
|
|
675
|
-
| Metadata file routes | ✅ | sitemap.xml, robots.txt, manifest, favicon, OG images (static + dynamic)
|
|
676
|
-
| Static export (`output: 'export'`) | ✅ | Generates static HTML/JSON for all routes
|
|
677
|
-
| Standalone output (`output: 'standalone'`) | ✅ | Generates `dist/standalone` with `server.js`, build artifacts, and runtime deps
|
|
678
|
-
| `connection()` | ✅ | Forces dynamic rendering
|
|
679
|
-
| `"use cache"` directive | ✅ | File-level and function-level. `cacheLife()` profiles, `cacheTag()`, stale-while-revalidate |
|
|
680
|
-
| `instrumentation.ts` | ✅ | `register()`, `onRequestError()`, and [framework tracing](docs/tracing.mdx)
|
|
681
|
-
| Route segment config | 🟡 | `revalidate`, `dynamic`, `dynamicParams`, `runtime` (not placement). No `preferredRegion`
|
|
662
|
+
| Feature | | Notes |
|
|
663
|
+
| ------------------------------------------ | --- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
664
|
+
| SSR (Pages Router) | ✅ | Streaming, `_app`/`_document`, `__NEXT_DATA__`, hydration |
|
|
665
|
+
| SSR (App Router) | ✅ | RSC pipeline, nested layouts, streaming, nav context for client components |
|
|
666
|
+
| `getStaticProps` | ✅ | Props, redirect, notFound, revalidate |
|
|
667
|
+
| `getStaticPaths` | ✅ | `fallback: false`, `true`, `"blocking"` |
|
|
668
|
+
| `getServerSideProps` | ✅ | Full context including locale |
|
|
669
|
+
| ISR | ✅ | Stale-while-revalidate, pluggable `CacheHandler`, background regeneration |
|
|
670
|
+
| Server Actions (`"use server"`) | ✅ | Action execution, FormData, re-render after mutation, `redirect()` in actions |
|
|
671
|
+
| React Server Components | ✅ | Via `@vitejs/plugin-rsc`. `"use client"` boundaries work correctly |
|
|
672
|
+
| Streaming SSR | ✅ | Both routers |
|
|
673
|
+
| Metadata API | ✅ | `metadata`, `generateMetadata`, `viewport`, `generateViewport`, title templates |
|
|
674
|
+
| `generateStaticParams` | ✅ | With `dynamicParams` enforcement |
|
|
675
|
+
| Metadata file routes | ✅ | sitemap.xml, robots.txt, manifest, favicon, OG images (static + dynamic) |
|
|
676
|
+
| Static export (`output: 'export'`) | ✅ | Generates static HTML/JSON for all routes |
|
|
677
|
+
| Standalone output (`output: 'standalone'`) | ✅ | Generates `dist/standalone` with `server.js`, build artifacts, and runtime deps |
|
|
678
|
+
| `connection()` | ✅ | Forces dynamic rendering |
|
|
679
|
+
| `"use cache"` directive | ✅ | File-level and function-level. `cacheLife()` with built-in and next.config `cacheLife` (or legacy `experimental.cacheLife`) profiles, `cacheTag()`, stale-while-revalidate |
|
|
680
|
+
| `instrumentation.ts` | ✅ | `register()`, `onRequestError()`, and [framework tracing](docs/tracing.mdx) |
|
|
681
|
+
| Route segment config | 🟡 | `revalidate`, `dynamic`, `dynamicParams`, `runtime` (not placement). No `preferredRegion` |
|
|
682
682
|
|
|
683
683
|
### Configuration
|
|
684
684
|
|
package/dist/build/prerender.js
CHANGED
|
@@ -608,12 +608,13 @@ async function prerenderPages({ routes, apiRoutes, pagesDir, outDir, config, mod
|
|
|
608
608
|
fs.writeFileSync(htmlFullPath, html, "utf-8");
|
|
609
609
|
outputFiles.push(htmlOutputPath);
|
|
610
610
|
}
|
|
611
|
+
const renderedRevalidate = isStaticPropsResponse ? readPrerenderCacheLifeHeader(response.headers)?.revalidate ?? false : revalidate;
|
|
611
612
|
result = {
|
|
612
613
|
route: route.pattern,
|
|
613
614
|
status: "rendered",
|
|
614
615
|
outputFiles,
|
|
615
|
-
revalidate,
|
|
616
|
-
...typeof
|
|
616
|
+
revalidate: renderedRevalidate,
|
|
617
|
+
...typeof renderedRevalidate === "number" ? { expire: config.expireTime } : {},
|
|
617
618
|
router: "pages",
|
|
618
619
|
...urlPath !== route.pattern ? { path: urlPath } : {},
|
|
619
620
|
...locale ? { locale } : {},
|
package/dist/check.js
CHANGED
|
@@ -243,13 +243,10 @@ const CONFIG_SUPPORT = {
|
|
|
243
243
|
status: "unsupported",
|
|
244
244
|
detail: "ignored; configure cache adapters with vinext({ cache }) in vite.config"
|
|
245
245
|
},
|
|
246
|
-
cacheLife: {
|
|
247
|
-
status: "unsupported",
|
|
248
|
-
detail: "custom cacheLife profiles are ignored; only the built-in profiles are available"
|
|
249
|
-
},
|
|
246
|
+
cacheLife: { status: "supported" },
|
|
250
247
|
"experimental.cacheLife": {
|
|
251
|
-
status: "
|
|
252
|
-
detail: "
|
|
248
|
+
status: "supported",
|
|
249
|
+
detail: "legacy alias of cacheLife"
|
|
253
250
|
},
|
|
254
251
|
skipTrailingSlashRedirect: { status: "supported" },
|
|
255
252
|
"experimental.skipTrailingSlashRedirect": {
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { ReportedError, dismissOverlay } from "./dev-error-overlay-store.js";
|
|
2
|
+
import { InitialDevServerErrorPayload } from "../server/dev-initial-server-error.js";
|
|
2
3
|
//#region src/client/dev-error-overlay.d.ts
|
|
3
4
|
export declare const DEV_ERROR_OVERLAY_HOST_ID = "__vinext_dev_error_overlay_root";
|
|
4
5
|
export declare const DEV_ERROR_OVERLAY_MOUNT_ID = "__vinext_dev_error_overlay_mount";
|
|
5
6
|
export declare function installDevErrorOverlay(): void;
|
|
6
7
|
export declare function installReactRefreshErrorRecovery(): void;
|
|
8
|
+
export declare function selectUnreportedInitialDevServerErrors(payloads: readonly InitialDevServerErrorPayload[], reported: readonly ReportedError[]): InitialDevServerErrorPayload[];
|
|
7
9
|
export declare function reportInitialDevServerErrors(): void;
|
|
8
10
|
type ViteHmrErrorPayload = {
|
|
9
11
|
err?: ViteHmrError;
|
|
@@ -11,6 +11,7 @@ const DEV_ERROR_OVERLAY_MOUNT_ID = "__vinext_dev_error_overlay_mount";
|
|
|
11
11
|
const VITE_ERROR_HANDLER_DATA_KEY = "__vinext_vite_error_handler__";
|
|
12
12
|
const REACT_REFRESH_RECOVERY_RETRY_DELAY_MS = 16;
|
|
13
13
|
let reactRoot = null;
|
|
14
|
+
let overlayHost = null;
|
|
14
15
|
let installed = false;
|
|
15
16
|
const reportedErrors = /* @__PURE__ */ new WeakSet();
|
|
16
17
|
function rememberReported(error) {
|
|
@@ -81,12 +82,21 @@ function tryInstallReactRefreshErrorRecovery() {
|
|
|
81
82
|
});
|
|
82
83
|
return true;
|
|
83
84
|
}
|
|
85
|
+
function selectUnreportedInitialDevServerErrors(payloads, reported) {
|
|
86
|
+
const rethrownErrors = reported.filter((error) => error.source === "caught" || error.source === "uncaught" || error.source === "window-error");
|
|
87
|
+
return payloads.filter((payload) => {
|
|
88
|
+
const rethrownIndex = rethrownErrors.findIndex((error) => error.message === payload.message && (error.name || "Error") === (payload.name || "Error"));
|
|
89
|
+
if (rethrownIndex === -1) return true;
|
|
90
|
+
rethrownErrors.splice(rethrownIndex, 1);
|
|
91
|
+
return false;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
84
94
|
function reportInitialDevServerErrors() {
|
|
85
95
|
if (typeof window === "undefined") return;
|
|
86
96
|
const errors = window.__VINEXT_INITIAL_DEV_ERRORS__;
|
|
87
97
|
if (!errors || errors.length === 0) return;
|
|
88
98
|
window.__VINEXT_INITIAL_DEV_ERRORS__ = [];
|
|
89
|
-
for (const payload of errors) {
|
|
99
|
+
for (const payload of selectUnreportedInitialDevServerErrors(errors, getOverlaySnapshot().errors)) {
|
|
90
100
|
const error = new Error(payload.message);
|
|
91
101
|
if (payload.name) error.name = payload.name;
|
|
92
102
|
if (payload.stack) error.stack = payload.stack;
|
|
@@ -130,6 +140,7 @@ function reportDevError(error, options) {
|
|
|
130
140
|
ensureMounted();
|
|
131
141
|
const id = reportToOverlay({
|
|
132
142
|
source: options.source,
|
|
143
|
+
name: error instanceof Error ? error.name : void 0,
|
|
133
144
|
message,
|
|
134
145
|
stack,
|
|
135
146
|
ignoredStackFrames: void 0,
|
|
@@ -176,8 +187,13 @@ function stringValue(value) {
|
|
|
176
187
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
177
188
|
}
|
|
178
189
|
function ensureMounted() {
|
|
179
|
-
if (reactRoot)
|
|
190
|
+
if (reactRoot) {
|
|
191
|
+
const host = overlayHost;
|
|
192
|
+
if (host && !host.isConnected) (document.body ?? document.documentElement).appendChild(host);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
180
195
|
const node = createDevErrorOverlayMountNode(document);
|
|
196
|
+
overlayHost = document.getElementById(DEV_ERROR_OVERLAY_HOST_ID);
|
|
181
197
|
reactRoot = createRoot(node);
|
|
182
198
|
reactRoot.render(/* @__PURE__ */ jsx(DevErrorOverlayApp, {}));
|
|
183
199
|
}
|
|
@@ -1342,4 +1358,4 @@ const componentStackStyle = {
|
|
|
1342
1358
|
color: "var(--vinext-overlay-muted)"
|
|
1343
1359
|
};
|
|
1344
1360
|
//#endregion
|
|
1345
|
-
export { DEV_ERROR_OVERLAY_HOST_ID, DEV_ERROR_OVERLAY_MOUNT_ID, createDevErrorOverlayMountNode, createViteOpenInEditorUrl, devOnCaughtError, devOnUncaughtError, dismissOverlay, formatErrorInfoForClipboard, formatOverlayDisplayFile, formatViteOpenInEditorFile, installDevErrorOverlay, installReactRefreshErrorRecovery, installViteHmrErrorHandler, normalizeViteHmrError, reportInitialDevServerErrors };
|
|
1361
|
+
export { DEV_ERROR_OVERLAY_HOST_ID, DEV_ERROR_OVERLAY_MOUNT_ID, createDevErrorOverlayMountNode, createViteOpenInEditorUrl, devOnCaughtError, devOnUncaughtError, dismissOverlay, formatErrorInfoForClipboard, formatOverlayDisplayFile, formatViteOpenInEditorFile, installDevErrorOverlay, installReactRefreshErrorRecovery, installViteHmrErrorHandler, normalizeViteHmrError, reportInitialDevServerErrors, selectUnreportedInitialDevServerErrors };
|
|
@@ -759,8 +759,10 @@ async function proxyExternalRequest(request, externalUrl) {
|
|
|
759
759
|
const destinationKeys = new Set(targetUrl.searchParams.keys());
|
|
760
760
|
for (const [key, value] of originalUrl.searchParams) if (!destinationKeys.has(key)) targetUrl.searchParams.append(key, value);
|
|
761
761
|
const headers = new Headers(request.headers);
|
|
762
|
+
const requestHost = headers.get("host") ?? "";
|
|
762
763
|
headers.set("host", targetUrl.host);
|
|
763
764
|
stripHopByHopRequestHeaders(headers);
|
|
765
|
+
headers.set("x-forwarded-host", requestHost);
|
|
764
766
|
const keysToDelete = [];
|
|
765
767
|
for (const key of headers.keys()) if (key.startsWith("x-middleware-") && !key.startsWith("x-middleware-request-")) keysToDelete.push(key);
|
|
766
768
|
for (const key of keysToDelete) headers.delete(key);
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
* `%XX` escapes a raw path capture already carries. A decoded `has` capture
|
|
26
26
|
* carrying `#`, `/`, `?`, `@` or `:` then makes the host invalid instead of
|
|
27
27
|
* moving the proxy to another host. Next.js substitutes the hostname verbatim
|
|
28
|
-
* and can be retargeted this way; vinext is stricter.
|
|
28
|
+
* and can be retargeted this way; vinext is stricter. Params in a rewrite's
|
|
29
|
+
* path follow substituteRewritePath.
|
|
29
30
|
*
|
|
30
31
|
* https://github.com/vercel/next.js/blob/canary/packages/next/src/shared/lib/router/utils/prepare-destination.ts
|
|
31
32
|
* https://github.com/vercel/next.js/blob/canary/packages/next/src/server/server-route-utils.ts
|
|
@@ -33,7 +33,8 @@ const _compiledDestinationParamCache = /* @__PURE__ */ new Map();
|
|
|
33
33
|
* `%XX` escapes a raw path capture already carries. A decoded `has` capture
|
|
34
34
|
* carrying `#`, `/`, `?`, `@` or `:` then makes the host invalid instead of
|
|
35
35
|
* moving the proxy to another host. Next.js substitutes the hostname verbatim
|
|
36
|
-
* and can be retargeted this way; vinext is stricter.
|
|
36
|
+
* and can be retargeted this way; vinext is stricter. Params in a rewrite's
|
|
37
|
+
* path follow substituteRewritePath.
|
|
37
38
|
*
|
|
38
39
|
* https://github.com/vercel/next.js/blob/canary/packages/next/src/shared/lib/router/utils/prepare-destination.ts
|
|
39
40
|
* https://github.com/vercel/next.js/blob/canary/packages/next/src/server/server-route-utils.ts
|
|
@@ -49,7 +50,56 @@ function substituteDestinationParams(destination, params, kind) {
|
|
|
49
50
|
const queryIndex = beforeHash.indexOf("?");
|
|
50
51
|
const beforeQuery = queryIndex === -1 ? beforeHash : beforeHash.slice(0, queryIndex);
|
|
51
52
|
const authorityEnd = getAbsoluteUrlAuthorityEnd(beforeQuery.replace(paramRe, (token) => "x".repeat(token.length)));
|
|
52
|
-
|
|
53
|
+
const [path, pathHash] = substituteRewritePath(beforeQuery.slice(authorityEnd), paramRe, params, authorityEnd > 0);
|
|
54
|
+
const query = queryIndex === -1 ? "" : `?${replaceParams(beforeHash.slice(queryIndex + 1), encodeRewriteQueryParamValue)}`;
|
|
55
|
+
return `${replaceParams(beforeQuery.slice(0, authorityEnd), encodeAuthorityParam)}${path}${query}${pathHash ?? replaceParams(hash, (value) => value)}`;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Substitute params into a rewrite destination's path, returning the path and,
|
|
59
|
+
* when a param carried a `#`, the hash it starts.
|
|
60
|
+
*
|
|
61
|
+
* Next.js inserts path params verbatim, splits the result at the first `#`
|
|
62
|
+
* and escapes a `?` when it formats the URL. A decoded `has` capture can end
|
|
63
|
+
* the path with `#`, but neither `?` nor `#` can displace the destination's
|
|
64
|
+
* own query.
|
|
65
|
+
*
|
|
66
|
+
* An absolute destination is proxied with the URL parser, which removes tab
|
|
67
|
+
* and newlines, trims trailing control characters and spaces, and resolves
|
|
68
|
+
* `.`/`..` segments against the destination's literal path. Next.js sends a
|
|
69
|
+
* param's control characters and spaces percent-encoded, as vinext does, and
|
|
70
|
+
* its dot segments as written. A parsed URL cannot carry a `..` segment, so a
|
|
71
|
+
* dot segment that a param is in or next to is escaped once more (`.` as
|
|
72
|
+
* `%252E`, `%2e` as `%252e`) and stays inside the destination's path. The
|
|
73
|
+
* destination's own dot segments still resolve.
|
|
74
|
+
*
|
|
75
|
+
* https://github.com/vercel/next.js/blob/canary/packages/next/src/shared/lib/router/utils/prepare-destination.ts
|
|
76
|
+
* https://github.com/vercel/next.js/blob/canary/packages/next/src/shared/lib/router/utils/format-url.ts
|
|
77
|
+
*/
|
|
78
|
+
function substituteRewritePath(template, paramRe, params, absolute) {
|
|
79
|
+
const encodeParam = absolute ? encodeAbsoluteUrlPathParam : encodePathQueryDelimiter;
|
|
80
|
+
const paramRanges = [];
|
|
81
|
+
let substituted = "";
|
|
82
|
+
let templateIndex = 0;
|
|
83
|
+
for (const match of template.matchAll(paramRe)) {
|
|
84
|
+
substituted += template.slice(templateIndex, match.index);
|
|
85
|
+
const value = encodeParam(params[match[1]]);
|
|
86
|
+
paramRanges.push([substituted.length, substituted.length + value.length]);
|
|
87
|
+
substituted += value;
|
|
88
|
+
templateIndex = match.index + match[0].length;
|
|
89
|
+
}
|
|
90
|
+
substituted += template.slice(templateIndex);
|
|
91
|
+
const [path, hash] = substituted.split("#", 2);
|
|
92
|
+
const pathHash = hash === void 0 ? void 0 : hash ? `#${hash}` : "";
|
|
93
|
+
if (!absolute) return [path, pathHash];
|
|
94
|
+
return [path.replace(DOT_SEGMENT_RE, (segment, offset) => paramRanges.some(([start, end]) => start <= offset + segment.length && end >= offset) ? segment.replace(/\.|%(?=2e)/gi, (char) => char === "." ? "%252E" : "%25") : segment), pathHash];
|
|
95
|
+
}
|
|
96
|
+
const DOT_SEGMENT_RE = /(?<=^|[/\\])(?:\.|%2e){1,2}(?=[/\\]|$)/gi;
|
|
97
|
+
function encodePathQueryDelimiter(value) {
|
|
98
|
+
return value.replaceAll("?", "%3F");
|
|
99
|
+
}
|
|
100
|
+
const ABSOLUTE_URL_PATH_PARAM_UNSAFE_RE = /[?\u0000- ]/g;
|
|
101
|
+
function encodeAbsoluteUrlPathParam(value) {
|
|
102
|
+
return value.replace(ABSOLUTE_URL_PATH_PARAM_UNSAFE_RE, encodeURIComponent);
|
|
53
103
|
}
|
|
54
104
|
function getAbsoluteUrlAuthorityEnd(value) {
|
|
55
105
|
const scheme = /^[a-z][a-z0-9+.-]*:[/\\\t\n\r]*/i.exec(value);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CacheLifeConfig } from "../utils/cache-life-profiles.js";
|
|
1
2
|
import { PHASE_PRODUCTION_BUILD } from "../shims/constants.js";
|
|
2
3
|
import { PluginOption } from "vite";
|
|
3
4
|
//#region src/config/next-config.d.ts
|
|
@@ -551,6 +552,11 @@ export type ResolvedNextConfig = {
|
|
|
551
552
|
dynamic: number;
|
|
552
553
|
static: number;
|
|
553
554
|
};
|
|
555
|
+
/**
|
|
556
|
+
* `cacheLife` profiles (or legacy `experimental.cacheLife`) merged over the
|
|
557
|
+
* built-ins with Next.js's rules; read by the `cacheLife()` shim.
|
|
558
|
+
*/
|
|
559
|
+
cacheLife: Record<string, CacheLifeConfig>;
|
|
554
560
|
/**
|
|
555
561
|
* Mirrors Next.js `experimental.useLightningcss`. When `true`, switch
|
|
556
562
|
* Vite's CSS pipeline from PostCSS to lightningcss for both transforms
|
|
@@ -6,6 +6,7 @@ import { isUnknownRecord } from "../utils/record.js";
|
|
|
6
6
|
import { PHASE_DEVELOPMENT_SERVER, PHASE_PRODUCTION_BUILD } from "../shims/constants.js";
|
|
7
7
|
import { getHtmlLimitedBotRegex } from "../utils/html-limited-bots.js";
|
|
8
8
|
import { flattenPluginOptions } from "../utils/plugin-options.js";
|
|
9
|
+
import { resolveCacheLifeProfiles } from "../utils/cache-life-profiles.js";
|
|
9
10
|
import { configSourceError } from "./config-source.js";
|
|
10
11
|
import { isExternalUrl } from "../utils/external-url.js";
|
|
11
12
|
import { applyLocaleToRoutes } from "./config-matchers.js";
|
|
@@ -376,7 +377,11 @@ function warnDeprecatedConfigOptions(config, root) {
|
|
|
376
377
|
];
|
|
377
378
|
for (const [propertyPath, warning] of warnings) if (hasConfigProperty(config, propertyPath)) warnConfigOnce(warning);
|
|
378
379
|
const experimental = readOptionalRecord(config.experimental);
|
|
379
|
-
for (const key of [
|
|
380
|
+
for (const key of [
|
|
381
|
+
"skipTrailingSlashRedirect",
|
|
382
|
+
"cacheComponents",
|
|
383
|
+
"cacheLife"
|
|
384
|
+
]) if (experimental?.[key] != null) warnConfigOnce(`\`experimental.${key}\` has been moved to \`${key}\`. Please update your ${configFileName} file accordingly.`);
|
|
380
385
|
}
|
|
381
386
|
async function resolveNextConfigInput(config, phase = PHASE_DEVELOPMENT_SERVER) {
|
|
382
387
|
return await resolveConfigValue(config, phase);
|
|
@@ -935,6 +940,10 @@ async function resolveNextConfig(config, root = toSlash(process.cwd()), options
|
|
|
935
940
|
instrumentationServerValueInjections: {},
|
|
936
941
|
clientTraceMetadata: void 0,
|
|
937
942
|
staleTimes: { ...DEFAULT_STALE_TIMES },
|
|
943
|
+
cacheLife: resolveCacheLifeProfiles(void 0, {
|
|
944
|
+
expireTime: DEFAULT_EXPIRE_TIME,
|
|
945
|
+
staticStaleTime: DEFAULT_STALE_TIMES.static
|
|
946
|
+
}, warnConfigOnce),
|
|
938
947
|
useLightningcss: false,
|
|
939
948
|
lightningCssFeatures: {
|
|
940
949
|
include: 0,
|
|
@@ -1014,6 +1023,8 @@ async function resolveNextConfig(config, root = toSlash(process.cwd()), options
|
|
|
1014
1023
|
const allowedDevOrigins = Array.isArray(config.allowedDevOrigins) ? config.allowedDevOrigins : [];
|
|
1015
1024
|
const experimental = readOptionalRecord(config.experimental);
|
|
1016
1025
|
const cacheComponents = (experimental?.cacheComponents ?? config.cacheComponents) === true;
|
|
1026
|
+
const expireTime = typeof config.expireTime === "number" ? config.expireTime : DEFAULT_EXPIRE_TIME;
|
|
1027
|
+
const staleTimes = resolveStaleTimes(experimental);
|
|
1017
1028
|
const serverActionsConfig = readOptionalRecord(experimental?.serverActions);
|
|
1018
1029
|
const serverActionsAllowedOrigins = readStringArray(serverActionsConfig?.allowedOrigins);
|
|
1019
1030
|
const serverActionsBodySizeLimitConfig = readOptionalBodySizeLimit(serverActionsConfig?.bodySizeLimit);
|
|
@@ -1124,7 +1135,7 @@ async function resolveNextConfig(config, root = toSlash(process.cwd()), options
|
|
|
1124
1135
|
globalNotFound,
|
|
1125
1136
|
serverActionsBodySizeLimit,
|
|
1126
1137
|
serverActionsBodySizeLimitLabel,
|
|
1127
|
-
expireTime
|
|
1138
|
+
expireTime,
|
|
1128
1139
|
reactMaxHeadersLength: typeof config.reactMaxHeadersLength === "number" ? config.reactMaxHeadersLength : DEFAULT_REACT_MAX_HEADERS_LENGTH,
|
|
1129
1140
|
htmlLimitedBots,
|
|
1130
1141
|
serverExternalPackages,
|
|
@@ -1144,7 +1155,11 @@ async function resolveNextConfig(config, root = toSlash(process.cwd()), options
|
|
|
1144
1155
|
compilerDefine: serializeCompilerDefine(config.compiler?.define),
|
|
1145
1156
|
compilerDefineServer: serializeCompilerDefine(config.compiler?.defineServer),
|
|
1146
1157
|
clientTraceMetadata: Array.isArray(experimental?.clientTraceMetadata) ? experimental.clientTraceMetadata.filter((value) => typeof value === "string") : void 0,
|
|
1147
|
-
staleTimes
|
|
1158
|
+
staleTimes,
|
|
1159
|
+
cacheLife: resolveCacheLifeProfiles(experimental?.cacheLife ?? config.cacheLife, {
|
|
1160
|
+
expireTime,
|
|
1161
|
+
staticStaleTime: staleTimes.static
|
|
1162
|
+
}, warnConfigOnce),
|
|
1148
1163
|
useLightningcss,
|
|
1149
1164
|
lightningCssFeatures
|
|
1150
1165
|
};
|
|
@@ -1161,7 +1161,6 @@ ${responseStageOnly ? "const __responseStageOptions = {" : "const __appRscHandle
|
|
|
1161
1161
|
getSourceRoute(sourceRouteIndex) {
|
|
1162
1162
|
return routes[sourceRouteIndex];
|
|
1163
1163
|
},
|
|
1164
|
-
hasCustomGlobalError: ${globalErrorVar ? `Boolean(${globalErrorVar}?.default)` : "false"},
|
|
1165
1164
|
hasAnyGenerateStaticParams: __generateStaticParams.length > 0,
|
|
1166
1165
|
hasGenerateStaticParams: __staticGeneration.hasGenerateStaticParams,
|
|
1167
1166
|
hasPageDefaultExport: !!PageComponent,
|
|
@@ -21,6 +21,7 @@ const _pagesI18nPath = resolveEntryPath("../server/pages-i18n.js", import.meta.u
|
|
|
21
21
|
const _pagesDataRoutePath = resolveEntryPath("../server/pages-data-route.js", import.meta.url);
|
|
22
22
|
const _pagesDefault404Path = resolveEntryPath("../server/pages-default-404.js", import.meta.url);
|
|
23
23
|
const _pagesApiRoutePath = resolveEntryPath("../server/pages-api-route.js", import.meta.url);
|
|
24
|
+
const _requestErrorLogPath = resolveEntryPath("../server/request-error-log.js", import.meta.url);
|
|
24
25
|
const _serverGlobalsPath = resolveEntryPath("../server/server-globals.js", import.meta.url);
|
|
25
26
|
const _queryUtilsPath = resolveEntryPath("../utils/query.js", import.meta.url);
|
|
26
27
|
const _pagesPageHandlerPath = resolveEntryPath("../server/pages-page-handler.js", import.meta.url);
|
|
@@ -370,6 +371,7 @@ import { buildRouteTrie as _buildRouteTrie, trieMatch as _trieMatch } from ${JSO
|
|
|
370
371
|
import { reportRequestError as _reportRequestError } from "vinext/instrumentation";
|
|
371
372
|
import { resolvePagesI18nRequest } from ${JSON.stringify(_pagesI18nPath)};
|
|
372
373
|
import { handlePagesApiRoute as __handlePagesApiRoute } from ${JSON.stringify(_pagesApiRoutePath)};
|
|
374
|
+
import { logRequestError as __logRequestError } from ${JSON.stringify(_requestErrorLogPath)};
|
|
373
375
|
import { normalizePagesDataRequest as __normalizePagesDataRequest, shouldAddTrailingSlashToPagesDataPath as __shouldAddTrailingSlashToPagesDataPath, buildNextDataNotFoundResponse as __buildNextDataNotFoundResponse } from ${JSON.stringify(_pagesDataRoutePath)};
|
|
374
376
|
import { buildDefaultPagesNotFoundResponse as __buildDefaultPagesNotFoundResponse } from ${JSON.stringify(_pagesDefault404Path)};
|
|
375
377
|
import { createPagesPageHandler as __createPagesPageHandler } from ${JSON.stringify(_pagesPageHandlerPath)};
|
|
@@ -618,7 +620,7 @@ export async function handleApiRoute(request, url, ctx, trustedRevalidateOrigin,
|
|
|
618
620
|
trustedRevalidateOrigin,
|
|
619
621
|
url,
|
|
620
622
|
reportRequestError(error, routePattern) {
|
|
621
|
-
|
|
623
|
+
__logRequestError("[vinext] API error:", error);
|
|
622
624
|
return _reportRequestError(
|
|
623
625
|
error,
|
|
624
626
|
{ path: url, method: request.method, headers: Object.fromEntries(request.headers.entries()) },
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import { getReactUpgradeDeps, reactUpgradeRequiredMessage } from "./utils/react-
|
|
|
11
11
|
import { resolvePluginReactPath } from "./utils/plugin-react-version.js";
|
|
12
12
|
import { loadDotenv } from "./config/dotenv.js";
|
|
13
13
|
import { PHASE_DEVELOPMENT_SERVER, PHASE_PRODUCTION_BUILD } from "./shims/constants.js";
|
|
14
|
+
import { serializeCacheLifeProfiles } from "./utils/cache-life-profiles.js";
|
|
14
15
|
import { VINEXT_MW_CTX_HEADER } from "./utils/protocol-headers.js";
|
|
15
16
|
import { encodeMiddlewareRequestHeaders } from "./utils/middleware-request-headers.js";
|
|
16
17
|
import { hasBasePath, stripBasePath } from "./utils/base-path.js";
|
|
@@ -1373,6 +1374,29 @@ function vinext(options = {}) {
|
|
|
1373
1374
|
});
|
|
1374
1375
|
const plugins = [
|
|
1375
1376
|
buildLifecyclePlugins[0],
|
|
1377
|
+
{
|
|
1378
|
+
name: "vinext:dev-origin-check",
|
|
1379
|
+
apply: "serve",
|
|
1380
|
+
enforce: "pre",
|
|
1381
|
+
configureServer(server) {
|
|
1382
|
+
server.middlewares.use((req, res, next) => {
|
|
1383
|
+
const blockReason = validateDevRequest({
|
|
1384
|
+
origin: req.headers.origin,
|
|
1385
|
+
host: req.headers.host,
|
|
1386
|
+
"x-forwarded-host": req.headers["x-forwarded-host"],
|
|
1387
|
+
"sec-fetch-site": req.headers["sec-fetch-site"],
|
|
1388
|
+
"sec-fetch-mode": req.headers["sec-fetch-mode"]
|
|
1389
|
+
}, nextConfig?.allowedDevOrigins);
|
|
1390
|
+
if (blockReason) {
|
|
1391
|
+
console.warn(`[vinext] Blocked dev request: ${blockReason} (${req.url})`);
|
|
1392
|
+
res.writeHead(403, { "Content-Type": "text/plain" });
|
|
1393
|
+
res.end("Forbidden");
|
|
1394
|
+
return;
|
|
1395
|
+
}
|
|
1396
|
+
next();
|
|
1397
|
+
});
|
|
1398
|
+
}
|
|
1399
|
+
},
|
|
1376
1400
|
createStyledJsxPlugin(earlyBaseDir),
|
|
1377
1401
|
mdxProxyPlugin,
|
|
1378
1402
|
reactPluginPromise,
|
|
@@ -1595,6 +1619,7 @@ function vinext(options = {}) {
|
|
|
1595
1619
|
defines["process.env.__VINEXT_TRAILING_SLASH"] = JSON.stringify(nextConfig.trailingSlash ? "true" : "false");
|
|
1596
1620
|
defines["process.env.__VINEXT_MANUAL_TRAILING_SLASH"] = JSON.stringify(nextConfig.skipTrailingSlashRedirect ? "true" : "false");
|
|
1597
1621
|
defines["process.env.__NEXT_CONFIG_OUTPUT"] = JSON.stringify(nextConfig.output ?? "");
|
|
1622
|
+
defines["process.env.__VINEXT_CACHE_LIFE_PROFILES"] = JSON.stringify(serializeCacheLifeProfiles(nextConfig.cacheLife));
|
|
1598
1623
|
defines["process.env.__VINEXT_IMAGE_REMOTE_PATTERNS"] = JSON.stringify(JSON.stringify(nextConfig.images?.remotePatterns ?? []));
|
|
1599
1624
|
defines["process.env.__VINEXT_IMAGE_DOMAINS"] = JSON.stringify(JSON.stringify(nextConfig.images?.domains ?? []));
|
|
1600
1625
|
{
|
|
@@ -3147,22 +3172,6 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
|
|
|
3147
3172
|
}
|
|
3148
3173
|
return isExistingDevPublicFile(pathname);
|
|
3149
3174
|
};
|
|
3150
|
-
server.middlewares.use((req, res, next) => {
|
|
3151
|
-
const blockReason = validateDevRequest({
|
|
3152
|
-
origin: req.headers.origin,
|
|
3153
|
-
host: req.headers.host,
|
|
3154
|
-
"x-forwarded-host": req.headers["x-forwarded-host"],
|
|
3155
|
-
"sec-fetch-site": req.headers["sec-fetch-site"],
|
|
3156
|
-
"sec-fetch-mode": req.headers["sec-fetch-mode"]
|
|
3157
|
-
}, nextConfig?.allowedDevOrigins);
|
|
3158
|
-
if (blockReason) {
|
|
3159
|
-
console.warn(`[vinext] Blocked dev request: ${blockReason} (${req.url})`);
|
|
3160
|
-
res.writeHead(403, { "Content-Type": "text/plain" });
|
|
3161
|
-
res.end("Forbidden");
|
|
3162
|
-
return;
|
|
3163
|
-
}
|
|
3164
|
-
next();
|
|
3165
|
-
});
|
|
3166
3175
|
server.middlewares.use((req, res, next) => {
|
|
3167
3176
|
if (!sendRepeatedSlashRedirect(req.url ?? "/", res)) next();
|
|
3168
3177
|
});
|
|
@@ -3253,7 +3262,7 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
|
|
|
3253
3262
|
if (hasAppDir) server.middlewares.use((req, res, next) => {
|
|
3254
3263
|
const url = req.url ?? "/";
|
|
3255
3264
|
const [pathname] = url.split("?");
|
|
3256
|
-
if (url.startsWith("/@") || url.startsWith("/__vite") || url.startsWith("/node_modules") || url.includes(".") && !pathname.endsWith(".html")
|
|
3265
|
+
if (url.startsWith("/@") || url.startsWith("/__vite") || url.startsWith("/node_modules") || url.includes(".") && !pathname.endsWith(".html")) return next();
|
|
3257
3266
|
const _reqStart = now();
|
|
3258
3267
|
const _reqWallStart = Date.now();
|
|
3259
3268
|
let _compileMs;
|
|
@@ -3287,12 +3296,11 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
|
|
|
3287
3296
|
return _origWriteHead(statusCode, ...args);
|
|
3288
3297
|
};
|
|
3289
3298
|
res.on("finish", () => {
|
|
3290
|
-
const logUrl = url.replace(/\.rsc(\?|$)/, "$1");
|
|
3291
3299
|
const totalMs = now() - _reqStart;
|
|
3292
3300
|
const resolvedRenderMs = _renderMs !== void 0 ? _renderMs : _compileMs !== void 0 ? Math.max(0, Math.round(totalMs - _compileMs)) : void 0;
|
|
3293
3301
|
logRequest({
|
|
3294
3302
|
method: req.method ?? "GET",
|
|
3295
|
-
url
|
|
3303
|
+
url,
|
|
3296
3304
|
status: res.statusCode,
|
|
3297
3305
|
totalMs,
|
|
3298
3306
|
compileMs: _compileMs,
|
|
@@ -3468,6 +3476,8 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
|
|
|
3468
3476
|
removeForwardedMiddlewareContext(req);
|
|
3469
3477
|
for (const key of Object.keys(req.headers)) delete req.headers[key];
|
|
3470
3478
|
for (const [key, value] of nextRequestHeaders) req.headers[key] = value;
|
|
3479
|
+
const setCookies = nextRequestHeaders.getSetCookie();
|
|
3480
|
+
if (setCookies.length > 1) req.headers["set-cookie"] = setCookies;
|
|
3471
3481
|
};
|
|
3472
3482
|
const devRunMiddlewareAdapter = capturedMiddlewarePath ? async (_request, _ctx, opts) => {
|
|
3473
3483
|
const rawProto = process.env.VINEXT_TRUST_PROXY === "1" || (process.env.VINEXT_TRUSTED_HOSTS ?? "").split(",").some((h) => h.trim()) ? String(req.headers["x-forwarded-proto"] || "").split(",")[0].trim() : "";
|
|
@@ -3662,7 +3672,6 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
|
|
|
3662
3672
|
const url = req.url ?? "/";
|
|
3663
3673
|
if (!hasPagesDir) return next();
|
|
3664
3674
|
if (url.startsWith("/@") || url.startsWith("/__vite") || url.startsWith("/node_modules")) return next();
|
|
3665
|
-
if (url.split("?")[0].endsWith(".rsc")) return next();
|
|
3666
3675
|
if (hasCloudflarePlugin) return next();
|
|
3667
3676
|
await pagesInstrumentationReady;
|
|
3668
3677
|
const traceHeaders = new Headers();
|
|
@@ -89,7 +89,8 @@ function isReferenceIdentifier(node, parent) {
|
|
|
89
89
|
if (parent.type === "ImportSpecifier" || parent.type === "ImportDefaultSpecifier" || parent.type === "ImportNamespaceSpecifier") return false;
|
|
90
90
|
if (parent.type === "ExportSpecifier") return false;
|
|
91
91
|
if (parent.type === "MemberExpression" && parent.property === node && !parent.computed) return false;
|
|
92
|
-
if ((parent.type === "Property" || parent.type === "MethodDefinition") && parent.key === node && !parent.computed && !(parent.type === "Property" && parent.shorthand)) return false;
|
|
92
|
+
if ((parent.type === "Property" || parent.type === "MethodDefinition" || parent.type === "PropertyDefinition" || parent.type === "AccessorProperty" || parent.type === "ImportAttribute") && parent.key === node && !parent.computed && !(parent.type === "Property" && parent.shorthand)) return false;
|
|
93
|
+
if (parent.type === "MetaProperty") return false;
|
|
93
94
|
if ((parent.type === "LabeledStatement" || parent.type === "BreakStatement" || parent.type === "ContinueStatement") && parent.label === node) return false;
|
|
94
95
|
const parameters = "params" in parent ? parent.params : void 0;
|
|
95
96
|
if (Array.isArray(parameters) && parameters.includes(node)) return false;
|
|
@@ -4,6 +4,7 @@ import "./server-globals.js";
|
|
|
4
4
|
import { resolveRequestHost, resolveRequestProtocol } from "./proxy-trust.js";
|
|
5
5
|
import { NextRequest } from "../shims/server.js";
|
|
6
6
|
import { importModule, reportRequestError } from "./instrumentation.js";
|
|
7
|
+
import { logRequestError } from "./request-error-log.js";
|
|
7
8
|
import { mergeRouteParamsIntoQuery, parseQueryString, urlQueryToSearchParams } from "../utils/query.js";
|
|
8
9
|
import { tracePagesApiHandler } from "./pages-execution-tracing.js";
|
|
9
10
|
import { DEFAULT_PAGES_API_BODY_SIZE_LIMIT, resolveBodyParserConfig } from "./pages-body-parser-config.js";
|
|
@@ -287,7 +288,7 @@ async function handleApiRoute(runner, req, res, url, apiRoutes, nextConfig) {
|
|
|
287
288
|
res.end(e.message);
|
|
288
289
|
return true;
|
|
289
290
|
}
|
|
290
|
-
|
|
291
|
+
logRequestError(e);
|
|
291
292
|
await reportRequestError(e, {
|
|
292
293
|
path: url,
|
|
293
294
|
method: req.method ?? "GET",
|
|
@@ -45,7 +45,7 @@ import { createHydrationCachePublication } from "./app-hydration-cache-publicati
|
|
|
45
45
|
import { peekSettledPrefetchResponseForNavigation, prepareConsumedPrefetchResponseForPublication, preserveCommittedPrefetchExpiry, resolvePrefetchNavigationResponseUrl } from "./app-browser-prefetch-response.js";
|
|
46
46
|
import { canCommitOptimisticRouteTemplate, createOptimisticRouteTemplate, createOptimisticRouteTemplateStore, getOptimisticPrefetchSourceKey, getOptimisticRouteTemplateKey, keepOptimisticStreamingMetadata, resolveOptimisticNavigationParamsForHref, resolveOptimisticNavigationPayload } from "./app-optimistic-routing.js";
|
|
47
47
|
import { removeStylesheetLinksCoveredByInlineCss } from "./app-inline-css-client.js";
|
|
48
|
-
import { createElement, startTransition, use, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
48
|
+
import { Fragment, createElement, startTransition, use, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
49
49
|
import { createRoot, hydrateRoot } from "react-dom/client";
|
|
50
50
|
import { createFromFetch, createFromReadableStream, setServerCallback } from "@vitejs/plugin-rsc/browser";
|
|
51
51
|
import { flushSync } from "react-dom";
|
|
@@ -1000,6 +1000,10 @@ function registerServerActionCallback() {
|
|
|
1000
1000
|
})).finally(releaseCacheInvalidationGuard);
|
|
1001
1001
|
});
|
|
1002
1002
|
}
|
|
1003
|
+
function ReplayInitialDevServerErrors({ report }) {
|
|
1004
|
+
useEffect(report, [report]);
|
|
1005
|
+
return null;
|
|
1006
|
+
}
|
|
1003
1007
|
async function main() {
|
|
1004
1008
|
if (!claimInitialAppRouterBootstrap()) return;
|
|
1005
1009
|
loadNavigationRuntimeRouteManifest();
|
|
@@ -1011,7 +1015,7 @@ async function main() {
|
|
|
1011
1015
|
devErrorOverlay = await import("../client/dev-error-overlay.js");
|
|
1012
1016
|
devErrorOverlay.installDevErrorOverlay();
|
|
1013
1017
|
devErrorOverlay.installViteHmrErrorHandler(import.meta.hot);
|
|
1014
|
-
devErrorOverlay.reportInitialDevServerErrors();
|
|
1018
|
+
if (document.documentElement.id !== "__next_error__") devErrorOverlay.reportInitialDevServerErrors();
|
|
1015
1019
|
}
|
|
1016
1020
|
const initialRscBootstrap = getNavigationRuntime()?.bootstrap.rsc;
|
|
1017
1021
|
const rscStream = await readInitialRscStream();
|
|
@@ -1098,7 +1102,7 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
1098
1102
|
for (const style of errorShellStyles) style.remove();
|
|
1099
1103
|
startTransition(() => {
|
|
1100
1104
|
const clientRoot = createRoot(document, createRootOptions);
|
|
1101
|
-
clientRoot.render(children);
|
|
1105
|
+
clientRoot.render(import.meta.env.DEV && devErrorOverlay ? createElement(Fragment, null, children, createElement(ReplayInitialDevServerErrors, { report: devErrorOverlay.reportInitialDevServerErrors })) : children);
|
|
1102
1106
|
window.__VINEXT_RSC_ROOT__ = clientRoot;
|
|
1103
1107
|
});
|
|
1104
1108
|
} else window.__VINEXT_RSC_ROOT__ = hydrateRootInTransition({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isDangerousScheme, reportBlockedDangerousNavigation } from "../shims/url-safety.js";
|
|
2
|
-
import { resolveHardNavigationTargetFromRscResponse, stripRscCacheBustingSearchParam,
|
|
2
|
+
import { resolveHardNavigationTargetFromRscResponse, stripRscCacheBustingSearchParam, toPathAbsolutePathname } from "./app-rsc-cache-busting.js";
|
|
3
3
|
//#region src/server/app-browser-rsc-redirect.ts
|
|
4
4
|
const MAX_RSC_REDIRECT_DEPTH = 10;
|
|
5
5
|
function blockDangerousStreamedRscRedirect(response, streamedRedirectTarget) {
|
|
@@ -11,10 +11,10 @@ function blockDangerousStreamedRscRedirect(response, streamedRedirectTarget) {
|
|
|
11
11
|
function toVisibleAppHref(href, origin) {
|
|
12
12
|
const url = new URL(href, origin);
|
|
13
13
|
stripRscCacheBustingSearchParam(url);
|
|
14
|
-
return `${
|
|
14
|
+
return `${toPathAbsolutePathname(url.pathname)}${url.search}${url.hash}`;
|
|
15
15
|
}
|
|
16
16
|
function toStreamedRedirectVisibleAppHref(url) {
|
|
17
|
-
return `${url.pathname}${url.search}${url.hash}`;
|
|
17
|
+
return `${toPathAbsolutePathname(url.pathname)}${url.search}${url.hash}`;
|
|
18
18
|
}
|
|
19
19
|
function resolveRedirectLifecycleHopFromTarget(options) {
|
|
20
20
|
if (options.targetUrl.origin !== options.origin) return {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { hasBasePath } from "../utils/base-path.js";
|
|
2
2
|
import { ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, VINEXT_RENDERED_PATH_AND_SEARCH_HEADER } from "./headers.js";
|
|
3
3
|
import { DANGEROUS_URL_BLOCK_MESSAGE, isDangerousScheme } from "../shims/url-safety.js";
|
|
4
|
-
import { VINEXT_RSC_COMPATIBILITY_ID_HEADER, createServerActionRequestUrl } from "./app-rsc-cache-busting.js";
|
|
4
|
+
import { VINEXT_RSC_COMPATIBILITY_ID_HEADER, createServerActionRequestUrl, toPathAbsolutePathname } from "./app-rsc-cache-busting.js";
|
|
5
5
|
import { throwOnServerActionNotFound } from "./server-action-not-found.js";
|
|
6
6
|
import { AppElementsWire } from "./app-elements-wire.js";
|
|
7
7
|
import "./app-elements.js";
|
|
@@ -49,7 +49,7 @@ var ServerActionRedirectError = class extends Error {
|
|
|
49
49
|
constructor(target) {
|
|
50
50
|
super("NEXT_REDIRECT");
|
|
51
51
|
const redirectUrl = new URL(target.href, window.location.href);
|
|
52
|
-
const redirectHref = redirectUrl.pathname + redirectUrl.search + redirectUrl.hash;
|
|
52
|
+
const redirectHref = toPathAbsolutePathname(redirectUrl.pathname) + redirectUrl.search + redirectUrl.hash;
|
|
53
53
|
const redirectType = target.type === "push" ? "push" : "replace";
|
|
54
54
|
this.digest = `NEXT_REDIRECT;${redirectType};${redirectHref};${target.status};`;
|
|
55
55
|
}
|
|
@@ -87,11 +87,11 @@ function reduceApprovedVisibleCommitState(state, commit) {
|
|
|
87
87
|
const hmrUniquePreserveElementIds = hmrPreserveElementIds.length > 1 ? [...new Set(hmrPreserveElementIds)] : hmrPreserveElementIds;
|
|
88
88
|
const preserveElementIds = action.operation.lane === "hmr" ? hmrUniquePreserveElementIds : bfcacheCompatiblePreserveElementIds;
|
|
89
89
|
const preserveTemplateIds = action.reuseCurrentBfcacheIds && (action.operation.lane === "navigation" || action.operation.lane === "traverse" || action.operation.lane === "server-action") ? Object.keys(state.elements).filter((id) => Object.hasOwn(action.elements, id) && hasSameServerTemplateSeedIdentity(id, state.navigationSnapshot, action.navigationSnapshot)) : [];
|
|
90
|
-
const mergedPreserveElementIds = preserveTemplateIds.length === 0 ? preserveElementIds : [.../* @__PURE__ */ new Set([...preserveElementIds, ...preserveTemplateIds])];
|
|
91
90
|
const mergedElements = mergeElements(state.elements, action.elements, {
|
|
92
91
|
clearAbsentSlots: action.type === "traverse" || !action.reuseCurrentBfcacheIds,
|
|
93
92
|
preserveAbsentSlots: action.reuseCurrentBfcacheIds && commit.decision.preserveAbsentSlots,
|
|
94
|
-
preserveElementIds
|
|
93
|
+
preserveElementIds,
|
|
94
|
+
preserveElementSeedIds: preserveTemplateIds,
|
|
95
95
|
preservePreviousSlotIds: effectivePreservePreviousSlotIds
|
|
96
96
|
});
|
|
97
97
|
return commitVisibleRouterState(state, {
|