vinext 0.0.51 → 0.0.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build/precompress.d.ts +7 -7
- package/dist/build/precompress.js +18 -17
- package/dist/build/precompress.js.map +1 -1
- package/dist/build/prerender.d.ts +3 -14
- package/dist/build/prerender.js +40 -40
- package/dist/build/prerender.js.map +1 -1
- package/dist/check.js +4 -0
- package/dist/check.js.map +1 -1
- package/dist/cli-args.d.ts +1 -0
- package/dist/cli-args.js +5 -0
- package/dist/cli-args.js.map +1 -1
- package/dist/cli.js +39 -0
- package/dist/cli.js.map +1 -1
- package/dist/client/navigation-runtime.d.ts +47 -0
- package/dist/client/navigation-runtime.js +156 -0
- package/dist/client/navigation-runtime.js.map +1 -0
- package/dist/client/pages-router-link-navigation.d.ts +26 -0
- package/dist/client/pages-router-link-navigation.js +14 -0
- package/dist/client/pages-router-link-navigation.js.map +1 -0
- package/dist/client/vinext-next-data.d.ts +12 -2
- package/dist/client/vinext-next-data.js +50 -1
- package/dist/client/vinext-next-data.js.map +1 -0
- package/dist/cloudflare/kv-cache-handler.js +2 -1
- package/dist/cloudflare/kv-cache-handler.js.map +1 -1
- package/dist/config/config-matchers.d.ts +63 -16
- package/dist/config/config-matchers.js +143 -8
- package/dist/config/config-matchers.js.map +1 -1
- package/dist/config/next-config.d.ts +20 -2
- package/dist/config/next-config.js +11 -1
- package/dist/config/next-config.js.map +1 -1
- package/dist/deploy.js +101 -39
- package/dist/deploy.js.map +1 -1
- package/dist/entries/app-browser-entry.js +9 -3
- package/dist/entries/app-browser-entry.js.map +1 -1
- package/dist/entries/app-rsc-entry.js +53 -13
- package/dist/entries/app-rsc-entry.js.map +1 -1
- package/dist/entries/app-rsc-manifest.d.ts +1 -0
- package/dist/entries/app-rsc-manifest.js +53 -6
- package/dist/entries/app-rsc-manifest.js.map +1 -1
- package/dist/entries/app-ssr-entry.d.ts +3 -3
- package/dist/entries/app-ssr-entry.js +4 -4
- package/dist/entries/app-ssr-entry.js.map +1 -1
- package/dist/entries/pages-client-entry.js +18 -2
- package/dist/entries/pages-client-entry.js.map +1 -1
- package/dist/entries/pages-server-entry.js +58 -8
- package/dist/entries/pages-server-entry.js.map +1 -1
- package/dist/entries/runtime-entry-module.d.ts +2 -1
- package/dist/entries/runtime-entry-module.js +9 -3
- package/dist/entries/runtime-entry-module.js.map +1 -1
- package/dist/index.js +132 -40
- package/dist/index.js.map +1 -1
- package/dist/plugins/css-data-url.d.ts +7 -0
- package/dist/plugins/css-data-url.js +81 -0
- package/dist/plugins/css-data-url.js.map +1 -0
- package/dist/plugins/fonts.js +5 -3
- package/dist/plugins/fonts.js.map +1 -1
- package/dist/plugins/middleware-server-only.d.ts +54 -0
- package/dist/plugins/middleware-server-only.js +91 -0
- package/dist/plugins/middleware-server-only.js.map +1 -0
- package/dist/plugins/optimize-imports.js +4 -4
- package/dist/plugins/optimize-imports.js.map +1 -1
- package/dist/plugins/strip-server-exports.js +5 -8
- package/dist/plugins/strip-server-exports.js.map +1 -1
- package/dist/routing/app-route-graph.d.ts +20 -1
- package/dist/routing/app-route-graph.js +58 -6
- package/dist/routing/app-route-graph.js.map +1 -1
- package/dist/routing/app-router.d.ts +2 -2
- package/dist/routing/app-router.js +2 -2
- package/dist/routing/app-router.js.map +1 -1
- package/dist/routing/utils.d.ts +2 -1
- package/dist/routing/utils.js +4 -1
- package/dist/routing/utils.js.map +1 -1
- package/dist/server/api-handler.js +139 -37
- package/dist/server/api-handler.js.map +1 -1
- package/dist/server/app-browser-entry.js +293 -149
- package/dist/server/app-browser-entry.js.map +1 -1
- package/dist/server/app-browser-interception-context.d.ts +24 -0
- package/dist/server/app-browser-interception-context.js +32 -0
- package/dist/server/app-browser-interception-context.js.map +1 -0
- package/dist/server/app-browser-navigation-controller.d.ts +3 -1
- package/dist/server/app-browser-navigation-controller.js +5 -1
- package/dist/server/app-browser-navigation-controller.js.map +1 -1
- package/dist/server/app-browser-rsc-redirect.d.ts +2 -1
- package/dist/server/app-browser-rsc-redirect.js +2 -2
- package/dist/server/app-browser-rsc-redirect.js.map +1 -1
- package/dist/server/app-browser-state.d.ts +18 -1
- package/dist/server/app-browser-state.js +19 -1
- package/dist/server/app-browser-state.js.map +1 -1
- package/dist/server/app-browser-stream.d.ts +5 -14
- package/dist/server/app-browser-stream.js +13 -7
- package/dist/server/app-browser-stream.js.map +1 -1
- package/dist/server/app-browser-visible-commit.d.ts +2 -1
- package/dist/server/app-browser-visible-commit.js +1 -0
- package/dist/server/app-browser-visible-commit.js.map +1 -1
- package/dist/server/app-elements-wire.d.ts +10 -5
- package/dist/server/app-elements-wire.js +84 -2
- package/dist/server/app-elements-wire.js.map +1 -1
- package/dist/server/app-elements.d.ts +3 -2
- package/dist/server/app-elements.js +3 -2
- package/dist/server/app-elements.js.map +1 -1
- package/dist/server/app-fallback-renderer.js +5 -3
- package/dist/server/app-fallback-renderer.js.map +1 -1
- package/dist/server/app-middleware.d.ts +13 -0
- package/dist/server/app-middleware.js +3 -1
- package/dist/server/app-middleware.js.map +1 -1
- package/dist/server/app-optimistic-routing.d.ts +54 -0
- package/dist/server/app-optimistic-routing.js +200 -0
- package/dist/server/app-optimistic-routing.js.map +1 -0
- package/dist/server/app-page-cache.d.ts +13 -1
- package/dist/server/app-page-cache.js +61 -6
- package/dist/server/app-page-cache.js.map +1 -1
- package/dist/server/app-page-dispatch.d.ts +2 -0
- package/dist/server/app-page-dispatch.js +28 -1
- package/dist/server/app-page-dispatch.js.map +1 -1
- package/dist/server/app-page-element-builder.js +2 -1
- package/dist/server/app-page-element-builder.js.map +1 -1
- package/dist/server/app-page-execution.d.ts +28 -1
- package/dist/server/app-page-execution.js +89 -4
- package/dist/server/app-page-execution.js.map +1 -1
- package/dist/server/app-page-head.js +21 -2
- package/dist/server/app-page-head.js.map +1 -1
- package/dist/server/app-page-probe.js +1 -1
- package/dist/server/app-page-render.d.ts +2 -0
- package/dist/server/app-page-render.js +2 -1
- package/dist/server/app-page-render.js.map +1 -1
- package/dist/server/app-page-response.js +4 -3
- package/dist/server/app-page-response.js.map +1 -1
- package/dist/server/app-page-route-wiring.js +17 -10
- package/dist/server/app-page-route-wiring.js.map +1 -1
- package/dist/server/app-page-stream.d.ts +3 -0
- package/dist/server/app-page-stream.js +1 -0
- package/dist/server/app-page-stream.js.map +1 -1
- package/dist/server/app-prerender-static-params.d.ts +2 -1
- package/dist/server/app-prerender-static-params.js +44 -8
- package/dist/server/app-prerender-static-params.js.map +1 -1
- package/dist/server/app-route-handler-cache.d.ts +2 -2
- package/dist/server/app-route-handler-cache.js +3 -2
- package/dist/server/app-route-handler-cache.js.map +1 -1
- package/dist/server/app-route-handler-dispatch.d.ts +6 -1
- package/dist/server/app-route-handler-dispatch.js +1 -1
- package/dist/server/app-route-handler-dispatch.js.map +1 -1
- package/dist/server/app-route-handler-execution.d.ts +17 -2
- package/dist/server/app-route-handler-execution.js.map +1 -1
- package/dist/server/app-route-handler-response.js +5 -4
- package/dist/server/app-route-handler-response.js.map +1 -1
- package/dist/server/app-router-entry.js +6 -2
- package/dist/server/app-router-entry.js.map +1 -1
- package/dist/server/app-rsc-handler.d.ts +9 -1
- package/dist/server/app-rsc-handler.js +32 -14
- package/dist/server/app-rsc-handler.js.map +1 -1
- package/dist/server/app-rsc-render-mode.d.ts +4 -3
- package/dist/server/app-rsc-render-mode.js +7 -1
- package/dist/server/app-rsc-render-mode.js.map +1 -1
- package/dist/server/app-rsc-request-normalization.d.ts +4 -1
- package/dist/server/app-rsc-request-normalization.js +4 -1
- package/dist/server/app-rsc-request-normalization.js.map +1 -1
- package/dist/server/app-rsc-response-finalizer.d.ts +8 -1
- package/dist/server/app-rsc-response-finalizer.js +10 -3
- package/dist/server/app-rsc-response-finalizer.js.map +1 -1
- package/dist/server/app-rsc-route-matching.js +2 -2
- package/dist/server/app-rsc-route-matching.js.map +1 -1
- package/dist/server/app-server-action-execution.js +1 -1
- package/dist/server/app-ssr-entry.d.ts +2 -0
- package/dist/server/app-ssr-entry.js +56 -55
- package/dist/server/app-ssr-entry.js.map +1 -1
- package/dist/server/app-ssr-stream.d.ts +6 -1
- package/dist/server/app-ssr-stream.js +17 -3
- package/dist/server/app-ssr-stream.js.map +1 -1
- package/dist/server/artifact-compatibility.d.ts +1 -1
- package/dist/server/artifact-compatibility.js.map +1 -1
- package/dist/server/cache-headers.d.ts +7 -0
- package/dist/server/cache-headers.js +19 -0
- package/dist/server/cache-headers.js.map +1 -0
- package/dist/server/cache-proof.d.ts +49 -3
- package/dist/server/cache-proof.js +78 -22
- package/dist/server/cache-proof.js.map +1 -1
- package/dist/server/client-reuse-manifest.d.ts +99 -0
- package/dist/server/client-reuse-manifest.js +212 -0
- package/dist/server/client-reuse-manifest.js.map +1 -0
- package/dist/server/default-global-error-module.d.ts +20 -0
- package/dist/server/default-global-error-module.js +20 -0
- package/dist/server/default-global-error-module.js.map +1 -0
- package/dist/server/dev-server.d.ts +9 -1
- package/dist/server/dev-server.js +76 -29
- package/dist/server/dev-server.js.map +1 -1
- package/dist/server/edge-api-runtime.d.ts +5 -0
- package/dist/server/edge-api-runtime.js +8 -0
- package/dist/server/edge-api-runtime.js.map +1 -0
- package/dist/server/headers.d.ts +18 -1
- package/dist/server/headers.js +18 -1
- package/dist/server/headers.js.map +1 -1
- package/dist/server/http-error-responses.d.ts +16 -1
- package/dist/server/http-error-responses.js +21 -1
- package/dist/server/http-error-responses.js.map +1 -1
- package/dist/server/isr-cache.d.ts +6 -2
- package/dist/server/isr-cache.js +20 -4
- package/dist/server/isr-cache.js.map +1 -1
- package/dist/server/middleware-runtime.d.ts +15 -0
- package/dist/server/middleware-runtime.js +59 -7
- package/dist/server/middleware-runtime.js.map +1 -1
- package/dist/server/middleware.d.ts +1 -1
- package/dist/server/middleware.js +4 -2
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/navigation-planner.d.ts +9 -3
- package/dist/server/navigation-planner.js +98 -25
- package/dist/server/navigation-planner.js.map +1 -1
- package/dist/server/navigation-trace.d.ts +2 -1
- package/dist/server/navigation-trace.js +1 -0
- package/dist/server/navigation-trace.js.map +1 -1
- package/dist/server/pages-api-route.d.ts +27 -1
- package/dist/server/pages-api-route.js +24 -3
- package/dist/server/pages-api-route.js.map +1 -1
- package/dist/server/pages-data-route.d.ts +77 -0
- package/dist/server/pages-data-route.js +97 -0
- package/dist/server/pages-data-route.js.map +1 -0
- package/dist/server/pages-i18n.d.ts +51 -1
- package/dist/server/pages-i18n.js +61 -1
- package/dist/server/pages-i18n.js.map +1 -1
- package/dist/server/pages-page-data.d.ts +29 -2
- package/dist/server/pages-page-data.js +31 -17
- package/dist/server/pages-page-data.js.map +1 -1
- package/dist/server/pages-page-response.d.ts +11 -1
- package/dist/server/pages-page-response.js +5 -3
- package/dist/server/pages-page-response.js.map +1 -1
- package/dist/server/prod-server.d.ts +13 -15
- package/dist/server/prod-server.js +109 -56
- package/dist/server/prod-server.js.map +1 -1
- package/dist/server/request-pipeline.d.ts +11 -2
- package/dist/server/request-pipeline.js +28 -11
- package/dist/server/request-pipeline.js.map +1 -1
- package/dist/server/seed-cache.d.ts +12 -31
- package/dist/server/seed-cache.js +22 -35
- package/dist/server/seed-cache.js.map +1 -1
- package/dist/server/server-action-not-found.js +8 -3
- package/dist/server/server-action-not-found.js.map +1 -1
- package/dist/server/skip-cache-proof.d.ts +41 -0
- package/dist/server/skip-cache-proof.js +101 -0
- package/dist/server/skip-cache-proof.js.map +1 -0
- package/dist/server/static-file-cache.d.ts +1 -1
- package/dist/server/static-file-cache.js +7 -6
- package/dist/server/static-file-cache.js.map +1 -1
- package/dist/shims/client-locale.d.ts +15 -0
- package/dist/shims/client-locale.js +13 -0
- package/dist/shims/client-locale.js.map +1 -0
- package/dist/shims/default-global-error.d.ts +32 -0
- package/dist/shims/default-global-error.js +181 -0
- package/dist/shims/default-global-error.js.map +1 -0
- package/dist/shims/document.d.ts +59 -3
- package/dist/shims/document.js +36 -5
- package/dist/shims/document.js.map +1 -1
- package/dist/shims/error-boundary.d.ts +2 -2
- package/dist/shims/form.js +13 -6
- package/dist/shims/form.js.map +1 -1
- package/dist/shims/link.d.ts +21 -3
- package/dist/shims/link.js +131 -22
- package/dist/shims/link.js.map +1 -1
- package/dist/shims/metadata.js +4 -4
- package/dist/shims/metadata.js.map +1 -1
- package/dist/shims/navigation.d.ts +8 -2
- package/dist/shims/navigation.js +36 -15
- package/dist/shims/navigation.js.map +1 -1
- package/dist/shims/og.d.ts +18 -2
- package/dist/shims/og.js +49 -1
- package/dist/shims/og.js.map +1 -0
- package/dist/shims/request-state-types.d.ts +1 -1
- package/dist/shims/root-params.d.ts +3 -1
- package/dist/shims/root-params.js +11 -3
- package/dist/shims/root-params.js.map +1 -1
- package/dist/shims/router-state.d.ts +1 -0
- package/dist/shims/router-state.js.map +1 -1
- package/dist/shims/router.d.ts +12 -5
- package/dist/shims/router.js +172 -22
- package/dist/shims/router.js.map +1 -1
- package/dist/shims/server.d.ts +21 -4
- package/dist/shims/server.js +29 -9
- package/dist/shims/server.js.map +1 -1
- package/dist/shims/slot.js +5 -1
- package/dist/shims/slot.js.map +1 -1
- package/dist/shims/unified-request-context.d.ts +1 -1
- package/dist/shims/url-safety.d.ts +23 -1
- package/dist/shims/url-safety.js +29 -2
- package/dist/shims/url-safety.js.map +1 -1
- package/dist/typegen.d.ts +10 -0
- package/dist/typegen.js +242 -0
- package/dist/typegen.js.map +1 -0
- package/dist/utils/asset-prefix.d.ts +33 -5
- package/dist/utils/asset-prefix.js +39 -6
- package/dist/utils/asset-prefix.js.map +1 -1
- package/dist/utils/cache-control-metadata.d.ts +2 -1
- package/dist/utils/cache-control-metadata.js +1 -3
- package/dist/utils/cache-control-metadata.js.map +1 -1
- package/dist/utils/domain-locale.d.ts +2 -1
- package/dist/utils/domain-locale.js +9 -1
- package/dist/utils/domain-locale.js.map +1 -1
- package/dist/utils/lazy-chunks.d.ts +1 -1
- package/dist/utils/lazy-chunks.js +1 -1
- package/dist/utils/lazy-chunks.js.map +1 -1
- package/dist/utils/prerender-output-paths.d.ts +15 -0
- package/dist/utils/prerender-output-paths.js +24 -0
- package/dist/utils/prerender-output-paths.js.map +1 -0
- package/dist/utils/query.d.ts +17 -1
- package/dist/utils/query.js +36 -1
- package/dist/utils/query.js.map +1 -1
- package/dist/utils/record.d.ts +5 -0
- package/dist/utils/record.js +8 -0
- package/dist/utils/record.js.map +1 -0
- package/package.json +11 -3
package/dist/shims/document.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
import "react";
|
|
1
|
+
import React from "react";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
//#region src/shims/document.tsx
|
|
4
|
+
/**
|
|
5
|
+
* next/document shim
|
|
6
|
+
*
|
|
7
|
+
* Provides Html, Head, Main, NextScript components for custom _document.tsx.
|
|
8
|
+
* During SSR these render placeholder markers that the dev server replaces
|
|
9
|
+
* with actual content.
|
|
10
|
+
*/
|
|
4
11
|
function Html({ children, lang, ...props }) {
|
|
5
12
|
return /* @__PURE__ */ jsx("html", {
|
|
6
13
|
lang,
|
|
@@ -40,11 +47,35 @@ function NextScript() {
|
|
|
40
47
|
return /* @__PURE__ */ jsx("span", { dangerouslySetInnerHTML: { __html: "<!-- __NEXT_SCRIPTS__ -->" } });
|
|
41
48
|
}
|
|
42
49
|
/**
|
|
43
|
-
* Default Document component
|
|
50
|
+
* Default Document component — also the base class user `_document.tsx` files
|
|
51
|
+
* `extend`. Must be a class (not a function) to match Next.js's `next/document`
|
|
52
|
+
* default export so `class MyDocument extends Document` produces a constructible
|
|
53
|
+
* class that React can instantiate during SSR. Returning a function here breaks
|
|
54
|
+
* any user `_document.tsx` that uses the class-based form because `extends`
|
|
55
|
+
* against a non-constructor produces a class that can only be called without
|
|
56
|
+
* `new`, which React refuses to do.
|
|
57
|
+
*
|
|
58
|
+
* @see https://github.com/vercel/next.js/blob/canary/packages/next/src/pages/_document.tsx
|
|
59
|
+
* Ported behavior: Next.js's default `Document` is a `class Document extends
|
|
60
|
+
* React.Component`. Custom documents extend it and override `getInitialProps`
|
|
61
|
+
* and `render`. Generic default matches Next.js (`P = {}`).
|
|
44
62
|
*/
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
63
|
+
var Document = class extends React.Component {
|
|
64
|
+
/**
|
|
65
|
+
* `getInitialProps` is invoked by the SSR pipeline. The default implementation
|
|
66
|
+
* is a stub: vinext does not yet plumb the Pages Router `renderPage` /
|
|
67
|
+
* `defaultGetInitialProps` chain into the SSR entry, so subclasses that
|
|
68
|
+
* delegate via `await Document.getInitialProps(ctx)` receive an empty shell
|
|
69
|
+
* (`html: ""`). This matches the runtime contract user code expects without
|
|
70
|
+
* pretending the chain is wired up.
|
|
71
|
+
*/
|
|
72
|
+
static async getInitialProps(_ctx) {
|
|
73
|
+
return { html: "" };
|
|
74
|
+
}
|
|
75
|
+
render() {
|
|
76
|
+
return /* @__PURE__ */ jsxs(Html, { children: [/* @__PURE__ */ jsx(Head, {}), /* @__PURE__ */ jsxs("body", { children: [/* @__PURE__ */ jsx(Main, {}), /* @__PURE__ */ jsx(NextScript, {})] })] });
|
|
77
|
+
}
|
|
78
|
+
};
|
|
48
79
|
//#endregion
|
|
49
80
|
export { Head, Html, Main, NextScript, Document as default };
|
|
50
81
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"document.js","names":[],"sources":["../../src/shims/document.tsx"],"sourcesContent":["/**\n * next/document shim\n *\n * Provides Html, Head, Main, NextScript components for custom _document.tsx.\n * During SSR these render placeholder markers that the dev server replaces\n * with actual content.\n */\nimport React from \"react\";\n\nexport function Html({\n children,\n lang,\n ...props\n}: React.HTMLAttributes<HTMLHtmlElement> & { children?: React.ReactNode }) {\n return (\n <html lang={lang} {...props}>\n {children}\n </html>\n );\n}\n\n/**\n * Document Head - renders <head> with children.\n * The dev server injects meta tags, styles, etc.\n */\nexport function Head({ children }: { children?: React.ReactNode }) {\n return (\n <head>\n <meta charSet=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n {children}\n </head>\n );\n}\n\n/**\n * Main - renders the page content container.\n */\nexport function Main() {\n return <div id=\"__next\" dangerouslySetInnerHTML={{ __html: \"__NEXT_MAIN__\" }} />;\n}\n\n/**\n * NextScript - renders a placeholder that the dev-server replaces with\n * actual hydration scripts (__NEXT_DATA__ + entry module).\n * Uses dangerouslySetInnerHTML so the HTML comment survives renderToString.\n */\nexport function NextScript() {\n return <span dangerouslySetInnerHTML={{ __html: \"<!-- __NEXT_SCRIPTS__ -->\" }} />;\n}\n\n/**\n * Default Document component -
|
|
1
|
+
{"version":3,"file":"document.js","names":[],"sources":["../../src/shims/document.tsx"],"sourcesContent":["/**\n * next/document shim\n *\n * Provides Html, Head, Main, NextScript components for custom _document.tsx.\n * During SSR these render placeholder markers that the dev server replaces\n * with actual content.\n */\nimport React from \"react\";\n\nexport function Html({\n children,\n lang,\n ...props\n}: React.HTMLAttributes<HTMLHtmlElement> & { children?: React.ReactNode }) {\n return (\n <html lang={lang} {...props}>\n {children}\n </html>\n );\n}\n\n/**\n * Document Head - renders <head> with children.\n * The dev server injects meta tags, styles, etc.\n */\nexport function Head({ children }: { children?: React.ReactNode }) {\n return (\n <head>\n <meta charSet=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n {children}\n </head>\n );\n}\n\n/**\n * Main - renders the page content container.\n */\nexport function Main() {\n return <div id=\"__next\" dangerouslySetInnerHTML={{ __html: \"__NEXT_MAIN__\" }} />;\n}\n\n/**\n * NextScript - renders a placeholder that the dev-server replaces with\n * actual hydration scripts (__NEXT_DATA__ + entry module).\n * Uses dangerouslySetInnerHTML so the HTML comment survives renderToString.\n */\nexport function NextScript() {\n return <span dangerouslySetInnerHTML={{ __html: \"<!-- __NEXT_SCRIPTS__ -->\" }} />;\n}\n\n/**\n * Loose stand-ins for Next.js's `DocumentContext` / `DocumentInitialProps`.\n * The shim doesn't currently invoke `getInitialProps` on user `_document.tsx`\n * files (separate gap), but the signatures here match Next.js's so subclasses\n * that delegate via `await Document.getInitialProps(ctx)` typecheck against\n * the same shape they'd see under real Next.js.\n *\n * @see https://github.com/vercel/next.js/blob/canary/packages/next/src/shared/lib/utils.ts\n */\nexport type DocumentContext = {\n // The full `DocumentContext` includes `renderPage`, `defaultGetInitialProps`,\n // and the inherited `NextPageContext` (`pathname`, `query`, `req`, `res`,\n // `err`, `asPath`, ...). They're declared as optional here because vinext\n // does not yet plumb them through; widening to optional avoids forcing user\n // code to assert their presence.\n renderPage?: (options?: {\n enhanceApp?: (App: React.ComponentType<{ children?: React.ReactNode }>) => unknown;\n enhanceComponent?: (Comp: React.ComponentType<unknown>) => unknown;\n }) => { html: string; head?: ReadonlyArray<React.ReactElement> };\n defaultGetInitialProps?: (\n ctx: DocumentContext,\n options?: { nonce?: string },\n ) => Promise<DocumentInitialProps>;\n pathname?: string;\n query?: Record<string, string | string[] | undefined>;\n asPath?: string;\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n err?: any;\n};\n\nexport type DocumentInitialProps = {\n html: string;\n head?: ReadonlyArray<React.ReactElement>;\n styles?: React.ReactElement[] | Iterable<React.ReactNode> | React.ReactElement;\n};\n\n/**\n * Default Document component — also the base class user `_document.tsx` files\n * `extend`. Must be a class (not a function) to match Next.js's `next/document`\n * default export so `class MyDocument extends Document` produces a constructible\n * class that React can instantiate during SSR. Returning a function here breaks\n * any user `_document.tsx` that uses the class-based form because `extends`\n * against a non-constructor produces a class that can only be called without\n * `new`, which React refuses to do.\n *\n * @see https://github.com/vercel/next.js/blob/canary/packages/next/src/pages/_document.tsx\n * Ported behavior: Next.js's default `Document` is a `class Document extends\n * React.Component`. Custom documents extend it and override `getInitialProps`\n * and `render`. Generic default matches Next.js (`P = {}`).\n */\n// oxlint-disable-next-line @typescript-eslint/no-empty-object-type\nexport default class Document<P = {}> extends React.Component<P & { children?: React.ReactNode }> {\n /**\n * `getInitialProps` is invoked by the SSR pipeline. The default implementation\n * is a stub: vinext does not yet plumb the Pages Router `renderPage` /\n * `defaultGetInitialProps` chain into the SSR entry, so subclasses that\n * delegate via `await Document.getInitialProps(ctx)` receive an empty shell\n * (`html: \"\"`). This matches the runtime contract user code expects without\n * pretending the chain is wired up.\n */\n static async getInitialProps(_ctx: DocumentContext): Promise<DocumentInitialProps> {\n return { html: \"\" };\n }\n\n render(): React.ReactNode {\n return (\n <Html>\n <Head />\n <body>\n <Main />\n <NextScript />\n </body>\n </Html>\n );\n }\n}\n"],"mappings":";;;;;;;;;;AASA,SAAgB,KAAK,EACnB,UACA,MACA,GAAG,SACsE;CACzE,OACE,oBAAC,QAAD;EAAY;EAAM,GAAI;EACnB;EACI,CAAA;;;;;;AAQX,SAAgB,KAAK,EAAE,YAA4C;CACjE,OACE,qBAAC,QAAD,EAAA,UAAA;EACE,oBAAC,QAAD,EAAM,SAAQ,SAAU,CAAA;EACxB,oBAAC,QAAD;GAAM,MAAK;GAAW,SAAQ;GAAwC,CAAA;EACrE;EACI,EAAA,CAAA;;;;;AAOX,SAAgB,OAAO;CACrB,OAAO,oBAAC,OAAD;EAAK,IAAG;EAAS,yBAAyB,EAAE,QAAQ,iBAAiB;EAAI,CAAA;;;;;;;AAQlF,SAAgB,aAAa;CAC3B,OAAO,oBAAC,QAAD,EAAM,yBAAyB,EAAE,QAAQ,6BAA6B,EAAI,CAAA;;;;;;;;;;;;;;;;AAsDnF,IAAqB,WAArB,cAA8C,MAAM,UAA8C;;;;;;;;;CAShG,aAAa,gBAAgB,MAAsD;EACjF,OAAO,EAAE,MAAM,IAAI;;CAGrB,SAA0B;EACxB,OACE,qBAAC,MAAD,EAAA,UAAA,CACE,oBAAC,MAAD,EAAQ,CAAA,EACR,qBAAC,QAAD,EAAA,UAAA,CACE,oBAAC,MAAD,EAAQ,CAAA,EACR,oBAAC,YAAD,EAAc,CAAA,CACT,EAAA,CAAA,CACF,EAAA,CAAA"}
|
|
@@ -32,7 +32,7 @@ declare class RedirectErrorBoundary extends React.Component<{
|
|
|
32
32
|
children?: React.ReactNode;
|
|
33
33
|
});
|
|
34
34
|
static getDerivedStateFromError(error: unknown): RedirectBoundaryState;
|
|
35
|
-
render(): string | number | bigint | boolean |
|
|
35
|
+
render(): string | number | bigint | boolean | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | _$react_jsx_runtime0.JSX.Element | null | undefined;
|
|
36
36
|
}
|
|
37
37
|
declare function RedirectBoundary({
|
|
38
38
|
children
|
|
@@ -49,7 +49,7 @@ declare class ErrorBoundaryInner extends React.Component<ErrorBoundaryInnerProps
|
|
|
49
49
|
static getDerivedStateFromProps(props: ErrorBoundaryInnerProps, state: ErrorBoundaryState): ErrorBoundaryState | null;
|
|
50
50
|
static getDerivedStateFromError(error: unknown): Partial<ErrorBoundaryState>;
|
|
51
51
|
reset: () => void;
|
|
52
|
-
render(): string | number | bigint | boolean |
|
|
52
|
+
render(): string | number | bigint | boolean | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | _$react_jsx_runtime0.JSX.Element | null | undefined;
|
|
53
53
|
}
|
|
54
54
|
declare function ErrorBoundary({
|
|
55
55
|
fallback,
|
package/dist/shims/form.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { isDangerousScheme } from "./url-safety.js";
|
|
3
|
-
import { toSameOriginPath } from "./url-utils.js";
|
|
3
|
+
import { toSameOriginPath, withBasePath } from "./url-utils.js";
|
|
4
|
+
import { hasAppNavigationRuntime } from "../client/navigation-runtime.js";
|
|
4
5
|
import { navigateClientSide } from "./navigation.js";
|
|
5
6
|
import { forwardRef, useActionState } from "react";
|
|
6
7
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -22,6 +23,7 @@ import { jsx } from "react/jsx-runtime";
|
|
|
22
23
|
* <button type="submit">Search</button>
|
|
23
24
|
* </Form>
|
|
24
25
|
*/
|
|
26
|
+
const __basePath = process.env.__NEXT_ROUTER_BASEPATH ?? "";
|
|
25
27
|
const SUPPORTED_FORM_ENCTYPE = "application/x-www-form-urlencoded";
|
|
26
28
|
const SUPPORTED_FORM_METHOD = "GET";
|
|
27
29
|
const SUPPORTED_FORM_TARGET = "_self";
|
|
@@ -112,6 +114,7 @@ const Form = forwardRef(function Form(props, ref) {
|
|
|
112
114
|
...rest
|
|
113
115
|
});
|
|
114
116
|
}
|
|
117
|
+
const actionHref = withBasePath(action, __basePath);
|
|
115
118
|
async function handleSubmit(e) {
|
|
116
119
|
if (onSubmit) {
|
|
117
120
|
onSubmit(e);
|
|
@@ -120,7 +123,7 @@ const Form = forwardRef(function Form(props, ref) {
|
|
|
120
123
|
const submitter = getSubmitter(e.nativeEvent);
|
|
121
124
|
if (submitter && hasUnsupportedSubmitterAttributes(submitter)) return;
|
|
122
125
|
if (getEffectiveMethod(submitter, rest.method) !== "GET") return;
|
|
123
|
-
const effectiveAction = getEffectiveAction(submitter,
|
|
126
|
+
const effectiveAction = getEffectiveAction(submitter, actionHref);
|
|
124
127
|
if (process.env.NODE_ENV !== "production" && submitter?.getAttribute("formaction") !== null) checkFormActionUrl(effectiveAction, "formAction");
|
|
125
128
|
if (!isSafeAction(effectiveAction)) {
|
|
126
129
|
if (process.env.NODE_ENV !== "production") console.warn(`<Form> blocked unsafe action: ${effectiveAction}`);
|
|
@@ -129,17 +132,21 @@ const Form = forwardRef(function Form(props, ref) {
|
|
|
129
132
|
}
|
|
130
133
|
e.preventDefault();
|
|
131
134
|
const url = createFormSubmitDestinationUrl(effectiveAction, e.currentTarget, submitter);
|
|
132
|
-
if (
|
|
133
|
-
else {
|
|
135
|
+
if (hasAppNavigationRuntime()) await navigateClientSide(url, replace ? "replace" : "push", scroll);
|
|
136
|
+
else try {
|
|
137
|
+
const Router = (await import("./router.js")).default;
|
|
138
|
+
if (replace) await Router.replace(url, void 0, { scroll });
|
|
139
|
+
else await Router.push(url, void 0, { scroll });
|
|
140
|
+
} catch {
|
|
134
141
|
if (replace) window.history.replaceState({}, "", url);
|
|
135
142
|
else window.history.pushState({}, "", url);
|
|
136
143
|
window.dispatchEvent(new PopStateEvent("popstate"));
|
|
144
|
+
if (scroll) window.scrollTo(0, 0);
|
|
137
145
|
}
|
|
138
|
-
if (typeof window.__VINEXT_RSC_NAVIGATE__ !== "function" && scroll) window.scrollTo(0, 0);
|
|
139
146
|
}
|
|
140
147
|
return /* @__PURE__ */ jsx("form", {
|
|
141
148
|
ref,
|
|
142
|
-
action,
|
|
149
|
+
action: actionHref,
|
|
143
150
|
onSubmit: (event) => {
|
|
144
151
|
handleSubmit(event);
|
|
145
152
|
},
|
package/dist/shims/form.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form.js","names":[],"sources":["../../src/shims/form.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * next/form shim\n *\n * Progressive enhancement form component. In Next.js, this replaces\n * the standard <form> element with one that intercepts submissions\n * and performs client-side navigation for GET forms (search forms).\n *\n * For POST forms with server actions, it delegates to React's built-in\n * form action handling.\n *\n * Usage:\n * import Form from 'next/form';\n * <Form action=\"/search\">\n * <input name=\"q\" />\n * <button type=\"submit\">Search</button>\n * </Form>\n */\n\nimport { forwardRef, useActionState, type FormHTMLAttributes, type ForwardedRef } from \"react\";\nimport { navigateClientSide } from \"./navigation.js\";\nimport { isDangerousScheme } from \"./url-safety.js\";\nimport { toSameOriginPath } from \"./url-utils.js\";\n\n// Re-export useActionState from React 19 to match Next.js's next/form module\nexport { useActionState };\n\ntype FormSubmitter = HTMLButtonElement | HTMLInputElement;\nconst SUPPORTED_FORM_ENCTYPE = \"application/x-www-form-urlencoded\";\nconst SUPPORTED_FORM_METHOD = \"GET\";\nconst SUPPORTED_FORM_TARGET = \"_self\";\n\nfunction isSafeAction(action: string): boolean {\n // Block dangerous URI schemes\n if (isDangerousScheme(action)) return false;\n // Block protocol-relative URLs (//evil.com/...)\n if (action.startsWith(\"//\")) return false;\n // Block absolute URLs to external origins (client-side: compare origins)\n if (/^https?:\\/\\//i.test(action)) {\n if (typeof window !== \"undefined\") {\n try {\n const actionUrl = new URL(action);\n return actionUrl.origin === window.location.origin;\n } catch {\n return false;\n }\n }\n // Server-side: block all absolute URLs (can't compare origins)\n return false;\n }\n return true;\n}\n\nfunction getSubmitter(nativeEvent: unknown): FormSubmitter | null {\n const submitter =\n nativeEvent &&\n typeof nativeEvent === \"object\" &&\n \"submitter\" in nativeEvent &&\n nativeEvent.submitter instanceof Element\n ? nativeEvent.submitter\n : null;\n\n if (submitter instanceof HTMLButtonElement || submitter instanceof HTMLInputElement) {\n return submitter;\n }\n return null;\n}\n\nfunction getEffectiveMethod(\n submitter: FormSubmitter | null,\n formMethod: FormHTMLAttributes<HTMLFormElement>[\"method\"],\n): string {\n const override = submitter?.getAttribute(\"formmethod\");\n return (override ?? formMethod ?? \"GET\").toUpperCase();\n}\n\nfunction getEffectiveAction(submitter: FormSubmitter | null, formAction: string): string {\n return submitter?.getAttribute(\"formaction\") ?? formAction;\n}\n\nfunction checkFormActionUrl(action: string, source: \"action\" | \"formAction\"): void {\n const aPropName = source === \"action\" ? \"an `action`\" : \"a `formAction`\";\n\n let testUrl: URL;\n try {\n testUrl = new URL(action, \"http://n\");\n } catch {\n console.error(`<Form> received ${aPropName} that cannot be parsed as a URL: \"${action}\".`);\n return;\n }\n\n if (testUrl.searchParams.size) {\n console.warn(\n `<Form> received ${aPropName} that contains search params: \"${action}\". This is not supported, and they will be ignored. ` +\n `If you need to pass in additional search params, use an \\`<input type=\"hidden\" />\\` instead.`,\n );\n }\n}\n\nfunction hasUnsupportedSubmitterAttributes(submitter: FormSubmitter): boolean {\n const formEncType = submitter.getAttribute(\"formenctype\");\n if (formEncType !== null && formEncType !== SUPPORTED_FORM_ENCTYPE) {\n console.error(\n `<Form>'s \\`encType\\` was set to an unsupported value via \\`formEncType=\"${formEncType}\"\\`. ` +\n `This will disable <Form>'s navigation functionality. If you need this, use a native <form> element instead.`,\n );\n return true;\n }\n\n const formMethod = submitter.getAttribute(\"formmethod\");\n if (formMethod !== null && formMethod.toUpperCase() !== SUPPORTED_FORM_METHOD) {\n console.error(\n `<Form>'s \\`method\\` was set to an unsupported value via \\`formMethod=\"${formMethod}\"\\`. ` +\n `This will disable <Form>'s navigation functionality. If you need this, use a native <form> element instead.`,\n );\n return true;\n }\n\n const formTarget = submitter.getAttribute(\"formtarget\");\n if (formTarget !== null && formTarget !== SUPPORTED_FORM_TARGET) {\n console.error(\n `<Form>'s \\`target\\` was set to an unsupported value via \\`formTarget=\"${formTarget}\"\\`. ` +\n `This will disable <Form>'s navigation functionality. If you need this, use a native <form> element instead.`,\n );\n return true;\n }\n\n return false;\n}\n\nfunction createFormSubmitDestinationUrl(\n action: string,\n form: HTMLFormElement,\n submitter: FormSubmitter | null,\n): string {\n const targetUrl = new URL(action, window.location.href);\n if (targetUrl.searchParams.size) {\n targetUrl.search = \"\";\n }\n\n const formData = buildFormData(form, submitter);\n for (const [name, value] of formData) {\n targetUrl.searchParams.append(name, typeof value === \"string\" ? value : value.name);\n }\n\n return toSameOriginPath(targetUrl.href) ?? targetUrl.href;\n}\n\nfunction buildFormData(form: HTMLFormElement, submitter: FormSubmitter | null): FormData {\n if (!submitter) return new FormData(form);\n\n try {\n return new FormData(form, submitter);\n } catch {\n const formData = new FormData(form);\n if (!submitter.disabled && submitter.name) {\n formData.append(submitter.name, submitter.value);\n }\n return formData;\n }\n}\n\ntype FormProps = {\n /** Target URL for GET forms, or server action for POST forms */\n action: string | ((formData: FormData) => void | Promise<void>);\n /** Replace instead of push in history (default: false) */\n replace?: boolean;\n /** Scroll to top after navigation (default: true) */\n scroll?: boolean;\n} & FormHTMLAttributes<HTMLFormElement>;\n\nconst Form = forwardRef(function Form(props: FormProps, ref: ForwardedRef<HTMLFormElement>) {\n const { action, replace = false, scroll = true, onSubmit, ...rest } = props;\n\n // If action is a function (server action), pass it directly to React\n if (typeof action === \"function\") {\n return <form ref={ref} action={action} onSubmit={onSubmit} {...rest} />;\n }\n\n // Block dangerous action URLs. Render <form> without action attribute\n // so it submits to the current page (safe default).\n if (process.env.NODE_ENV !== \"production\") {\n checkFormActionUrl(action, \"action\");\n }\n\n if (!isSafeAction(action)) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(`<Form> blocked unsafe action: ${action}`);\n }\n return <form ref={ref} onSubmit={onSubmit} {...rest} />;\n }\n\n async function handleSubmit(e: React.SubmitEvent<HTMLFormElement>) {\n // Call user's onSubmit first\n if (onSubmit) {\n onSubmit(e);\n if (e.defaultPrevented) return;\n }\n\n const submitter = getSubmitter(e.nativeEvent);\n if (submitter && hasUnsupportedSubmitterAttributes(submitter)) {\n return;\n }\n\n // Only intercept GET forms for client-side navigation\n const method = getEffectiveMethod(submitter, rest.method);\n if (method !== \"GET\") return;\n\n const effectiveAction = getEffectiveAction(submitter, action as string);\n if (process.env.NODE_ENV !== \"production\" && submitter?.getAttribute(\"formaction\") !== null) {\n checkFormActionUrl(effectiveAction, \"formAction\");\n }\n if (!isSafeAction(effectiveAction)) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(`<Form> blocked unsafe action: ${effectiveAction}`);\n }\n e.preventDefault();\n return;\n }\n\n e.preventDefault();\n const url = createFormSubmitDestinationUrl(effectiveAction, e.currentTarget, submitter);\n\n // Navigate client-side\n if (typeof window.__VINEXT_RSC_NAVIGATE__ === \"function\") {\n // App Router: use the shared navigator so URL/history publish stays\n // aligned with the committed RSC tree.\n await navigateClientSide(url, replace ? \"replace\" : \"push\", scroll);\n } else {\n // Pages Router: use router or fallback\n if (replace) {\n window.history.replaceState({}, \"\", url);\n } else {\n window.history.pushState({}, \"\", url);\n }\n window.dispatchEvent(new PopStateEvent(\"popstate\"));\n }\n\n // App Router: scroll is handled inside navigateClientSide (called above).\n // Pages Router: scroll manually since pushState/popstate doesn't auto-scroll.\n if (typeof window.__VINEXT_RSC_NAVIGATE__ !== \"function\" && scroll) {\n window.scrollTo(0, 0);\n }\n }\n\n return (\n <form\n ref={ref}\n action={action}\n onSubmit={(event) => {\n void handleSubmit(event);\n }}\n {...rest}\n />\n );\n});\n\nexport default Form;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"form.js","names":[],"sources":["../../src/shims/form.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * next/form shim\n *\n * Progressive enhancement form component. In Next.js, this replaces\n * the standard <form> element with one that intercepts submissions\n * and performs client-side navigation for GET forms (search forms).\n *\n * For POST forms with server actions, it delegates to React's built-in\n * form action handling.\n *\n * Usage:\n * import Form from 'next/form';\n * <Form action=\"/search\">\n * <input name=\"q\" />\n * <button type=\"submit\">Search</button>\n * </Form>\n */\n\nimport { forwardRef, useActionState, type FormHTMLAttributes, type ForwardedRef } from \"react\";\nimport { hasAppNavigationRuntime } from \"../client/navigation-runtime.js\";\nimport { navigateClientSide } from \"./navigation.js\";\nimport { isDangerousScheme } from \"./url-safety.js\";\nimport { toSameOriginPath, withBasePath } from \"./url-utils.js\";\n\n// Mirrors `__NEXT_ROUTER_BASEPATH` exposure in `next/link` / `next/router`.\n// `addBasePath` is only applied to the form-level `action` prop. A submitter's\n// `formAction` is intentionally untouched, matching Next.js (the comment in\n// upstream `form.tsx` notes \"this should not have basePath added, because we\n// can't add it before hydration\").\nconst __basePath: string = process.env.__NEXT_ROUTER_BASEPATH ?? \"\";\n\n// Re-export useActionState from React 19 to match Next.js's next/form module\nexport { useActionState };\n\ntype FormSubmitter = HTMLButtonElement | HTMLInputElement;\nconst SUPPORTED_FORM_ENCTYPE = \"application/x-www-form-urlencoded\";\nconst SUPPORTED_FORM_METHOD = \"GET\";\nconst SUPPORTED_FORM_TARGET = \"_self\";\n\nfunction isSafeAction(action: string): boolean {\n // Block dangerous URI schemes\n if (isDangerousScheme(action)) return false;\n // Block protocol-relative URLs (//evil.com/...)\n if (action.startsWith(\"//\")) return false;\n // Block absolute URLs to external origins (client-side: compare origins)\n if (/^https?:\\/\\//i.test(action)) {\n if (typeof window !== \"undefined\") {\n try {\n const actionUrl = new URL(action);\n return actionUrl.origin === window.location.origin;\n } catch {\n return false;\n }\n }\n // Server-side: block all absolute URLs (can't compare origins)\n return false;\n }\n return true;\n}\n\nfunction getSubmitter(nativeEvent: unknown): FormSubmitter | null {\n const submitter =\n nativeEvent &&\n typeof nativeEvent === \"object\" &&\n \"submitter\" in nativeEvent &&\n nativeEvent.submitter instanceof Element\n ? nativeEvent.submitter\n : null;\n\n if (submitter instanceof HTMLButtonElement || submitter instanceof HTMLInputElement) {\n return submitter;\n }\n return null;\n}\n\nfunction getEffectiveMethod(\n submitter: FormSubmitter | null,\n formMethod: FormHTMLAttributes<HTMLFormElement>[\"method\"],\n): string {\n const override = submitter?.getAttribute(\"formmethod\");\n return (override ?? formMethod ?? \"GET\").toUpperCase();\n}\n\nfunction getEffectiveAction(submitter: FormSubmitter | null, formAction: string): string {\n return submitter?.getAttribute(\"formaction\") ?? formAction;\n}\n\nfunction checkFormActionUrl(action: string, source: \"action\" | \"formAction\"): void {\n const aPropName = source === \"action\" ? \"an `action`\" : \"a `formAction`\";\n\n let testUrl: URL;\n try {\n testUrl = new URL(action, \"http://n\");\n } catch {\n console.error(`<Form> received ${aPropName} that cannot be parsed as a URL: \"${action}\".`);\n return;\n }\n\n if (testUrl.searchParams.size) {\n console.warn(\n `<Form> received ${aPropName} that contains search params: \"${action}\". This is not supported, and they will be ignored. ` +\n `If you need to pass in additional search params, use an \\`<input type=\"hidden\" />\\` instead.`,\n );\n }\n}\n\nfunction hasUnsupportedSubmitterAttributes(submitter: FormSubmitter): boolean {\n const formEncType = submitter.getAttribute(\"formenctype\");\n if (formEncType !== null && formEncType !== SUPPORTED_FORM_ENCTYPE) {\n console.error(\n `<Form>'s \\`encType\\` was set to an unsupported value via \\`formEncType=\"${formEncType}\"\\`. ` +\n `This will disable <Form>'s navigation functionality. If you need this, use a native <form> element instead.`,\n );\n return true;\n }\n\n const formMethod = submitter.getAttribute(\"formmethod\");\n if (formMethod !== null && formMethod.toUpperCase() !== SUPPORTED_FORM_METHOD) {\n console.error(\n `<Form>'s \\`method\\` was set to an unsupported value via \\`formMethod=\"${formMethod}\"\\`. ` +\n `This will disable <Form>'s navigation functionality. If you need this, use a native <form> element instead.`,\n );\n return true;\n }\n\n const formTarget = submitter.getAttribute(\"formtarget\");\n if (formTarget !== null && formTarget !== SUPPORTED_FORM_TARGET) {\n console.error(\n `<Form>'s \\`target\\` was set to an unsupported value via \\`formTarget=\"${formTarget}\"\\`. ` +\n `This will disable <Form>'s navigation functionality. If you need this, use a native <form> element instead.`,\n );\n return true;\n }\n\n return false;\n}\n\nfunction createFormSubmitDestinationUrl(\n action: string,\n form: HTMLFormElement,\n submitter: FormSubmitter | null,\n): string {\n const targetUrl = new URL(action, window.location.href);\n if (targetUrl.searchParams.size) {\n targetUrl.search = \"\";\n }\n\n const formData = buildFormData(form, submitter);\n for (const [name, value] of formData) {\n targetUrl.searchParams.append(name, typeof value === \"string\" ? value : value.name);\n }\n\n return toSameOriginPath(targetUrl.href) ?? targetUrl.href;\n}\n\nfunction buildFormData(form: HTMLFormElement, submitter: FormSubmitter | null): FormData {\n if (!submitter) return new FormData(form);\n\n try {\n return new FormData(form, submitter);\n } catch {\n const formData = new FormData(form);\n if (!submitter.disabled && submitter.name) {\n formData.append(submitter.name, submitter.value);\n }\n return formData;\n }\n}\n\ntype FormProps = {\n /** Target URL for GET forms, or server action for POST forms */\n action: string | ((formData: FormData) => void | Promise<void>);\n /** Replace instead of push in history (default: false) */\n replace?: boolean;\n /** Scroll to top after navigation (default: true) */\n scroll?: boolean;\n} & FormHTMLAttributes<HTMLFormElement>;\n\nconst Form = forwardRef(function Form(props: FormProps, ref: ForwardedRef<HTMLFormElement>) {\n const { action, replace = false, scroll = true, onSubmit, ...rest } = props;\n\n // If action is a function (server action), pass it directly to React\n if (typeof action === \"function\") {\n return <form ref={ref} action={action} onSubmit={onSubmit} {...rest} />;\n }\n\n // Block dangerous action URLs. Render <form> without action attribute\n // so it submits to the current page (safe default).\n if (process.env.NODE_ENV !== \"production\") {\n checkFormActionUrl(action, \"action\");\n }\n\n if (!isSafeAction(action)) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(`<Form> blocked unsafe action: ${action}`);\n }\n return <form ref={ref} onSubmit={onSubmit} {...rest} />;\n }\n\n // Prefix basePath to the navigating `action` prop (matches Next.js's\n // `addBasePath(actionProp)` in `client/form.tsx` and `client/app-dir/form.tsx`).\n // This becomes both the rendered `action=` attribute (so JS-disabled\n // submissions still hit the right URL) and the soft-navigation target.\n const actionHref = withBasePath(action, __basePath);\n\n async function handleSubmit(e: React.SubmitEvent<HTMLFormElement>) {\n // Call user's onSubmit first\n if (onSubmit) {\n onSubmit(e);\n if (e.defaultPrevented) return;\n }\n\n const submitter = getSubmitter(e.nativeEvent);\n if (submitter && hasUnsupportedSubmitterAttributes(submitter)) {\n return;\n }\n\n // Only intercept GET forms for client-side navigation\n const method = getEffectiveMethod(submitter, rest.method);\n if (method !== \"GET\") return;\n\n // NOTE: a submitter's `formAction` is intentionally NOT base-path-prefixed\n // here, matching Next.js. Upstream `form.tsx` notes: \"this should not have\n // `basePath` added, because we can't add it before hydration\".\n const effectiveAction = getEffectiveAction(submitter, actionHref);\n if (process.env.NODE_ENV !== \"production\" && submitter?.getAttribute(\"formaction\") !== null) {\n checkFormActionUrl(effectiveAction, \"formAction\");\n }\n if (!isSafeAction(effectiveAction)) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(`<Form> blocked unsafe action: ${effectiveAction}`);\n }\n e.preventDefault();\n return;\n }\n\n e.preventDefault();\n const url = createFormSubmitDestinationUrl(effectiveAction, e.currentTarget, submitter);\n\n // Navigate client-side\n if (hasAppNavigationRuntime()) {\n // App Router: use the shared navigator so URL/history publish stays\n // aligned with the committed RSC tree.\n await navigateClientSide(url, replace ? \"replace\" : \"push\", scroll);\n } else {\n // Pages Router: delegate to the Router singleton so navigation flows\n // through `performNavigation` (route events, HTML fetch, scroll\n // handling). Mirrors what `<Link>` does at link.tsx:619-623.\n try {\n const routerModule = await import(\"./router.js\");\n const Router = routerModule.default;\n if (replace) {\n await Router.replace(url, undefined, { scroll });\n } else {\n await Router.push(url, undefined, { scroll });\n }\n } catch {\n // Fallback: pushState + popstate keeps the URL in sync even if the\n // Router singleton import fails (e.g. in test/SSR-only contexts).\n if (replace) {\n window.history.replaceState({}, \"\", url);\n } else {\n window.history.pushState({}, \"\", url);\n }\n window.dispatchEvent(new PopStateEvent(\"popstate\"));\n if (scroll) {\n window.scrollTo(0, 0);\n }\n }\n }\n }\n\n return (\n <form\n ref={ref}\n action={actionHref}\n onSubmit={(event) => {\n void handleSubmit(event);\n }}\n {...rest}\n />\n );\n});\n\nexport default Form;\n"],"mappings":"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"}
|
package/dist/shims/link.d.ts
CHANGED
|
@@ -22,7 +22,14 @@ type LinkProps = {
|
|
|
22
22
|
*/
|
|
23
23
|
unstable_dynamicOnHover?: boolean; /** Whether to pass the href to the child element */
|
|
24
24
|
passHref?: boolean; /** Scroll to top on navigation (default: true) */
|
|
25
|
-
scroll?: boolean;
|
|
25
|
+
scroll?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Pages Router: update the URL without re-running data fetching methods
|
|
28
|
+
* (getServerSideProps / getStaticProps / getInitialProps). The shallow change
|
|
29
|
+
* still triggers the route change events and updates `router.query`. Only
|
|
30
|
+
* applies to navigations within the same page. No-op on the App Router.
|
|
31
|
+
*/
|
|
32
|
+
shallow?: boolean; /** Locale for i18n (used for locale-prefixed URLs) */
|
|
26
33
|
locale?: string | false; /** Called before navigation happens (Next.js 16). Return value is ignored. */
|
|
27
34
|
onNavigate?: (event: NavigateEvent) => void;
|
|
28
35
|
children?: React.ReactNode;
|
|
@@ -44,6 +51,10 @@ type LinkStatusContextValue = {
|
|
|
44
51
|
declare function useLinkStatus(): LinkStatusContextValue;
|
|
45
52
|
declare function resolveLinkPrefetchMode(prefetchProp: LinkProps["prefetch"], isDangerous: boolean): LinkPrefetchMode;
|
|
46
53
|
declare function canAutoPrefetchFullAppRoute(href: string): boolean;
|
|
54
|
+
declare function resolveAutoAppRoutePrefetch(href: string): {
|
|
55
|
+
cacheForNavigation: boolean;
|
|
56
|
+
shouldPrefetch: boolean;
|
|
57
|
+
};
|
|
47
58
|
declare const Link: React.ForwardRefExoticComponent<{
|
|
48
59
|
href: string | {
|
|
49
60
|
pathname?: string;
|
|
@@ -58,11 +69,18 @@ declare const Link: React.ForwardRefExoticComponent<{
|
|
|
58
69
|
*/
|
|
59
70
|
unstable_dynamicOnHover?: boolean; /** Whether to pass the href to the child element */
|
|
60
71
|
passHref?: boolean; /** Scroll to top on navigation (default: true) */
|
|
61
|
-
scroll?: boolean;
|
|
72
|
+
scroll?: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Pages Router: update the URL without re-running data fetching methods
|
|
75
|
+
* (getServerSideProps / getStaticProps / getInitialProps). The shallow change
|
|
76
|
+
* still triggers the route change events and updates `router.query`. Only
|
|
77
|
+
* applies to navigations within the same page. No-op on the App Router.
|
|
78
|
+
*/
|
|
79
|
+
shallow?: boolean; /** Locale for i18n (used for locale-prefixed URLs) */
|
|
62
80
|
locale?: string | false; /** Called before navigation happens (Next.js 16). Return value is ignored. */
|
|
63
81
|
onNavigate?: (event: NavigateEvent) => void;
|
|
64
82
|
children?: React.ReactNode;
|
|
65
83
|
} & Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, "href"> & React.RefAttributes<HTMLAnchorElement>>;
|
|
66
84
|
//#endregion
|
|
67
|
-
export { canAutoPrefetchFullAppRoute, Link as default, resolveLinkPrefetchMode, useLinkStatus };
|
|
85
|
+
export { canAutoPrefetchFullAppRoute, Link as default, resolveAutoAppRoutePrefetch, resolveLinkPrefetchMode, useLinkStatus };
|
|
68
86
|
//# sourceMappingURL=link.d.ts.map
|
package/dist/shims/link.js
CHANGED
|
@@ -2,14 +2,18 @@
|
|
|
2
2
|
import { stripBasePath } from "../utils/base-path.js";
|
|
3
3
|
import { createRouteTrieCache, matchRouteWithTrie } from "../routing/route-matching.js";
|
|
4
4
|
import { VINEXT_MOUNTED_SLOTS_HEADER } from "../server/headers.js";
|
|
5
|
-
import { isDangerousScheme } from "./url-safety.js";
|
|
5
|
+
import { isDangerousScheme, reportBlockedDangerousNavigation } from "./url-safety.js";
|
|
6
|
+
import { appendSearchParamsToUrl, urlQueryToSearchParams } from "../utils/query.js";
|
|
7
|
+
import { APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL } from "../server/app-rsc-render-mode.js";
|
|
6
8
|
import { isAbsoluteOrProtocolRelativeUrl, normalizePathTrailingSlash, resolveRelativeHref, toBrowserNavigationHref, toSameOriginAppPath, withBasePath } from "./url-utils.js";
|
|
7
9
|
import { addLocalePrefix, getDomainLocaleUrl } from "../utils/domain-locale.js";
|
|
8
|
-
import {
|
|
10
|
+
import { getCurrentBrowserLocale } from "./client-locale.js";
|
|
11
|
+
import { getNavigationRuntime, hasAppNavigationRuntime, registerNavigationRuntimeFunctions } from "../client/navigation-runtime.js";
|
|
9
12
|
import { AppElementsWire } from "../server/app-elements-wire.js";
|
|
10
13
|
import "../server/app-elements.js";
|
|
11
|
-
import { createRscRequestHeaders, createRscRequestUrl } from "../server/app-rsc-cache-busting.js";
|
|
12
|
-
import {
|
|
14
|
+
import { createRscRequestHeaders, createRscRequestUrl, stripRscCacheBustingSearchParam, stripRscSuffix } from "../server/app-rsc-cache-busting.js";
|
|
15
|
+
import { getMountedSlotsHeader, getPrefetchCache, getPrefetchInterceptionContext, getPrefetchedUrls, navigateClientSide, prefetchRscResponse } from "./navigation.js";
|
|
16
|
+
import { navigatePagesRouterLink } from "../client/pages-router-link-navigation.js";
|
|
13
17
|
import { getI18nContext } from "./i18n-context.js";
|
|
14
18
|
import { canLinkIntentPrefetch, canLinkPrefetch, getLinkPrefetchHref } from "./link-prefetch.js";
|
|
15
19
|
import React, { createContext, forwardRef, useCallback, useContext, useEffect, useRef, useState } from "react";
|
|
@@ -62,7 +66,7 @@ function toSameOriginRouteHref(href) {
|
|
|
62
66
|
return `${stripBasePath(url.pathname, __basePath)}${url.search}`;
|
|
63
67
|
}
|
|
64
68
|
function getLinkPrefetchRouterMode() {
|
|
65
|
-
return
|
|
69
|
+
return hasAppNavigationRuntime() ? "app" : "pages";
|
|
66
70
|
}
|
|
67
71
|
function canAutoPrefetchFullAppRoute(href) {
|
|
68
72
|
if (typeof window === "undefined") return false;
|
|
@@ -74,6 +78,31 @@ function canAutoPrefetchFullAppRoute(href) {
|
|
|
74
78
|
if (!match) return false;
|
|
75
79
|
return !match.route.isDynamic;
|
|
76
80
|
}
|
|
81
|
+
function resolveAutoAppRoutePrefetch(href) {
|
|
82
|
+
if (typeof window === "undefined") return {
|
|
83
|
+
cacheForNavigation: false,
|
|
84
|
+
shouldPrefetch: false
|
|
85
|
+
};
|
|
86
|
+
const routes = window.__VINEXT_LINK_PREFETCH_ROUTES__;
|
|
87
|
+
if (!routes) return {
|
|
88
|
+
cacheForNavigation: false,
|
|
89
|
+
shouldPrefetch: false
|
|
90
|
+
};
|
|
91
|
+
const routeHref = toSameOriginRouteHref(href);
|
|
92
|
+
if (routeHref === null) return {
|
|
93
|
+
cacheForNavigation: false,
|
|
94
|
+
shouldPrefetch: false
|
|
95
|
+
};
|
|
96
|
+
const match = matchRouteWithTrie(routeHref, routes, linkPrefetchRouteTrieCache);
|
|
97
|
+
if (!match) return {
|
|
98
|
+
cacheForNavigation: false,
|
|
99
|
+
shouldPrefetch: false
|
|
100
|
+
};
|
|
101
|
+
return {
|
|
102
|
+
cacheForNavigation: !match.route.isDynamic,
|
|
103
|
+
shouldPrefetch: !match.route.isDynamic || match.route.canPrefetchLoadingShell
|
|
104
|
+
};
|
|
105
|
+
}
|
|
77
106
|
/**
|
|
78
107
|
* Prefetch a URL for faster navigation.
|
|
79
108
|
*
|
|
@@ -95,23 +124,41 @@ function prefetchUrl(href, mode, priority = "low") {
|
|
|
95
124
|
const fullHref = toBrowserNavigationHref(prefetchHref, window.location.href, __basePath);
|
|
96
125
|
(window.requestIdleCallback ?? ((fn) => setTimeout(fn, 100)))(() => {
|
|
97
126
|
(async () => {
|
|
98
|
-
if (
|
|
99
|
-
|
|
100
|
-
|
|
127
|
+
if (hasAppNavigationRuntime()) {
|
|
128
|
+
const autoPrefetch = mode === "auto" ? resolveAutoAppRoutePrefetch(prefetchHref) : {
|
|
129
|
+
cacheForNavigation: true,
|
|
130
|
+
shouldPrefetch: true
|
|
131
|
+
};
|
|
132
|
+
if (!autoPrefetch.shouldPrefetch) return;
|
|
133
|
+
const interceptionContext = getPrefetchInterceptionContext(fullHref);
|
|
101
134
|
const mountedSlotsHeader = getMountedSlotsHeader();
|
|
102
|
-
const
|
|
135
|
+
const isOptimisticRouteShellPrefetch = !autoPrefetch.cacheForNavigation;
|
|
136
|
+
if (isOptimisticRouteShellPrefetch && interceptionContext !== null) return;
|
|
137
|
+
const headers = createRscRequestHeaders({
|
|
138
|
+
interceptionContext,
|
|
139
|
+
renderMode: isOptimisticRouteShellPrefetch ? APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL : void 0
|
|
140
|
+
});
|
|
103
141
|
if (mountedSlotsHeader) headers.set(VINEXT_MOUNTED_SLOTS_HEADER, mountedSlotsHeader);
|
|
104
142
|
const rscUrl = await createRscRequestUrl(fullHref, headers);
|
|
105
143
|
const cacheKey = AppElementsWire.encodeCacheKey(rscUrl, interceptionContext);
|
|
106
144
|
const prefetched = getPrefetchedUrls();
|
|
107
|
-
if (prefetched.has(cacheKey))
|
|
145
|
+
if (prefetched.has(cacheKey)) {
|
|
146
|
+
if (autoPrefetch.cacheForNavigation) {
|
|
147
|
+
const existing = getPrefetchCache().get(cacheKey);
|
|
148
|
+
if (existing?.cacheForNavigation === false) existing.cacheForNavigation = true;
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
108
152
|
prefetched.add(cacheKey);
|
|
109
153
|
prefetchRscResponse(rscUrl, fetch(rscUrl, {
|
|
110
154
|
headers,
|
|
111
155
|
credentials: "include",
|
|
112
156
|
priority,
|
|
113
157
|
purpose: "prefetch"
|
|
114
|
-
}), interceptionContext, mountedSlotsHeader
|
|
158
|
+
}), interceptionContext, mountedSlotsHeader, void 0, {
|
|
159
|
+
cacheForNavigation: autoPrefetch.cacheForNavigation,
|
|
160
|
+
optimisticRouteShell: isOptimisticRouteShellPrefetch
|
|
161
|
+
});
|
|
115
162
|
} else if (window.__NEXT_DATA__?.__vinext?.pageModuleUrl) {
|
|
116
163
|
const link = document.createElement("link");
|
|
117
164
|
link.rel = "prefetch";
|
|
@@ -124,6 +171,27 @@ function prefetchUrl(href, mode, priority = "low") {
|
|
|
124
171
|
});
|
|
125
172
|
});
|
|
126
173
|
}
|
|
174
|
+
function promotePrefetchEntriesForNavigation(href) {
|
|
175
|
+
if (typeof window === "undefined") return;
|
|
176
|
+
let target;
|
|
177
|
+
try {
|
|
178
|
+
target = new URL(toBrowserNavigationHref(href, window.location.href, __basePath), window.location.href);
|
|
179
|
+
} catch {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
for (const [cacheKey, entry] of getPrefetchCache()) {
|
|
183
|
+
if (entry.optimisticRouteShell === true) continue;
|
|
184
|
+
const [rscUrl] = cacheKey.split("\0", 1);
|
|
185
|
+
let cached;
|
|
186
|
+
try {
|
|
187
|
+
cached = new URL(rscUrl, window.location.href);
|
|
188
|
+
} catch {
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
stripRscCacheBustingSearchParam(cached);
|
|
192
|
+
if (stripRscSuffix(cached.pathname) === target.pathname && cached.search === target.search) entry.cacheForNavigation = true;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
127
195
|
/**
|
|
128
196
|
* Shared IntersectionObserver for viewport-based prefetching.
|
|
129
197
|
* All Link elements use the same observer to minimize resource usage.
|
|
@@ -142,7 +210,7 @@ function setVisibleLinkPrefetch(instance, isVisible) {
|
|
|
142
210
|
}
|
|
143
211
|
function registerVisibleLinkPing() {
|
|
144
212
|
if (typeof window === "undefined") return;
|
|
145
|
-
|
|
213
|
+
registerNavigationRuntimeFunctions({ pingVisibleLinks: pingVisibleLinkPrefetches });
|
|
146
214
|
}
|
|
147
215
|
function pingVisibleLinkPrefetches() {
|
|
148
216
|
for (const instance of visibleLinkPrefetches) if (instance.isVisible && instance.routerMode === "app") prefetchUrl(instance.href, instance.mode, "low");
|
|
@@ -163,6 +231,14 @@ function getDefaultLocale() {
|
|
|
163
231
|
if (typeof window !== "undefined") return window.__VINEXT_DEFAULT_LOCALE__;
|
|
164
232
|
return getI18nContext()?.defaultLocale;
|
|
165
233
|
}
|
|
234
|
+
function getCurrentLocale() {
|
|
235
|
+
if (typeof window !== "undefined") return getCurrentBrowserLocale({
|
|
236
|
+
basePath: __basePath,
|
|
237
|
+
domainLocales: getDomainLocales(),
|
|
238
|
+
hostname: getCurrentHostname()
|
|
239
|
+
});
|
|
240
|
+
return getI18nContext()?.locale;
|
|
241
|
+
}
|
|
166
242
|
function getDomainLocales() {
|
|
167
243
|
if (typeof window !== "undefined") return window.__NEXT_DATA__?.domainLocales;
|
|
168
244
|
return getI18nContext()?.domainLocales;
|
|
@@ -178,6 +254,17 @@ function getDomainLocaleHref(href, locale) {
|
|
|
178
254
|
domainItems: getDomainLocales()
|
|
179
255
|
});
|
|
180
256
|
}
|
|
257
|
+
function addLocalePrefixForRoot(href, locale) {
|
|
258
|
+
if (href !== "/" && !href.startsWith("/?") && !href.startsWith("/#")) return;
|
|
259
|
+
let parsed;
|
|
260
|
+
try {
|
|
261
|
+
parsed = new URL(href, "http://vinext.local");
|
|
262
|
+
} catch {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if (parsed.origin !== "http://vinext.local" || parsed.pathname !== "/") return;
|
|
266
|
+
return `/${locale}${parsed.search}${parsed.hash}`;
|
|
267
|
+
}
|
|
181
268
|
/**
|
|
182
269
|
* Apply locale prefix to a URL path based on the locale prop.
|
|
183
270
|
* - locale="fr" → prepend /fr (unless it already has a locale prefix)
|
|
@@ -186,13 +273,19 @@ function getDomainLocaleHref(href, locale) {
|
|
|
186
273
|
*/
|
|
187
274
|
function applyLocaleToHref(href, locale) {
|
|
188
275
|
if (locale === false) return href;
|
|
189
|
-
|
|
276
|
+
const resolvedLocale = locale ?? getCurrentLocale();
|
|
277
|
+
if (resolvedLocale === void 0) return href;
|
|
190
278
|
if (isAbsoluteOrProtocolRelativeUrl(href)) return href;
|
|
191
|
-
const domainLocaleHref = getDomainLocaleHref(href,
|
|
279
|
+
const domainLocaleHref = getDomainLocaleHref(href, resolvedLocale);
|
|
192
280
|
if (domainLocaleHref) return domainLocaleHref;
|
|
193
|
-
|
|
281
|
+
const defaultLocale = getDefaultLocale() ?? "";
|
|
282
|
+
if (resolvedLocale.toLowerCase() === defaultLocale.toLowerCase()) {
|
|
283
|
+
const localeRootHref = addLocalePrefixForRoot(href, resolvedLocale);
|
|
284
|
+
if (localeRootHref) return localeRootHref;
|
|
285
|
+
}
|
|
286
|
+
return addLocalePrefix(href, resolvedLocale, defaultLocale);
|
|
194
287
|
}
|
|
195
|
-
const Link = forwardRef(function Link({ href, as, replace = false, prefetch: prefetchProp, scroll = true, children, onClick, onMouseEnter, onTouchStart, onNavigate, unstable_dynamicOnHover = false, ...rest }, forwardedRef) {
|
|
288
|
+
const Link = forwardRef(function Link({ href, as, replace = false, prefetch: prefetchProp, scroll = true, shallow = false, children, onClick, onMouseEnter, onTouchStart, onNavigate, unstable_dynamicOnHover = false, ...rest }, forwardedRef) {
|
|
196
289
|
const { locale, ...restWithoutLocale } = rest;
|
|
197
290
|
const resolvedHref = as ?? resolveHref(href);
|
|
198
291
|
const isDangerous = typeof resolvedHref === "string" && isDangerousScheme(resolvedHref);
|
|
@@ -261,6 +354,7 @@ const Link = forwardRef(function Link({ href, as, replace = false, prefetch: pre
|
|
|
261
354
|
if (unstable_dynamicOnHover && internalRef.current) {
|
|
262
355
|
const instance = observedLinkPrefetches.get(internalRef.current);
|
|
263
356
|
if (instance) instance.mode = "full";
|
|
357
|
+
promotePrefetchEntriesForNavigation(normalizedHref);
|
|
264
358
|
}
|
|
265
359
|
prefetchUrl(normalizedHref, intentMode, "high");
|
|
266
360
|
}, [
|
|
@@ -287,7 +381,13 @@ const Link = forwardRef(function Link({ href, as, replace = false, prefetch: pre
|
|
|
287
381
|
let navigateHref = normalizedHref;
|
|
288
382
|
if (isAbsoluteOrProtocolRelativeUrl(resolvedHref)) {
|
|
289
383
|
const localPath = toSameOriginAppPath(resolvedHref, __basePath);
|
|
290
|
-
if (localPath == null)
|
|
384
|
+
if (localPath == null) {
|
|
385
|
+
if (replace) {
|
|
386
|
+
e.preventDefault();
|
|
387
|
+
window.location.replace(resolvedHref);
|
|
388
|
+
}
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
291
391
|
navigateHref = localPath;
|
|
292
392
|
}
|
|
293
393
|
e.preventDefault();
|
|
@@ -308,7 +408,7 @@ const Link = forwardRef(function Link({ href, as, replace = false, prefetch: pre
|
|
|
308
408
|
onNavigate(navEvent);
|
|
309
409
|
if (navEvent.defaultPrevented) return;
|
|
310
410
|
} catch {}
|
|
311
|
-
if (
|
|
411
|
+
if (getNavigationRuntime()?.functions.navigate) {
|
|
312
412
|
setPending(true);
|
|
313
413
|
React.startTransition(() => {
|
|
314
414
|
navigateClientSide(navigateHref, replace ? "replace" : "push", scroll, true).finally(() => {
|
|
@@ -318,8 +418,13 @@ const Link = forwardRef(function Link({ href, as, replace = false, prefetch: pre
|
|
|
318
418
|
return;
|
|
319
419
|
} else try {
|
|
320
420
|
const Router = (await import("next/router.js")).default;
|
|
321
|
-
|
|
322
|
-
|
|
421
|
+
await navigatePagesRouterLink(Router, {
|
|
422
|
+
href: absoluteHref,
|
|
423
|
+
replace,
|
|
424
|
+
scroll,
|
|
425
|
+
shallow,
|
|
426
|
+
locale
|
|
427
|
+
});
|
|
323
428
|
} catch {
|
|
324
429
|
if (replace) window.history.replaceState({}, "", absoluteFullHref);
|
|
325
430
|
else window.history.pushState({}, "", absoluteFullHref);
|
|
@@ -330,11 +435,15 @@ const Link = forwardRef(function Link({ href, as, replace = false, prefetch: pre
|
|
|
330
435
|
const linkStatusValue = React.useMemo(() => ({ pending }), [pending]);
|
|
331
436
|
if (isDangerous) {
|
|
332
437
|
if (process.env.NODE_ENV !== "production") console.warn(`<Link> blocked dangerous href: ${resolvedHref}`);
|
|
438
|
+
const handleDangerousClick = (event) => {
|
|
439
|
+
if (onClick) onClick(event);
|
|
440
|
+
reportBlockedDangerousNavigation();
|
|
441
|
+
};
|
|
333
442
|
return /* @__PURE__ */ jsx(LinkStatusContext.Provider, {
|
|
334
443
|
value: linkStatusValue,
|
|
335
444
|
children: /* @__PURE__ */ jsx("a", {
|
|
336
445
|
ref: setRefs,
|
|
337
|
-
onClick,
|
|
446
|
+
onClick: handleDangerousClick,
|
|
338
447
|
onMouseEnter: handleMouseEnter,
|
|
339
448
|
onTouchStart: handleTouchStart,
|
|
340
449
|
...anchorProps,
|
|
@@ -358,6 +467,6 @@ const Link = forwardRef(function Link({ href, as, replace = false, prefetch: pre
|
|
|
358
467
|
});
|
|
359
468
|
});
|
|
360
469
|
//#endregion
|
|
361
|
-
export { canAutoPrefetchFullAppRoute, Link as default, resolveLinkPrefetchMode, useLinkStatus };
|
|
470
|
+
export { canAutoPrefetchFullAppRoute, Link as default, resolveAutoAppRoutePrefetch, resolveLinkPrefetchMode, useLinkStatus };
|
|
362
471
|
|
|
363
472
|
//# sourceMappingURL=link.js.map
|