vinext 1.2.0 → 1.2.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 +1 -1
- package/dist/build/output-root-externals.d.ts +21 -0
- package/dist/build/output-root-externals.js +40 -0
- package/dist/build/prerender-paths.js +1 -1
- package/dist/build/prerender.js +1 -1
- package/dist/check.js +5 -6
- package/dist/cli-dev-config.js +3 -3
- package/dist/cli.js +1 -1
- package/dist/config/config-matchers.js +12 -7
- package/dist/index.js +113 -94
- package/dist/init.d.ts +1 -1
- package/dist/init.js +86 -6
- package/dist/plugins/ast-utils.d.ts +53 -2
- package/dist/plugins/ast-utils.js +90 -2
- package/dist/plugins/cloudflare-dev-module-urls.d.ts +41 -0
- package/dist/plugins/cloudflare-dev-module-urls.js +271 -0
- package/dist/plugins/commonjs-esm-facade.js +2 -2
- package/dist/plugins/css-module-imports.js +13 -20
- package/dist/plugins/dynamic-preload-metadata.js +2 -3
- package/dist/plugins/extensionless-dynamic-import.js +2 -3
- package/dist/plugins/fonts.d.ts +1 -1
- package/dist/plugins/fonts.js +3 -3
- package/dist/plugins/ignore-dynamic-requests.js +2 -3
- package/dist/plugins/import-meta-url.js +3 -4
- package/dist/plugins/instrumentation-client.js +3 -2
- package/dist/plugins/middleware-export-validation.js +2 -3
- package/dist/plugins/og-harfbuzz.js +2 -3
- package/dist/plugins/optimize-imports.js +3 -4
- package/dist/plugins/pages-node-externals.js +2 -3
- package/dist/plugins/remove-console.js +2 -18
- package/dist/plugins/require-condition-resolution.js +3 -3
- package/dist/plugins/require-context.js +2 -3
- package/dist/plugins/strip-server-exports.js +3 -4
- package/dist/plugins/styled-jsx.js +2 -3
- package/dist/plugins/use-cache-callable.d.ts +21 -2
- package/dist/plugins/use-cache-callable.js +76 -33
- package/dist/plugins/worker-image-imports.js +30 -33
- package/dist/plugins/worker-use-server.js +38 -14
- package/dist/server/api-handler.js +1 -1
- package/dist/server/app-action-forwarding.js +2 -2
- package/dist/server/app-browser-entry.js +3 -3
- package/dist/server/app-browser-interception-context.js +1 -1
- package/dist/server/app-browser-prefetch-response.js +2 -2
- package/dist/server/app-browser-state.js +1 -1
- package/dist/server/app-browser-supplemental-refresh.js +1 -1
- package/dist/server/app-optimistic-routing.js +1 -1
- package/dist/server/app-page-dispatch.js +1 -1
- package/dist/server/app-page-render.js +1 -1
- package/dist/server/app-pages-bridge.js +1 -1
- package/dist/server/app-prerender-endpoints.js +1 -1
- package/dist/server/app-render-abort-error.d.ts +19 -0
- package/dist/server/app-render-abort-error.js +31 -1
- package/dist/server/app-request-stage-independent-entry.js +5 -5
- package/dist/server/app-response-stage-entry.js +2 -2
- package/dist/server/app-route-handler-runtime.js +1 -1
- package/dist/server/app-router-entry.js +5 -5
- package/dist/server/app-rsc-cache-busting.d.ts +16 -0
- package/dist/server/app-rsc-cache-busting.js +29 -12
- package/dist/server/app-rsc-handler.js +5 -5
- package/dist/server/app-rsc-request-normalization.js +3 -3
- package/dist/server/app-rsc-response-stage.js +2 -2
- package/dist/server/app-server-action-execution.js +1 -1
- package/dist/server/app-ssr-entry.js +5 -4
- package/dist/server/app-visited-response-cache.js +2 -2
- package/dist/server/config-headers.js +1 -1
- package/dist/server/defer-until-stream-consumed.d.ts +2 -1
- package/dist/server/defer-until-stream-consumed.js +3 -1
- package/dist/server/dev-server.js +1 -1
- package/dist/server/image-optimization.js +1 -0
- package/dist/server/metadata-route-response.js +2 -2
- package/dist/server/middleware-path-to-regexp.js +1 -1
- package/dist/server/middleware-runtime.js +5 -5
- package/dist/server/navigation-planner.js +1 -1
- package/dist/server/open-redirect.d.ts +11 -4
- package/dist/server/open-redirect.js +11 -4
- package/dist/server/pages-api-route.js +2 -2
- package/dist/server/pages-data-route.js +2 -1
- package/dist/server/pages-page-data.js +1 -1
- package/dist/server/pages-page-handler.js +1 -1
- package/dist/server/pages-page-response.js +1 -1
- package/dist/server/pages-request-pipeline.js +5 -5
- package/dist/server/pages-request-stage-entry.js +4 -4
- package/dist/server/pages-revalidate.js +2 -2
- package/dist/server/pages-router-entry.js +1 -1
- package/dist/server/prod-server.js +5 -5
- package/dist/server/rsc-stream-hints.d.ts +1 -1
- package/dist/server/rsc-stream-hints.js +21 -3
- package/dist/server/worker-image-optimization.js +19 -6
- package/dist/shims/head.js +1 -1
- package/dist/shims/headers.js +2 -2
- package/dist/shims/internal/hybrid-client-route-owner-direct.js +1 -1
- package/dist/shims/internal/hybrid-client-route-owner.js +1 -0
- package/dist/shims/link-prefetch.js +5 -4
- package/dist/shims/link.js +2 -2
- package/dist/shims/navigation.js +4 -5
- package/dist/shims/router.js +9 -12
- package/dist/shims/server.d.ts +11 -4
- package/dist/shims/server.js +41 -5
- package/dist/shims/url-utils.d.ts +7 -0
- package/dist/shims/url-utils.js +12 -3
- package/dist/utils/domain-locale.js +1 -1
- package/dist/utils/plugin-rsc-version.d.ts +2 -0
- package/dist/utils/plugin-rsc-version.js +28 -2
- package/dist/utils/react-version.d.ts +7 -0
- package/dist/utils/react-version.js +39 -16
- package/package.json +6 -6
- package/dist/build/nitro-route-rules.d.ts +0 -47
- package/dist/build/nitro-route-rules.js +0 -79
- package/dist/plugins/cache-flight-codec.d.ts +0 -19
- package/dist/plugins/cache-flight-codec.js +0 -68
package/dist/shims/router.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { isUnknownRecord } from "../utils/record.js";
|
|
2
2
|
import { parseCookieHeader } from "../utils/parse-cookie.js";
|
|
3
3
|
import { isExternalUrl } from "../utils/external-url.js";
|
|
4
|
+
import { hasBasePath, removeTrailingSlash, stripBasePath } from "../utils/base-path.js";
|
|
4
5
|
import { substituteDestinationParams } from "../config/destination-params.js";
|
|
5
6
|
import { sortRoutes, splitPathSegments } from "../routing/utils.js";
|
|
6
|
-
import { hasBasePath, removeTrailingSlash, stripBasePath } from "../utils/base-path.js";
|
|
7
7
|
import { assertSafeNavigationUrl } from "./url-safety.js";
|
|
8
8
|
import { addLocalePrefix, getDomainLocaleUrl, getLocalePathPrefix } from "../utils/domain-locale.js";
|
|
9
9
|
import { AppRouterContext } from "./internal/app-router-context.js";
|
|
@@ -935,7 +935,6 @@ function isAppComponent(value) {
|
|
|
935
935
|
function resolveSameOriginRedirectedUrl(responseUrl) {
|
|
936
936
|
const appPath = toSameOriginAppPath(responseUrl, __basePath);
|
|
937
937
|
if (appPath === null) return null;
|
|
938
|
-
if (appPath.startsWith("//")) return normalizePathTrailingSlash(responseUrl, __trailingSlash);
|
|
939
938
|
return normalizePathTrailingSlash(appPath, __trailingSlash);
|
|
940
939
|
}
|
|
941
940
|
function stripLocalePrefixForApiRedirect(appPath) {
|
|
@@ -957,19 +956,13 @@ function resolveLocalRedirectUrl(location) {
|
|
|
957
956
|
let appPath;
|
|
958
957
|
if (location.startsWith("/") && !location.startsWith("//")) try {
|
|
959
958
|
const parsed = new URL(location, "http://vinext.local");
|
|
960
|
-
|
|
959
|
+
const pathname = stripBasePath(parsed.pathname, __basePath);
|
|
960
|
+
appPath = pathname.startsWith("//") ? toSameOriginAppPath(new URL(location, window.location.href).href, __basePath) : pathname + parsed.search + parsed.hash;
|
|
961
961
|
} catch {
|
|
962
962
|
appPath = location;
|
|
963
963
|
}
|
|
964
964
|
else appPath = toSameOriginAppPath(location, __basePath);
|
|
965
965
|
if (appPath === null) return null;
|
|
966
|
-
if (appPath.startsWith("//")) try {
|
|
967
|
-
const browserUrl = new URL(location, window.location.href);
|
|
968
|
-
if (browserUrl.origin !== getWindowOrigin()) return null;
|
|
969
|
-
return normalizePathTrailingSlash(browserUrl.href, __trailingSlash);
|
|
970
|
-
} catch {
|
|
971
|
-
return null;
|
|
972
|
-
}
|
|
973
966
|
return normalizePathTrailingSlash(stripLocalePrefixForApiRedirect(appPath), __trailingSlash);
|
|
974
967
|
}
|
|
975
968
|
function hasClientRewriteRules() {
|
|
@@ -1816,6 +1809,10 @@ async function performNavigation(url, as, options, mode, onStateUpdate, redirect
|
|
|
1816
1809
|
const absoluteHistoryUrl = sameOriginPath === null ? void 0 : origin && inputHref.startsWith(origin) ? inputHref.slice(origin.length) : sameOriginPath;
|
|
1817
1810
|
const currentRoute = window.__NEXT_DATA__?.page ?? stripBasePath(window.location.pathname, __basePath);
|
|
1818
1811
|
const currentAs = stripBasePath(window.location.pathname, __basePath) + window.location.search;
|
|
1812
|
+
const resolveRelativeRoutePath = (href) => href === "" || /^[/?#]/.test(href) || isAbsoluteUrl(href) ? href : resolvePagesRouterHref(href, {
|
|
1813
|
+
pathname: currentRoute,
|
|
1814
|
+
asPath: currentAs
|
|
1815
|
+
}, __basePath);
|
|
1819
1816
|
const preparedHistoryHref = (isAbsoluteUrl(inputHref) ? toSameOriginAppPath(inputHref, __basePath) : null) ?? absoluteHistoryUrl ?? resolvePagesRouterHref(inputHref, {
|
|
1820
1817
|
pathname: currentRoute,
|
|
1821
1818
|
asPath: currentAs
|
|
@@ -1825,12 +1822,12 @@ async function performNavigation(url, as, options, mode, onStateUpdate, redirect
|
|
|
1825
1822
|
const replaceInheritedLocale = options?.locale !== void 0 && (!as ? urlInheritsCurrentPath || typeof url !== "string" && inheritsVisiblePath(url) : typeof as !== "string" && inheritsVisiblePath(as));
|
|
1826
1823
|
const credentialedHistoryHref = credentialedSameOriginHref(url);
|
|
1827
1824
|
const credentialedDisplayHref = credentialedSameOriginHref(as) ?? (!as ? credentialedHistoryHref : void 0);
|
|
1828
|
-
as = as ? resolveUrl(as, true) : void 0;
|
|
1825
|
+
as = as ? resolveRelativeRoutePath(resolveUrl(as, true)) : void 0;
|
|
1829
1826
|
assertSafeNavigationUrl(resolveUrl(url));
|
|
1830
1827
|
if (as !== void 0) assertSafeNavigationUrl(as);
|
|
1831
1828
|
const isHydrationQueryUpdate = options?._h === 1;
|
|
1832
1829
|
const navigationLocale = resolveTransitionLocale(options?.locale);
|
|
1833
|
-
const unlocalizedRouteHref = resolveUrl(isQueryOnlyString ? `${stripBasePath(window.location.pathname, __basePath)}${url}` : url);
|
|
1830
|
+
const unlocalizedRouteHref = resolveRelativeRoutePath(resolveUrl(isQueryOnlyString ? `${stripBasePath(window.location.pathname, __basePath)}${url}` : url));
|
|
1834
1831
|
const localRouteHref = isExternalUrl(unlocalizedRouteHref) ? toSameOriginAppPath(unlocalizedRouteHref, __basePath) : unlocalizedRouteHref;
|
|
1835
1832
|
const implicitDynamicRoute = !isHydrationQueryUpdate && as === void 0 && localRouteHref !== null ? resolveDynamicRouteHref(localRouteHref) : null;
|
|
1836
1833
|
const routeForNavigation = implicitDynamicRoute?.as ?? unlocalizedRouteHref;
|
package/dist/shims/server.d.ts
CHANGED
|
@@ -42,13 +42,20 @@ export declare class NextRequest extends Request {
|
|
|
42
42
|
get page(): void;
|
|
43
43
|
get ua(): void;
|
|
44
44
|
/**
|
|
45
|
-
* Client IP address.
|
|
46
|
-
*
|
|
45
|
+
* Client IP address. Next.js removed `ip` in v15; vinext keeps it as an
|
|
46
|
+
* extension. Returns undefined unless the value comes from a trusted source.
|
|
47
|
+
*
|
|
48
|
+
* On Cloudflare Workers (the request carries `cf` metadata), Cloudflare's
|
|
49
|
+
* edge sets `CF-Connecting-IP`. Elsewhere these headers are client-supplied
|
|
50
|
+
* and are only honored behind a trusted proxy (`VINEXT_TRUST_PROXY` /
|
|
51
|
+
* `VINEXT_TRUSTED_HOSTS`), matching the `X-Forwarded-*` policy.
|
|
47
52
|
*/
|
|
48
53
|
get ip(): string | undefined;
|
|
49
54
|
/**
|
|
50
|
-
* Geolocation data.
|
|
51
|
-
*
|
|
55
|
+
* Geolocation data. Next.js removed `geo` in v15; vinext keeps it as an
|
|
56
|
+
* extension. Read from Cloudflare's `request.cf` metadata on Workers, or
|
|
57
|
+
* from platform geo headers behind a trusted proxy. Returns undefined
|
|
58
|
+
* otherwise, because any client can send these headers.
|
|
52
59
|
*/
|
|
53
60
|
get geo(): {
|
|
54
61
|
city?: string;
|
package/dist/shims/server.js
CHANGED
|
@@ -3,10 +3,11 @@ import { MIDDLEWARE_SET_COOKIE_HEADER } from "../utils/protocol-headers.js";
|
|
|
3
3
|
import { encodeMiddlewareRequestHeaders } from "../utils/middleware-request-headers.js";
|
|
4
4
|
import { parseEdgeRequestCookieHeader } from "../utils/parse-cookie.js";
|
|
5
5
|
import { hasBasePath, removeTrailingSlash, stripBasePath } from "../utils/base-path.js";
|
|
6
|
-
import { getRequestExecutionContext } from "./request-context.js";
|
|
7
6
|
import { MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_REWRITE_HEADER } from "../server/headers.js";
|
|
7
|
+
import { getRequestExecutionContext } from "./request-context.js";
|
|
8
8
|
import { validateCookieAttributeValue, validateCookieName } from "./internal/cookie-serialize.js";
|
|
9
9
|
import { assertSafeNavigationUrl } from "./url-safety.js";
|
|
10
|
+
import { trustProxy } from "../server/proxy-trust.js";
|
|
10
11
|
import { userAgent, userAgentFromString } from "./user-agent.js";
|
|
11
12
|
//#region src/shims/server.ts
|
|
12
13
|
/**
|
|
@@ -97,6 +98,19 @@ function initWithoutInputBody(input, init) {
|
|
|
97
98
|
body: null
|
|
98
99
|
};
|
|
99
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* workerd's `request.cf` metadata. Only the Workers runtime sets it, so its
|
|
103
|
+
* presence means the request came through Cloudflare's edge rather than
|
|
104
|
+
* straight from a client.
|
|
105
|
+
*/
|
|
106
|
+
function readCloudflareMetadata(request) {
|
|
107
|
+
const cf = Reflect.get(request, "cf");
|
|
108
|
+
return cf !== null && typeof cf === "object" ? cf : void 0;
|
|
109
|
+
}
|
|
110
|
+
function readStringField(source, key) {
|
|
111
|
+
const value = Reflect.get(source, key);
|
|
112
|
+
return typeof value === "string" && value !== "" ? value : void 0;
|
|
113
|
+
}
|
|
100
114
|
var NextRequest = class extends Request {
|
|
101
115
|
_nextUrl;
|
|
102
116
|
_url;
|
|
@@ -155,17 +169,39 @@ var NextRequest = class extends Request {
|
|
|
155
169
|
throw new Error("NextRequest.ua has been removed; use userAgent() instead");
|
|
156
170
|
}
|
|
157
171
|
/**
|
|
158
|
-
* Client IP address.
|
|
159
|
-
*
|
|
172
|
+
* Client IP address. Next.js removed `ip` in v15; vinext keeps it as an
|
|
173
|
+
* extension. Returns undefined unless the value comes from a trusted source.
|
|
174
|
+
*
|
|
175
|
+
* On Cloudflare Workers (the request carries `cf` metadata), Cloudflare's
|
|
176
|
+
* edge sets `CF-Connecting-IP`. Elsewhere these headers are client-supplied
|
|
177
|
+
* and are only honored behind a trusted proxy (`VINEXT_TRUST_PROXY` /
|
|
178
|
+
* `VINEXT_TRUSTED_HOSTS`), matching the `X-Forwarded-*` policy.
|
|
160
179
|
*/
|
|
161
180
|
get ip() {
|
|
181
|
+
if (readCloudflareMetadata(this)) return this.headers.get("cf-connecting-ip") ?? void 0;
|
|
182
|
+
if (!trustProxy) return void 0;
|
|
162
183
|
return this.headers.get("cf-connecting-ip") ?? this.headers.get("x-real-ip") ?? this.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ?? void 0;
|
|
163
184
|
}
|
|
164
185
|
/**
|
|
165
|
-
* Geolocation data.
|
|
166
|
-
*
|
|
186
|
+
* Geolocation data. Next.js removed `geo` in v15; vinext keeps it as an
|
|
187
|
+
* extension. Read from Cloudflare's `request.cf` metadata on Workers, or
|
|
188
|
+
* from platform geo headers behind a trusted proxy. Returns undefined
|
|
189
|
+
* otherwise, because any client can send these headers.
|
|
167
190
|
*/
|
|
168
191
|
get geo() {
|
|
192
|
+
const cf = readCloudflareMetadata(this);
|
|
193
|
+
if (cf) {
|
|
194
|
+
const country = readStringField(cf, "country");
|
|
195
|
+
if (!country) return void 0;
|
|
196
|
+
return {
|
|
197
|
+
country,
|
|
198
|
+
city: readStringField(cf, "city"),
|
|
199
|
+
region: readStringField(cf, "region"),
|
|
200
|
+
latitude: readStringField(cf, "latitude"),
|
|
201
|
+
longitude: readStringField(cf, "longitude")
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
if (!trustProxy) return void 0;
|
|
169
205
|
const country = this.headers.get("cf-ipcountry") ?? this.headers.get("x-vercel-ip-country") ?? void 0;
|
|
170
206
|
if (!country) return void 0;
|
|
171
207
|
return {
|
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
export declare function isAbsoluteUrl(url: string): boolean;
|
|
3
3
|
export declare function isAbsoluteOrProtocolRelativeUrl(url: string): boolean;
|
|
4
4
|
export declare function getWindowOrigin(): string | null;
|
|
5
|
+
/**
|
|
6
|
+
* A same-origin pathname can start with "//" (e.g. `https://app.com//evil.com`),
|
|
7
|
+
* and as a local path that is a protocol-relative reference to another host.
|
|
8
|
+
* Collapse the leading slashes: Next.js does the same for these URLs, on the
|
|
9
|
+
* client (`resolveHref`) or with its server's repeated-slash 308.
|
|
10
|
+
*/
|
|
11
|
+
export declare function collapseLeadingSlashes(pathname: string): string;
|
|
5
12
|
/**
|
|
6
13
|
* If `url` is an absolute same-origin URL, return the local path
|
|
7
14
|
* (pathname + search + hash). Returns null for truly external URLs
|
package/dist/shims/url-utils.js
CHANGED
|
@@ -25,6 +25,15 @@ function getWindowOrigin() {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
|
+
* A same-origin pathname can start with "//" (e.g. `https://app.com//evil.com`),
|
|
29
|
+
* and as a local path that is a protocol-relative reference to another host.
|
|
30
|
+
* Collapse the leading slashes: Next.js does the same for these URLs, on the
|
|
31
|
+
* client (`resolveHref`) or with its server's repeated-slash 308.
|
|
32
|
+
*/
|
|
33
|
+
function collapseLeadingSlashes(pathname) {
|
|
34
|
+
return pathname.replace(/^\/{2,}/, "/");
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
28
37
|
* If `url` is an absolute same-origin URL, return the local path
|
|
29
38
|
* (pathname + search + hash). Returns null for truly external URLs
|
|
30
39
|
* or on the server (where origin is unknown).
|
|
@@ -34,7 +43,7 @@ function toSameOriginPath(url) {
|
|
|
34
43
|
if (!origin) return null;
|
|
35
44
|
try {
|
|
36
45
|
const parsed = url.startsWith("//") ? new URL(url, origin) : new URL(url);
|
|
37
|
-
if (parsed.origin === origin) return parsed.pathname + parsed.search + parsed.hash;
|
|
46
|
+
if (parsed.origin === origin) return collapseLeadingSlashes(parsed.pathname) + parsed.search + parsed.hash;
|
|
38
47
|
} catch {}
|
|
39
48
|
return null;
|
|
40
49
|
}
|
|
@@ -49,7 +58,7 @@ function toSameOriginAppPath(url, basePath) {
|
|
|
49
58
|
try {
|
|
50
59
|
const parsed = new URL(localPath, "http://vinext.local");
|
|
51
60
|
if (!hasBasePath(parsed.pathname, basePath)) return null;
|
|
52
|
-
return stripBasePath(parsed.pathname, basePath) + parsed.search + parsed.hash;
|
|
61
|
+
return collapseLeadingSlashes(stripBasePath(parsed.pathname, basePath)) + parsed.search + parsed.hash;
|
|
53
62
|
} catch {
|
|
54
63
|
return localPath;
|
|
55
64
|
}
|
|
@@ -164,4 +173,4 @@ function isHashOnlyBrowserUrlChange(href, currentHref, basePath = "") {
|
|
|
164
173
|
}
|
|
165
174
|
}
|
|
166
175
|
//#endregion
|
|
167
|
-
export { getWindowOrigin, isAbsoluteOrProtocolRelativeUrl, isAbsoluteUrl, isHashOnlyBrowserUrlChange, normalizePathTrailingSlash, resolveRelativeHref, toBrowserNavigationHref, toCanonicalBrowserNavigationHref, toSameOriginAppPath, toSameOriginPath, withBasePath };
|
|
176
|
+
export { collapseLeadingSlashes, getWindowOrigin, isAbsoluteOrProtocolRelativeUrl, isAbsoluteUrl, isHashOnlyBrowserUrlChange, normalizePathTrailingSlash, resolveRelativeHref, toBrowserNavigationHref, toCanonicalBrowserNavigationHref, toSameOriginAppPath, toSameOriginPath, withBasePath };
|
|
@@ -42,7 +42,7 @@ function getDomainLocaleUrl(url, locale, { basePath, currentHostname, domainItem
|
|
|
42
42
|
const targetDomain = detectDomainLocale(domainItems, void 0, locale);
|
|
43
43
|
if (!targetDomain) return void 0;
|
|
44
44
|
const currentDomain = detectDomainLocale(domainItems, currentHostname ?? void 0);
|
|
45
|
-
const localizedPath = addLocalePrefix(url
|
|
45
|
+
const localizedPath = addLocalePrefix(/^[/?#]/.test(url) ? url : `/${url}`, locale, targetDomain.defaultLocale);
|
|
46
46
|
if (currentDomain && normalizeDomainHostname(currentDomain.domain) === normalizeDomainHostname(targetDomain.domain)) return;
|
|
47
47
|
return `${`http${targetDomain.http ? "" : "s"}://`}${targetDomain.domain}${prefixBasePath(localizedPath, basePath)}`;
|
|
48
48
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
//#region src/utils/plugin-rsc-version.d.ts
|
|
2
2
|
/** Return the installed @vitejs/plugin-rsc version, or throw if it is unsupported. */
|
|
3
3
|
export declare function assertPluginRscVersion(resolvedRscPath: string): string;
|
|
4
|
+
/** Whether the package owning `dir` must install its own react-server-dom-webpack. */
|
|
5
|
+
export declare function needsAppReactServerDom(dir: string, pluginRscVersion: string): boolean;
|
|
4
6
|
//#endregion
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
|
|
2
|
+
import { MINIMUM_REACT_VERSION, findPackageVersion, isVersionBelow } from "./react-version.js";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import fs from "node:fs";
|
|
2
5
|
//#region src/utils/plugin-rsc-version.ts
|
|
3
6
|
const MINIMUM_PLUGIN_RSC_VERSION = [
|
|
4
7
|
0,
|
|
@@ -11,5 +14,28 @@ function assertPluginRscVersion(resolvedRscPath) {
|
|
|
11
14
|
if (!version || isVersionBelow(version, MINIMUM_PLUGIN_RSC_VERSION)) throw new Error(`vinext: App Router requires @vitejs/plugin-rsc ${MINIMUM_PLUGIN_RSC_VERSION.join(".")} or newer (found ${version ?? "an unknown version"}). Upgrade @vitejs/plugin-rsc.`);
|
|
12
15
|
return version;
|
|
13
16
|
}
|
|
17
|
+
const FIRST_PLUGIN_RSC_WITH_SUPPORTED_RSDW = [
|
|
18
|
+
0,
|
|
19
|
+
5,
|
|
20
|
+
38
|
|
21
|
+
];
|
|
22
|
+
/** Whether the package owning `dir` must install its own react-server-dom-webpack. */
|
|
23
|
+
function needsAppReactServerDom(dir, pluginRscVersion) {
|
|
24
|
+
if (!isVersionBelow(pluginRscVersion, FIRST_PLUGIN_RSC_WITH_SUPPORTED_RSDW)) return false;
|
|
25
|
+
let manifest = path.join(dir, "package.json");
|
|
26
|
+
while (!fs.existsSync(manifest)) {
|
|
27
|
+
const parent = path.dirname(path.dirname(manifest));
|
|
28
|
+
if (parent === path.dirname(manifest)) return true;
|
|
29
|
+
manifest = path.join(parent, "package.json");
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const pkg = JSON.parse(fs.readFileSync(manifest, "utf-8"));
|
|
33
|
+
if (!pkg.dependencies?.["react-server-dom-webpack"] && !pkg.devDependencies?.["react-server-dom-webpack"]) return true;
|
|
34
|
+
const version = findPackageVersion(createRequire(manifest).resolve("react-server-dom-webpack"), "react-server-dom-webpack");
|
|
35
|
+
return !version || isVersionBelow(version, MINIMUM_REACT_VERSION);
|
|
36
|
+
} catch {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
14
40
|
//#endregion
|
|
15
|
-
export { assertPluginRscVersion };
|
|
41
|
+
export { assertPluginRscVersion, needsAppReactServerDom };
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
//#region src/utils/react-version.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Lowest React accepted for App Router builds. react-server-dom-webpack 19.3.0
|
|
4
|
+
* shares typed-array buffers across Flight chunks, fixed in 19.3.1.
|
|
5
|
+
*/
|
|
6
|
+
export declare const MINIMUM_REACT_VERSION: [number, number, number];
|
|
2
7
|
export declare function getReactUpgradeDeps(root: string, options?: {
|
|
3
8
|
fromManifest?: boolean;
|
|
9
|
+
installing?: boolean;
|
|
4
10
|
}): string[];
|
|
11
|
+
export declare function reactUpgradeRequiredMessage(installCommand: string, upgrades: string[], pluginRscVersion?: string): string;
|
|
5
12
|
export declare function isVersionBelow(version: string, minimum: [number, number, number]): boolean;
|
|
6
13
|
/** Walk up from a resolved module entry to find its package version. */
|
|
7
14
|
export declare function findPackageVersion(resolvedEntry: string, packageName: string): string | null;
|
|
@@ -12,17 +12,27 @@ function getDependencyUpgradeDeps(root, recommendations) {
|
|
|
12
12
|
}
|
|
13
13
|
return [];
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Lowest React accepted for App Router builds. react-server-dom-webpack 19.3.0
|
|
17
|
+
* shares typed-array buffers across Flight chunks, fixed in 19.3.1.
|
|
18
|
+
*/
|
|
19
|
+
const MINIMUM_REACT_VERSION = [
|
|
20
|
+
19,
|
|
21
|
+
3,
|
|
22
|
+
1
|
|
23
|
+
];
|
|
15
24
|
function getReactUpgradeDeps(root, options = {}) {
|
|
16
25
|
if (options.fromManifest) {
|
|
17
26
|
const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf-8"));
|
|
18
|
-
for (const name of [
|
|
27
|
+
for (const name of [
|
|
28
|
+
"react",
|
|
29
|
+
"react-dom",
|
|
30
|
+
"react-server-dom-webpack"
|
|
31
|
+
]) {
|
|
19
32
|
const declared = pkg.dependencies?.[name] ?? pkg.devDependencies?.[name];
|
|
20
|
-
const version = /^[~^]?([0-9]+(?:\.[0-9]+){0,2}(?:-[0-9A-Za-z.*-]+)?)(?:\.[x*])
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
2,
|
|
24
|
-
6
|
|
25
|
-
]))) {
|
|
33
|
+
const version = (options.installing ? /^([0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?)$/ : /^[~^]?([0-9]+(?:\.[0-9]+){0,2}(?:-[0-9A-Za-z.*-]+)?)(?:\.[x*])?$/).exec(declared ?? "")?.[1];
|
|
34
|
+
const canary = !options.installing && version?.includes("-");
|
|
35
|
+
if (version && (canary || isVersionBelow(version, MINIMUM_REACT_VERSION))) {
|
|
26
36
|
const upgrades = ["react@latest", "react-dom@latest"];
|
|
27
37
|
const rsdw = pkg.dependencies?.["react-server-dom-webpack"] ?? pkg.devDependencies?.["react-server-dom-webpack"];
|
|
28
38
|
if (rsdw && rsdw !== "latest") upgrades.push("react-server-dom-webpack@latest");
|
|
@@ -31,14 +41,27 @@ function getReactUpgradeDeps(root, options = {}) {
|
|
|
31
41
|
}
|
|
32
42
|
return [];
|
|
33
43
|
}
|
|
34
|
-
return getDependencyUpgradeDeps(root, {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
44
|
+
return getDependencyUpgradeDeps(root, {
|
|
45
|
+
"react-server-dom-webpack": {
|
|
46
|
+
minimumVersion: MINIMUM_REACT_VERSION,
|
|
47
|
+
upgrades: [
|
|
48
|
+
"react@latest",
|
|
49
|
+
"react-dom@latest",
|
|
50
|
+
"react-server-dom-webpack@latest"
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
react: {
|
|
54
|
+
minimumVersion: MINIMUM_REACT_VERSION,
|
|
55
|
+
upgrades: ["react@latest", "react-dom@latest"]
|
|
56
|
+
},
|
|
57
|
+
"react-dom": {
|
|
58
|
+
minimumVersion: MINIMUM_REACT_VERSION,
|
|
59
|
+
upgrades: ["react@latest", "react-dom@latest"]
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function reactUpgradeRequiredMessage(installCommand, upgrades, pluginRscVersion) {
|
|
64
|
+
return `vinext: App Router requires react, react-dom, and react-server-dom-webpack ${MINIMUM_REACT_VERSION.join(".")} or later.\n` + (pluginRscVersion ? `@vitejs/plugin-rsc ${pluginRscVersion} bundles an older react-server-dom-webpack, so install the latest in your app.\n` : "") + `Run: ${installCommand} ${upgrades.join(" ")}`;
|
|
42
65
|
}
|
|
43
66
|
function isVersionBelow(version, minimum) {
|
|
44
67
|
const current = version.split(".").map((part) => parseInt(part, 10));
|
|
@@ -59,4 +82,4 @@ function findPackageVersion(resolvedEntry, packageName) {
|
|
|
59
82
|
return null;
|
|
60
83
|
}
|
|
61
84
|
//#endregion
|
|
62
|
-
export { findPackageVersion, getReactUpgradeDeps, isVersionBelow };
|
|
85
|
+
export { MINIMUM_REACT_VERSION, findPackageVersion, getReactUpgradeDeps, isVersionBelow, reactUpgradeRequiredMessage };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vinext",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Run Next.js apps on Vite. Drop-in replacement for the next CLI.",
|
|
5
5
|
"homepage": "https://vinext.dev/",
|
|
6
6
|
"license": "MIT",
|
|
@@ -238,12 +238,12 @@
|
|
|
238
238
|
"@types/react-dom": "^19.2.3",
|
|
239
239
|
"@types/ua-parser-js": "0.7.36",
|
|
240
240
|
"@vitejs/plugin-react": "^6.1.0",
|
|
241
|
-
"@vitejs/plugin-rsc": "^0.5.
|
|
241
|
+
"@vitejs/plugin-rsc": "^0.5.38",
|
|
242
242
|
"am-i-vibing": "^0.5.0",
|
|
243
243
|
"ignore": "5.3.2",
|
|
244
244
|
"image-size": "2.0.2",
|
|
245
245
|
"pathslash": "^0.1.0",
|
|
246
|
-
"react-server-dom-webpack": "^19.
|
|
246
|
+
"react-server-dom-webpack": "^19.3.1",
|
|
247
247
|
"ua-parser-js": "1.0.35",
|
|
248
248
|
"vite": "npm:@voidzero-dev/vite-plus-core@1.1.0",
|
|
249
249
|
"vite-plus": "1.1.0"
|
|
@@ -252,9 +252,9 @@
|
|
|
252
252
|
"@mdx-js/rollup": "^3.0.0",
|
|
253
253
|
"@vitejs/plugin-react": "^5.1.4 || ^6.0.0",
|
|
254
254
|
"@vitejs/plugin-rsc": "^0.5.37",
|
|
255
|
-
"react": "^19.
|
|
256
|
-
"react-dom": "^19.
|
|
257
|
-
"react-server-dom-webpack": "^19.
|
|
255
|
+
"react": "^19.3.1",
|
|
256
|
+
"react-dom": "^19.3.1",
|
|
257
|
+
"react-server-dom-webpack": "^19.3.1",
|
|
258
258
|
"vite": "^8.0.0"
|
|
259
259
|
},
|
|
260
260
|
"peerDependenciesMeta": {
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { RouteRow } from "./report.js";
|
|
2
|
-
//#region src/build/nitro-route-rules.d.ts
|
|
3
|
-
export type NitroRouteRuleConfig = Record<string, unknown> & {
|
|
4
|
-
swr?: boolean | number;
|
|
5
|
-
cache?: unknown;
|
|
6
|
-
static?: boolean;
|
|
7
|
-
isr?: boolean | number;
|
|
8
|
-
prerender?: boolean;
|
|
9
|
-
};
|
|
10
|
-
type NitroRouteRules = Record<string, {
|
|
11
|
-
swr: number;
|
|
12
|
-
}>;
|
|
13
|
-
/**
|
|
14
|
-
* Scans the filesystem for route files and generates Nitro `routeRules` for ISR routes.
|
|
15
|
-
*
|
|
16
|
-
* Note: this duplicates the filesystem scanning that `printBuildReport` also performs.
|
|
17
|
-
* The `nitro.setup` hook runs during Nitro initialization (before the build), while
|
|
18
|
-
* `printBuildReport` runs after the build, so sharing results is non-trivial. This is
|
|
19
|
-
* a future optimization target.
|
|
20
|
-
*
|
|
21
|
-
* Unlike `printBuildReport`, this path does not receive `prerenderResult`, so routes
|
|
22
|
-
* classified as `unknown` by static analysis (which `printBuildReport` might upgrade
|
|
23
|
-
* to `static` via speculative prerender) are skipped here.
|
|
24
|
-
*/
|
|
25
|
-
export declare function collectNitroRouteRules(options: {
|
|
26
|
-
appDir?: string | null;
|
|
27
|
-
pagesDir?: string | null;
|
|
28
|
-
pageExtensions: string[];
|
|
29
|
-
}): Promise<NitroRouteRules>;
|
|
30
|
-
export declare function generateNitroRouteRules(rows: RouteRow[]): NitroRouteRules;
|
|
31
|
-
/**
|
|
32
|
-
* Converts vinext's internal `:param` route syntax to Nitro's rou3
|
|
33
|
-
* pattern format. Nitro uses `rou3` for routeRules matching, which
|
|
34
|
-
* supports `*` (single-segment) and `**` (multi-segment) wildcards.
|
|
35
|
-
*
|
|
36
|
-
* /blog/:slug -> /blog/* (single segment)
|
|
37
|
-
* /docs/:slug+ -> /docs/** (one or more segments — catch-all)
|
|
38
|
-
* /docs/:slug* -> /docs/** (zero or more segments — optional catch-all)
|
|
39
|
-
* /about -> /about (unchanged)
|
|
40
|
-
* /:a/:b produces `/*`/`/*` (consecutive single-segment params)
|
|
41
|
-
*/
|
|
42
|
-
export declare function convertToNitroPattern(pattern: string): string;
|
|
43
|
-
export declare function mergeNitroRouteRules(existingRouteRules: Record<string, NitroRouteRuleConfig> | undefined, generatedRouteRules: NitroRouteRules): {
|
|
44
|
-
routeRules: Record<string, NitroRouteRuleConfig>;
|
|
45
|
-
skippedRoutes: string[];
|
|
46
|
-
};
|
|
47
|
-
//#endregion
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { appRouter } from "../routing/app-router.js";
|
|
2
|
-
import { apiRouter, pagesRouter } from "../routing/pages-router.js";
|
|
3
|
-
import { buildReportRows } from "./report.js";
|
|
4
|
-
//#region src/build/nitro-route-rules.ts
|
|
5
|
-
/**
|
|
6
|
-
* Scans the filesystem for route files and generates Nitro `routeRules` for ISR routes.
|
|
7
|
-
*
|
|
8
|
-
* Note: this duplicates the filesystem scanning that `printBuildReport` also performs.
|
|
9
|
-
* The `nitro.setup` hook runs during Nitro initialization (before the build), while
|
|
10
|
-
* `printBuildReport` runs after the build, so sharing results is non-trivial. This is
|
|
11
|
-
* a future optimization target.
|
|
12
|
-
*
|
|
13
|
-
* Unlike `printBuildReport`, this path does not receive `prerenderResult`, so routes
|
|
14
|
-
* classified as `unknown` by static analysis (which `printBuildReport` might upgrade
|
|
15
|
-
* to `static` via speculative prerender) are skipped here.
|
|
16
|
-
*/
|
|
17
|
-
async function collectNitroRouteRules(options) {
|
|
18
|
-
const { appDir, pageExtensions, pagesDir } = options;
|
|
19
|
-
let appRoutes = [];
|
|
20
|
-
let pageRoutes = [];
|
|
21
|
-
let apiRoutes = [];
|
|
22
|
-
if (appDir) appRoutes = await appRouter(appDir, pageExtensions);
|
|
23
|
-
if (pagesDir) {
|
|
24
|
-
const [pages, apis] = await Promise.all([pagesRouter(pagesDir, pageExtensions), apiRouter(pagesDir, pageExtensions)]);
|
|
25
|
-
pageRoutes = pages;
|
|
26
|
-
apiRoutes = apis;
|
|
27
|
-
}
|
|
28
|
-
return generateNitroRouteRules(buildReportRows({
|
|
29
|
-
appRoutes,
|
|
30
|
-
pageRoutes,
|
|
31
|
-
apiRoutes
|
|
32
|
-
}));
|
|
33
|
-
}
|
|
34
|
-
function generateNitroRouteRules(rows) {
|
|
35
|
-
const rules = {};
|
|
36
|
-
for (const row of rows) if (row.type === "isr" && typeof row.revalidate === "number" && Number.isFinite(row.revalidate) && row.revalidate > 0) rules[convertToNitroPattern(row.pattern)] = { swr: row.revalidate };
|
|
37
|
-
return rules;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Converts vinext's internal `:param` route syntax to Nitro's rou3
|
|
41
|
-
* pattern format. Nitro uses `rou3` for routeRules matching, which
|
|
42
|
-
* supports `*` (single-segment) and `**` (multi-segment) wildcards.
|
|
43
|
-
*
|
|
44
|
-
* /blog/:slug -> /blog/* (single segment)
|
|
45
|
-
* /docs/:slug+ -> /docs/** (one or more segments — catch-all)
|
|
46
|
-
* /docs/:slug* -> /docs/** (zero or more segments — optional catch-all)
|
|
47
|
-
* /about -> /about (unchanged)
|
|
48
|
-
* /:a/:b produces `/*`/`/*` (consecutive single-segment params)
|
|
49
|
-
*/
|
|
50
|
-
function convertToNitroPattern(pattern) {
|
|
51
|
-
return pattern.split("/").map((segment) => {
|
|
52
|
-
if (segment.startsWith(":")) return segment.endsWith("+") || segment.endsWith("*") ? "**" : "*";
|
|
53
|
-
return segment;
|
|
54
|
-
}).join("/");
|
|
55
|
-
}
|
|
56
|
-
function mergeNitroRouteRules(existingRouteRules, generatedRouteRules) {
|
|
57
|
-
const routeRules = { ...existingRouteRules };
|
|
58
|
-
const skippedRoutes = [];
|
|
59
|
-
for (const [route, generatedRule] of Object.entries(generatedRouteRules)) {
|
|
60
|
-
const existingRule = routeRules[route];
|
|
61
|
-
if (existingRule && hasUserDefinedCacheRule(existingRule)) {
|
|
62
|
-
skippedRoutes.push(route);
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
routeRules[route] = {
|
|
66
|
-
...existingRule,
|
|
67
|
-
...generatedRule
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
return {
|
|
71
|
-
routeRules,
|
|
72
|
-
skippedRoutes
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
function hasUserDefinedCacheRule(rule) {
|
|
76
|
-
return rule.swr !== void 0 || rule.cache !== void 0 || rule.static !== void 0 || rule.isr !== void 0 || rule.prerender !== void 0;
|
|
77
|
-
}
|
|
78
|
-
//#endregion
|
|
79
|
-
export { collectNitroRouteRules, convertToNitroPattern, generateNitroRouteRules, mergeNitroRouteRules };
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { MagicStringTransformResult } from "./transform-result.js";
|
|
2
|
-
import { Plugin } from "vite";
|
|
3
|
-
//#region src/plugins/cache-flight-codec.d.ts
|
|
4
|
-
/**
|
|
5
|
-
* Version of the Flight codec package that owns `id`. plugin-rsc resolves the
|
|
6
|
-
* app's own react-server-dom-webpack when installed and its vendored copy
|
|
7
|
-
* otherwise; reading the codec file's own package.json follows that choice.
|
|
8
|
-
*/
|
|
9
|
-
export declare function readFlightCodecVersion(id: string): string;
|
|
10
|
-
/**
|
|
11
|
-
* Backport React 19.3's root-element temporary reference branch to older Flight
|
|
12
|
-
* codecs, so a "use cache" argument like `Promise<ReactElement>` encodes as a
|
|
13
|
-
* temporary reference instead of throwing. Remove it once React 19.3 is the
|
|
14
|
-
* minimum supported version.
|
|
15
|
-
* https://github.com/facebook/react/blob/v19.3.0/packages/react-client/src/ReactFlightReplyClient.js#L486
|
|
16
|
-
*/
|
|
17
|
-
export declare function patchCacheFlightCodec(code: string, id: string, version?: string): MagicStringTransformResult<import("magic-string").SourceMap> | null;
|
|
18
|
-
export declare function cacheFlightCodecPlugin(): Plugin;
|
|
19
|
-
//#endregion
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
|
|
2
|
-
import { magicStringTransformResult } from "./transform-result.js";
|
|
3
|
-
import fs from "node:fs";
|
|
4
|
-
import MagicString from "magic-string";
|
|
5
|
-
//#region src/plugins/cache-flight-codec.ts
|
|
6
|
-
const CLIENT_CODEC = /\/react-server-dom-(?:webpack|turbopack)-client\.edge\.(?:development|production)\.js$/;
|
|
7
|
-
const ELEMENT_CASE = /case REACT_ELEMENT_TYPE:[\s\S]*?throw Error\(\s*"React Element cannot be passed/;
|
|
8
|
-
/**
|
|
9
|
-
* Version of the Flight codec package that owns `id`. plugin-rsc resolves the
|
|
10
|
-
* app's own react-server-dom-webpack when installed and its vendored copy
|
|
11
|
-
* otherwise; reading the codec file's own package.json follows that choice.
|
|
12
|
-
*/
|
|
13
|
-
function readFlightCodecVersion(id) {
|
|
14
|
-
for (let dir = path.dirname(id);; dir = path.dirname(dir)) {
|
|
15
|
-
const manifest = path.join(dir, "package.json");
|
|
16
|
-
if (fs.existsSync(manifest)) {
|
|
17
|
-
const pkg = JSON.parse(fs.readFileSync(manifest, "utf8"));
|
|
18
|
-
if (/^react-server-dom-(?:webpack|turbopack)$/.test(pkg.name ?? "") && pkg.version) return pkg.version;
|
|
19
|
-
}
|
|
20
|
-
if (path.dirname(dir) === dir) throw new Error(`vinext: cannot find the react-server-dom package version for ${id}`);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
/** React 19.3 ships the branch; 0.0.0-* experimental builds track main. */
|
|
24
|
-
function needsElementTemporaryReferenceBackport(version) {
|
|
25
|
-
const match = /^(\d+)\.(\d+)\./.exec(version);
|
|
26
|
-
if (!match || version.startsWith("0.0.0-")) return false;
|
|
27
|
-
const [major, minor] = [Number(match[1]), Number(match[2])];
|
|
28
|
-
return major < 19 || major === 19 && minor < 3;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Backport React 19.3's root-element temporary reference branch to older Flight
|
|
32
|
-
* codecs, so a "use cache" argument like `Promise<ReactElement>` encodes as a
|
|
33
|
-
* temporary reference instead of throwing. Remove it once React 19.3 is the
|
|
34
|
-
* minimum supported version.
|
|
35
|
-
* https://github.com/facebook/react/blob/v19.3.0/packages/react-client/src/ReactFlightReplyClient.js#L486
|
|
36
|
-
*/
|
|
37
|
-
function patchCacheFlightCodec(code, id, version = readFlightCodecVersion(id)) {
|
|
38
|
-
if (!needsElementTemporaryReferenceBackport(version)) return null;
|
|
39
|
-
const elementCase = ELEMENT_CASE.exec(code);
|
|
40
|
-
if (!elementCase) throw new Error(`vinext: react-server-dom-webpack ${version} (${id}) is not supported. Upgrade react, react-dom, and react-server-dom-webpack to 19.3 or later. If your app does not depend on react-server-dom-webpack directly, upgrade @vitejs/plugin-rsc to 0.5.35 or later instead.`);
|
|
41
|
-
const output = new MagicString(code);
|
|
42
|
-
output.appendLeft(elementCase.index + elementCase[0].lastIndexOf("throw Error("), "if (void 0 !== temporaryReferences && modelRoot === value) return (modelRoot = null), \"$T\";\n");
|
|
43
|
-
return magicStringTransformResult(output, {
|
|
44
|
-
hires: "boundary",
|
|
45
|
-
source: id
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
function cacheFlightCodecPlugin() {
|
|
49
|
-
const transform = {
|
|
50
|
-
filter: { id: CLIENT_CODEC },
|
|
51
|
-
handler: (code, id) => patchCacheFlightCodec(code, id)
|
|
52
|
-
};
|
|
53
|
-
return {
|
|
54
|
-
name: "vinext:cache-flight-codec",
|
|
55
|
-
enforce: "pre",
|
|
56
|
-
applyToEnvironment: (environment) => environment.name === "rsc",
|
|
57
|
-
configEnvironment(name) {
|
|
58
|
-
if (name !== "rsc") return;
|
|
59
|
-
return { optimizeDeps: { rolldownOptions: { plugins: [{
|
|
60
|
-
name: "vinext:cache-flight-codec-optimizer",
|
|
61
|
-
transform
|
|
62
|
-
}] } } };
|
|
63
|
-
},
|
|
64
|
-
transform
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
//#endregion
|
|
68
|
-
export { cacheFlightCodecPlugin, patchCacheFlightCodec, readFlightCodecVersion };
|