vinext 0.0.53 → 0.0.55

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.
Files changed (276) hide show
  1. package/README.md +1 -0
  2. package/dist/build/inline-css.d.ts +7 -0
  3. package/dist/build/inline-css.js +50 -0
  4. package/dist/build/inline-css.js.map +1 -0
  5. package/dist/build/prerender.js +2 -1
  6. package/dist/build/prerender.js.map +1 -1
  7. package/dist/check.js +19 -3
  8. package/dist/check.js.map +1 -1
  9. package/dist/client/navigation-runtime.d.ts +3 -1
  10. package/dist/client/navigation-runtime.js +1 -1
  11. package/dist/client/navigation-runtime.js.map +1 -1
  12. package/dist/client/window-next.d.ts +7 -0
  13. package/dist/client/window-next.js.map +1 -1
  14. package/dist/config/next-config.d.ts +97 -2
  15. package/dist/config/next-config.js +155 -6
  16. package/dist/config/next-config.js.map +1 -1
  17. package/dist/config/tsconfig-paths.d.ts +12 -3
  18. package/dist/config/tsconfig-paths.js +55 -24
  19. package/dist/config/tsconfig-paths.js.map +1 -1
  20. package/dist/deploy.js +13 -0
  21. package/dist/deploy.js.map +1 -1
  22. package/dist/entries/app-browser-entry.d.ts +11 -1
  23. package/dist/entries/app-browser-entry.js +16 -6
  24. package/dist/entries/app-browser-entry.js.map +1 -1
  25. package/dist/entries/app-rsc-entry.d.ts +9 -1
  26. package/dist/entries/app-rsc-entry.js +30 -5
  27. package/dist/entries/app-rsc-entry.js.map +1 -1
  28. package/dist/entries/app-rsc-manifest.d.ts +21 -1
  29. package/dist/entries/app-rsc-manifest.js +28 -9
  30. package/dist/entries/app-rsc-manifest.js.map +1 -1
  31. package/dist/entries/pages-client-entry.d.ts +4 -1
  32. package/dist/entries/pages-client-entry.js +18 -2
  33. package/dist/entries/pages-client-entry.js.map +1 -1
  34. package/dist/entries/pages-server-entry.js +123 -8
  35. package/dist/entries/pages-server-entry.js.map +1 -1
  36. package/dist/entries/runtime-entry-module.d.ts +1 -10
  37. package/dist/entries/runtime-entry-module.js +2 -12
  38. package/dist/entries/runtime-entry-module.js.map +1 -1
  39. package/dist/index.js +144 -44
  40. package/dist/index.js.map +1 -1
  41. package/dist/plugins/import-meta-url.d.ts +16 -0
  42. package/dist/plugins/import-meta-url.js +193 -0
  43. package/dist/plugins/import-meta-url.js.map +1 -0
  44. package/dist/plugins/remove-console.d.ts +16 -0
  45. package/dist/plugins/remove-console.js +176 -0
  46. package/dist/plugins/remove-console.js.map +1 -0
  47. package/dist/routing/app-route-graph.d.ts +24 -1
  48. package/dist/routing/app-route-graph.js +52 -4
  49. package/dist/routing/app-route-graph.js.map +1 -1
  50. package/dist/routing/app-router.d.ts +2 -2
  51. package/dist/routing/app-router.js +2 -2
  52. package/dist/routing/app-router.js.map +1 -1
  53. package/dist/routing/file-matcher.d.ts +21 -1
  54. package/dist/routing/file-matcher.js +39 -1
  55. package/dist/routing/file-matcher.js.map +1 -1
  56. package/dist/routing/pages-router.d.ts +1 -1
  57. package/dist/routing/pages-router.js +10 -3
  58. package/dist/routing/pages-router.js.map +1 -1
  59. package/dist/server/api-handler.js +1 -1
  60. package/dist/server/app-browser-action-result.d.ts +9 -16
  61. package/dist/server/app-browser-action-result.js +25 -14
  62. package/dist/server/app-browser-action-result.js.map +1 -1
  63. package/dist/server/app-browser-entry.js +195 -60
  64. package/dist/server/app-browser-entry.js.map +1 -1
  65. package/dist/server/app-browser-mpa-navigation.d.ts +16 -0
  66. package/dist/server/app-browser-mpa-navigation.js +36 -0
  67. package/dist/server/app-browser-mpa-navigation.js.map +1 -0
  68. package/dist/server/app-browser-navigation-controller.d.ts +2 -0
  69. package/dist/server/app-browser-navigation-controller.js +4 -0
  70. package/dist/server/app-browser-navigation-controller.js.map +1 -1
  71. package/dist/server/app-browser-popstate.d.ts +3 -1
  72. package/dist/server/app-browser-popstate.js +15 -1
  73. package/dist/server/app-browser-popstate.js.map +1 -1
  74. package/dist/server/app-browser-state.js +2 -1
  75. package/dist/server/app-browser-state.js.map +1 -1
  76. package/dist/server/app-elements-wire.d.ts +13 -4
  77. package/dist/server/app-elements-wire.js +10 -1
  78. package/dist/server/app-elements-wire.js.map +1 -1
  79. package/dist/server/app-elements.d.ts +2 -2
  80. package/dist/server/app-elements.js +2 -2
  81. package/dist/server/app-elements.js.map +1 -1
  82. package/dist/server/app-fallback-renderer.d.ts +15 -5
  83. package/dist/server/app-fallback-renderer.js +10 -4
  84. package/dist/server/app-fallback-renderer.js.map +1 -1
  85. package/dist/server/app-inline-css-client.d.ts +7 -0
  86. package/dist/server/app-inline-css-client.js +37 -0
  87. package/dist/server/app-inline-css-client.js.map +1 -0
  88. package/dist/server/app-layout-param-observation.d.ts +30 -0
  89. package/dist/server/app-layout-param-observation.js +130 -0
  90. package/dist/server/app-layout-param-observation.js.map +1 -0
  91. package/dist/server/app-page-boundary-render.js +2 -2
  92. package/dist/server/app-page-boundary-render.js.map +1 -1
  93. package/dist/server/app-page-boundary.d.ts +21 -1
  94. package/dist/server/app-page-boundary.js +28 -3
  95. package/dist/server/app-page-boundary.js.map +1 -1
  96. package/dist/server/app-page-cache.d.ts +7 -3
  97. package/dist/server/app-page-cache.js +7 -7
  98. package/dist/server/app-page-cache.js.map +1 -1
  99. package/dist/server/app-page-dispatch.d.ts +10 -1
  100. package/dist/server/app-page-dispatch.js +126 -79
  101. package/dist/server/app-page-dispatch.js.map +1 -1
  102. package/dist/server/app-page-element-builder.js +12 -28
  103. package/dist/server/app-page-element-builder.js.map +1 -1
  104. package/dist/server/app-page-params.d.ts +2 -1
  105. package/dist/server/app-page-params.js +14 -1
  106. package/dist/server/app-page-params.js.map +1 -1
  107. package/dist/server/app-page-probe.d.ts +12 -1
  108. package/dist/server/app-page-probe.js +116 -1
  109. package/dist/server/app-page-probe.js.map +1 -1
  110. package/dist/server/app-page-render-identity.d.ts +22 -0
  111. package/dist/server/app-page-render-identity.js +42 -0
  112. package/dist/server/app-page-render-identity.js.map +1 -0
  113. package/dist/server/app-page-render.d.ts +8 -1
  114. package/dist/server/app-page-render.js +4 -1
  115. package/dist/server/app-page-render.js.map +1 -1
  116. package/dist/server/app-page-request.d.ts +6 -3
  117. package/dist/server/app-page-request.js +5 -2
  118. package/dist/server/app-page-request.js.map +1 -1
  119. package/dist/server/app-page-response.js +2 -2
  120. package/dist/server/app-page-response.js.map +1 -1
  121. package/dist/server/app-page-route-wiring.d.ts +15 -0
  122. package/dist/server/app-page-route-wiring.js +7 -5
  123. package/dist/server/app-page-route-wiring.js.map +1 -1
  124. package/dist/server/app-page-stream.d.ts +11 -0
  125. package/dist/server/app-page-stream.js +1 -0
  126. package/dist/server/app-page-stream.js.map +1 -1
  127. package/dist/server/app-route-handler-response.js +37 -5
  128. package/dist/server/app-route-handler-response.js.map +1 -1
  129. package/dist/server/app-rsc-cache-busting.d.ts +3 -2
  130. package/dist/server/app-rsc-cache-busting.js +9 -7
  131. package/dist/server/app-rsc-cache-busting.js.map +1 -1
  132. package/dist/server/app-rsc-handler.d.ts +14 -3
  133. package/dist/server/app-rsc-handler.js +56 -6
  134. package/dist/server/app-rsc-handler.js.map +1 -1
  135. package/dist/server/app-rsc-request-normalization.d.ts +2 -1
  136. package/dist/server/app-rsc-request-normalization.js +3 -2
  137. package/dist/server/app-rsc-request-normalization.js.map +1 -1
  138. package/dist/server/app-segment-config.d.ts +1 -1
  139. package/dist/server/app-segment-config.js +4 -1
  140. package/dist/server/app-segment-config.js.map +1 -1
  141. package/dist/server/app-server-action-execution.d.ts +26 -3
  142. package/dist/server/app-server-action-execution.js +240 -29
  143. package/dist/server/app-server-action-execution.js.map +1 -1
  144. package/dist/server/app-ssr-entry.d.ts +6 -0
  145. package/dist/server/app-ssr-entry.js +22 -7
  146. package/dist/server/app-ssr-entry.js.map +1 -1
  147. package/dist/server/app-ssr-error-meta.js +3 -3
  148. package/dist/server/app-ssr-error-meta.js.map +1 -1
  149. package/dist/server/app-ssr-stream.d.ts +2 -1
  150. package/dist/server/app-ssr-stream.js +176 -31
  151. package/dist/server/app-ssr-stream.js.map +1 -1
  152. package/dist/server/artifact-compatibility.d.ts +2 -1
  153. package/dist/server/artifact-compatibility.js +10 -1
  154. package/dist/server/artifact-compatibility.js.map +1 -1
  155. package/dist/server/client-reuse-manifest.d.ts +9 -4
  156. package/dist/server/client-reuse-manifest.js +2 -1
  157. package/dist/server/client-reuse-manifest.js.map +1 -1
  158. package/dist/server/client-trace-metadata.d.ts +31 -0
  159. package/dist/server/client-trace-metadata.js +83 -0
  160. package/dist/server/client-trace-metadata.js.map +1 -0
  161. package/dist/server/cookie-utils.d.ts +13 -0
  162. package/dist/server/cookie-utils.js +20 -0
  163. package/dist/server/cookie-utils.js.map +1 -0
  164. package/dist/server/dev-server.d.ts +8 -1
  165. package/dist/server/dev-server.js +83 -12
  166. package/dist/server/dev-server.js.map +1 -1
  167. package/dist/server/document-initial-head.d.ts +7 -0
  168. package/dist/server/document-initial-head.js +35 -0
  169. package/dist/server/document-initial-head.js.map +1 -0
  170. package/dist/server/html.d.ts +2 -1
  171. package/dist/server/html.js +6 -1
  172. package/dist/server/html.js.map +1 -1
  173. package/dist/server/isr-cache.d.ts +7 -5
  174. package/dist/server/isr-cache.js +17 -6
  175. package/dist/server/isr-cache.js.map +1 -1
  176. package/dist/server/middleware-runtime.js +1 -2
  177. package/dist/server/middleware-runtime.js.map +1 -1
  178. package/dist/server/pages-document-initial-props.d.ts +89 -0
  179. package/dist/server/pages-document-initial-props.js +140 -0
  180. package/dist/server/pages-document-initial-props.js.map +1 -0
  181. package/dist/server/pages-node-compat.js +1 -1
  182. package/dist/server/pages-page-data.js +3 -0
  183. package/dist/server/pages-page-data.js.map +1 -1
  184. package/dist/server/pages-page-method.d.ts +48 -0
  185. package/dist/server/pages-page-method.js +19 -0
  186. package/dist/server/pages-page-method.js.map +1 -0
  187. package/dist/server/pages-page-response.d.ts +20 -0
  188. package/dist/server/pages-page-response.js +37 -7
  189. package/dist/server/pages-page-response.js.map +1 -1
  190. package/dist/server/pages-serializable-props.d.ts +25 -0
  191. package/dist/server/pages-serializable-props.js +69 -0
  192. package/dist/server/pages-serializable-props.js.map +1 -0
  193. package/dist/server/prod-server.js +16 -6
  194. package/dist/server/prod-server.js.map +1 -1
  195. package/dist/server/server-action-not-found.js +3 -2
  196. package/dist/server/server-action-not-found.js.map +1 -1
  197. package/dist/server/skip-cache-proof.d.ts +23 -2
  198. package/dist/server/skip-cache-proof.js +81 -12
  199. package/dist/server/skip-cache-proof.js.map +1 -1
  200. package/dist/server/static-file-cache.js +2 -1
  201. package/dist/server/static-file-cache.js.map +1 -1
  202. package/dist/server/static-layout-client-reuse-proof.d.ts +16 -0
  203. package/dist/server/static-layout-client-reuse-proof.js +35 -0
  204. package/dist/server/static-layout-client-reuse-proof.js.map +1 -0
  205. package/dist/shims/app-router-scroll-state.d.ts +4 -2
  206. package/dist/shims/app-router-scroll-state.js +16 -3
  207. package/dist/shims/app-router-scroll-state.js.map +1 -1
  208. package/dist/shims/app-router-scroll.d.ts +16 -2
  209. package/dist/shims/app-router-scroll.js +18 -3
  210. package/dist/shims/app-router-scroll.js.map +1 -1
  211. package/dist/shims/cache.d.ts +27 -1
  212. package/dist/shims/cache.js +108 -6
  213. package/dist/shims/cache.js.map +1 -1
  214. package/dist/shims/document.d.ts +6 -0
  215. package/dist/shims/document.js +7 -8
  216. package/dist/shims/document.js.map +1 -1
  217. package/dist/shims/error-boundary.d.ts +4 -4
  218. package/dist/shims/error-boundary.js +27 -28
  219. package/dist/shims/error-boundary.js.map +1 -1
  220. package/dist/shims/error.js +3 -0
  221. package/dist/shims/error.js.map +1 -1
  222. package/dist/shims/fetch-cache.d.ts +3 -1
  223. package/dist/shims/fetch-cache.js +16 -5
  224. package/dist/shims/fetch-cache.js.map +1 -1
  225. package/dist/shims/hash-scroll.d.ts +4 -1
  226. package/dist/shims/hash-scroll.js +13 -1
  227. package/dist/shims/hash-scroll.js.map +1 -1
  228. package/dist/shims/head-state.d.ts +1 -0
  229. package/dist/shims/head-state.js +18 -3
  230. package/dist/shims/head-state.js.map +1 -1
  231. package/dist/shims/head.d.ts +35 -1
  232. package/dist/shims/head.js +113 -14
  233. package/dist/shims/head.js.map +1 -1
  234. package/dist/shims/headers.d.ts +7 -0
  235. package/dist/shims/headers.js +9 -1
  236. package/dist/shims/headers.js.map +1 -1
  237. package/dist/shims/internal/app-route-detection.d.ts +37 -0
  238. package/dist/shims/internal/app-route-detection.js +69 -0
  239. package/dist/shims/internal/app-route-detection.js.map +1 -0
  240. package/dist/shims/internal/pages-data-fetch-dedup.d.ts +56 -0
  241. package/dist/shims/internal/pages-data-fetch-dedup.js +70 -0
  242. package/dist/shims/internal/pages-data-fetch-dedup.js.map +1 -0
  243. package/dist/shims/link.d.ts +18 -2
  244. package/dist/shims/link.js +98 -8
  245. package/dist/shims/link.js.map +1 -1
  246. package/dist/shims/metadata.d.ts +7 -6
  247. package/dist/shims/metadata.js +9 -5
  248. package/dist/shims/metadata.js.map +1 -1
  249. package/dist/shims/navigation.d.ts +40 -3
  250. package/dist/shims/navigation.js +124 -25
  251. package/dist/shims/navigation.js.map +1 -1
  252. package/dist/shims/router.d.ts +5 -0
  253. package/dist/shims/router.js +51 -21
  254. package/dist/shims/router.js.map +1 -1
  255. package/dist/shims/script.d.ts +11 -1
  256. package/dist/shims/script.js +75 -6
  257. package/dist/shims/script.js.map +1 -1
  258. package/dist/shims/thenable-params.d.ts +5 -2
  259. package/dist/shims/thenable-params.js +25 -1
  260. package/dist/shims/thenable-params.js.map +1 -1
  261. package/dist/shims/unified-request-context.js +3 -0
  262. package/dist/shims/unified-request-context.js.map +1 -1
  263. package/dist/utils/client-build-manifest.d.ts +15 -0
  264. package/dist/utils/client-build-manifest.js +54 -0
  265. package/dist/utils/client-build-manifest.js.map +1 -0
  266. package/dist/utils/hash.js +1 -1
  267. package/dist/utils/hash.js.map +1 -1
  268. package/dist/utils/lazy-chunks.d.ts +1 -1
  269. package/dist/utils/lazy-chunks.js.map +1 -1
  270. package/dist/utils/path.d.ts +13 -0
  271. package/dist/utils/path.js +16 -0
  272. package/dist/utils/path.js.map +1 -0
  273. package/dist/utils/vite-version.d.ts +11 -0
  274. package/dist/utils/vite-version.js +36 -0
  275. package/dist/utils/vite-version.js.map +1 -0
  276. package/package.json +2 -2
@@ -0,0 +1,37 @@
1
+ import { VinextLinkPrefetchRoute } from "../../client/vinext-next-data.js";
2
+
3
+ //#region src/shims/internal/app-route-detection.d.ts
4
+ declare global {
5
+ interface Window {
6
+ __VINEXT_LINK_PREFETCH_ROUTES__?: VinextLinkPrefetchRoute[];
7
+ }
8
+ }
9
+ /**
10
+ * Pages Router `components` map shape. Next.js types this loosely (route
11
+ * pattern → `PrivateRouteInfo`), but for the App Router-detected case it
12
+ * stores `{ __appRouter: true }` as a marker (see Next.js source link above).
13
+ * Vinext only writes the marker variant; reads are by test code that checks
14
+ * for `__appRouter: true`.
15
+ */
16
+ type PagesRouterComponentsMap = Record<string, {
17
+ __appRouter: true;
18
+ } | Record<string, unknown>>;
19
+ /**
20
+ * Get-or-create the Pages Router `components` map. Returns the same object
21
+ * on every call so `router.components` and `window.next.router.components`
22
+ * have referential identity.
23
+ */
24
+ declare function getPagesRouterComponentsMap(): PagesRouterComponentsMap;
25
+ /**
26
+ * Record `components[pathname] = { __appRouter: true }` on the shared
27
+ * Pages Router map when the href matches an App Router route. No-op when the
28
+ * manifest is absent, the URL is external, or no app route matches.
29
+ *
30
+ * `pathname` is the basePath-stripped path — matching Next.js's
31
+ * `router.components[urlPathname]` key (see the source link in this file's
32
+ * leading comment).
33
+ */
34
+ declare function markAppRouteDetectedOnPrefetch(href: string, basePath: string): void;
35
+ //#endregion
36
+ export { getPagesRouterComponentsMap, markAppRouteDetectedOnPrefetch };
37
+ //# sourceMappingURL=app-route-detection.d.ts.map
@@ -0,0 +1,69 @@
1
+ import { stripBasePath } from "../../utils/base-path.js";
2
+ import { createRouteTrieCache, matchRouteWithTrie } from "../../routing/route-matching.js";
3
+ //#region src/shims/internal/app-route-detection.ts
4
+ const appRouteTrieCache = createRouteTrieCache();
5
+ const _COMPONENTS_KEY = Symbol.for("vinext.pagesRouter.components");
6
+ /**
7
+ * Get-or-create the Pages Router `components` map. Returns the same object
8
+ * on every call so `router.components` and `window.next.router.components`
9
+ * have referential identity.
10
+ */
11
+ function getPagesRouterComponentsMap() {
12
+ const globalState = globalThis;
13
+ let components = globalState[_COMPONENTS_KEY];
14
+ if (!components) {
15
+ components = {};
16
+ globalState[_COMPONENTS_KEY] = components;
17
+ }
18
+ return components;
19
+ }
20
+ /**
21
+ * Resolve a prefetch href to a same-origin pathname (basePath-stripped),
22
+ * suitable as the key used by Next.js for `router.components[urlPathname]`.
23
+ *
24
+ * Returns null for external URLs, malformed URLs, or non-browser contexts.
25
+ */
26
+ function resolveSameOriginPathname(href, basePath) {
27
+ if (typeof window === "undefined") return null;
28
+ let url;
29
+ try {
30
+ url = new URL(href, window.location.href);
31
+ } catch {
32
+ return null;
33
+ }
34
+ if (url.origin !== window.location.origin) return null;
35
+ return stripBasePath(url.pathname, basePath);
36
+ }
37
+ /**
38
+ * Returns true when the prefetch href matches any route in the App Router
39
+ * prefetch manifest (static or dynamic). Returns false when the manifest is
40
+ * absent (Pages-Router-only build), the URL is external, or no route matches.
41
+ */
42
+ function matchesAppRoute(href, basePath) {
43
+ if (typeof window === "undefined") return false;
44
+ const routes = window.__VINEXT_LINK_PREFETCH_ROUTES__;
45
+ if (!routes || routes.length === 0) return false;
46
+ const pathname = resolveSameOriginPathname(href, basePath);
47
+ if (pathname === null) return false;
48
+ return matchRouteWithTrie(pathname, routes, appRouteTrieCache) !== null;
49
+ }
50
+ /**
51
+ * Record `components[pathname] = { __appRouter: true }` on the shared
52
+ * Pages Router map when the href matches an App Router route. No-op when the
53
+ * manifest is absent, the URL is external, or no app route matches.
54
+ *
55
+ * `pathname` is the basePath-stripped path — matching Next.js's
56
+ * `router.components[urlPathname]` key (see the source link in this file's
57
+ * leading comment).
58
+ */
59
+ function markAppRouteDetectedOnPrefetch(href, basePath) {
60
+ if (typeof window === "undefined") return;
61
+ if (!matchesAppRoute(href, basePath)) return;
62
+ const pathname = resolveSameOriginPathname(href, basePath);
63
+ if (pathname === null) return;
64
+ getPagesRouterComponentsMap()[pathname] = { __appRouter: true };
65
+ }
66
+ //#endregion
67
+ export { getPagesRouterComponentsMap, markAppRouteDetectedOnPrefetch };
68
+
69
+ //# sourceMappingURL=app-route-detection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-route-detection.js","names":[],"sources":["../../../src/shims/internal/app-route-detection.ts"],"sourcesContent":["/**\n * Shared helper for marking an App Router route as \"detected\" on the Pages\n * Router singleton when a `<Link>` or `Router.prefetch` targets it.\n *\n * Ported from Next.js: `packages/next/src/shared/lib/router/router.ts:2525`\n *\n * if (await this._bfl(asPath, resolvedAs, options.locale, true)) {\n * this.components[urlPathname] = { __appRouter: true } as any\n * }\n *\n * Next.js uses a bloom filter (`_bfl`) of App Router routes that the Pages\n * Router cannot handle. When the prefetch target matches the filter, the\n * Pages Router records the route on `this.components` with\n * `{ __appRouter: true }`. The Next.js deploy test\n * test/e2e/app-dir/app/index.test.ts → \"should successfully detect app\n * route during prefetch\"\n * reads this through `window.next.router.components[\"/dashboard\"]`.\n *\n * Vinext does not need a bloom filter — the App Router prefetch route\n * manifest (`__VINEXT_LINK_PREFETCH_ROUTES__`) already lives on the client\n * for Link's App Router auto-prefetch decisions. This helper reuses that\n * manifest and the shared trie matcher to decide whether a prefetch target\n * is an App Router route.\n *\n * Lives in `shims/internal/` so both the Pages Router (`router.ts`) and the\n * Link shim's Pages-mode branch (`link.tsx`) can call it without pulling in\n * the other shim at module init.\n *\n * The components map is stored behind a `Symbol.for` global so the Pages\n * Router (`router.ts`) and the Link shim (`link.tsx`) both write through the\n * same instance even when Vite loads the router shim through a different\n * resolved module ID than the link shim (mirrors the same module-split\n * mitigation used by `navigation.ts`'s GLOBAL_ACCESSORS_KEY).\n *\n * Issue: https://github.com/cloudflare/vinext/issues/1526\n */\nimport type { VinextLinkPrefetchRoute } from \"../../client/vinext-next-data.js\";\nimport { createRouteTrieCache, matchRouteWithTrie } from \"../../routing/route-matching.js\";\nimport { stripBasePath } from \"../../utils/base-path.js\";\n\nconst appRouteTrieCache = createRouteTrieCache<VinextLinkPrefetchRoute>();\n\ndeclare global {\n // oxlint-disable-next-line typescript-eslint/consistent-type-definitions\n interface Window {\n __VINEXT_LINK_PREFETCH_ROUTES__?: VinextLinkPrefetchRoute[];\n }\n}\n\n/**\n * Pages Router `components` map shape. Next.js types this loosely (route\n * pattern → `PrivateRouteInfo`), but for the App Router-detected case it\n * stores `{ __appRouter: true }` as a marker (see Next.js source link above).\n * Vinext only writes the marker variant; reads are by test code that checks\n * for `__appRouter: true`.\n */\ntype PagesRouterComponentsMap = Record<string, { __appRouter: true } | Record<string, unknown>>;\n\nconst _COMPONENTS_KEY = Symbol.for(\"vinext.pagesRouter.components\");\ntype _GlobalWithComponents = typeof globalThis & {\n [_COMPONENTS_KEY]?: PagesRouterComponentsMap;\n};\n\n/**\n * Get-or-create the Pages Router `components` map. Returns the same object\n * on every call so `router.components` and `window.next.router.components`\n * have referential identity.\n */\nexport function getPagesRouterComponentsMap(): PagesRouterComponentsMap {\n const globalState = globalThis as _GlobalWithComponents;\n let components = globalState[_COMPONENTS_KEY];\n if (!components) {\n components = {};\n globalState[_COMPONENTS_KEY] = components;\n }\n return components;\n}\n\n/**\n * Resolve a prefetch href to a same-origin pathname (basePath-stripped),\n * suitable as the key used by Next.js for `router.components[urlPathname]`.\n *\n * Returns null for external URLs, malformed URLs, or non-browser contexts.\n */\nfunction resolveSameOriginPathname(href: string, basePath: string): string | null {\n if (typeof window === \"undefined\") return null;\n let url: URL;\n try {\n url = new URL(href, window.location.href);\n } catch {\n return null;\n }\n if (url.origin !== window.location.origin) return null;\n return stripBasePath(url.pathname, basePath);\n}\n\n/**\n * Returns true when the prefetch href matches any route in the App Router\n * prefetch manifest (static or dynamic). Returns false when the manifest is\n * absent (Pages-Router-only build), the URL is external, or no route matches.\n */\nfunction matchesAppRoute(href: string, basePath: string): boolean {\n if (typeof window === \"undefined\") return false;\n const routes = window.__VINEXT_LINK_PREFETCH_ROUTES__;\n if (!routes || routes.length === 0) return false;\n\n const pathname = resolveSameOriginPathname(href, basePath);\n if (pathname === null) return false;\n\n return matchRouteWithTrie(pathname, routes, appRouteTrieCache) !== null;\n}\n\n/**\n * Record `components[pathname] = { __appRouter: true }` on the shared\n * Pages Router map when the href matches an App Router route. No-op when the\n * manifest is absent, the URL is external, or no app route matches.\n *\n * `pathname` is the basePath-stripped path — matching Next.js's\n * `router.components[urlPathname]` key (see the source link in this file's\n * leading comment).\n */\nexport function markAppRouteDetectedOnPrefetch(href: string, basePath: string): void {\n if (typeof window === \"undefined\") return;\n if (!matchesAppRoute(href, basePath)) return;\n\n const pathname = resolveSameOriginPathname(href, basePath);\n if (pathname === null) return;\n\n getPagesRouterComponentsMap()[pathname] = { __appRouter: true };\n}\n"],"mappings":";;;AAwCA,MAAM,oBAAoB,sBAA+C;AAkBzE,MAAM,kBAAkB,OAAO,IAAI,gCAAgC;;;;;;AAUnE,SAAgB,8BAAwD;CACtE,MAAM,cAAc;CACpB,IAAI,aAAa,YAAY;CAC7B,IAAI,CAAC,YAAY;EACf,aAAa,EAAE;EACf,YAAY,mBAAmB;;CAEjC,OAAO;;;;;;;;AAST,SAAS,0BAA0B,MAAc,UAAiC;CAChF,IAAI,OAAO,WAAW,aAAa,OAAO;CAC1C,IAAI;CACJ,IAAI;EACF,MAAM,IAAI,IAAI,MAAM,OAAO,SAAS,KAAK;SACnC;EACN,OAAO;;CAET,IAAI,IAAI,WAAW,OAAO,SAAS,QAAQ,OAAO;CAClD,OAAO,cAAc,IAAI,UAAU,SAAS;;;;;;;AAQ9C,SAAS,gBAAgB,MAAc,UAA2B;CAChE,IAAI,OAAO,WAAW,aAAa,OAAO;CAC1C,MAAM,SAAS,OAAO;CACtB,IAAI,CAAC,UAAU,OAAO,WAAW,GAAG,OAAO;CAE3C,MAAM,WAAW,0BAA0B,MAAM,SAAS;CAC1D,IAAI,aAAa,MAAM,OAAO;CAE9B,OAAO,mBAAmB,UAAU,QAAQ,kBAAkB,KAAK;;;;;;;;;;;AAYrE,SAAgB,+BAA+B,MAAc,UAAwB;CACnF,IAAI,OAAO,WAAW,aAAa;CACnC,IAAI,CAAC,gBAAgB,MAAM,SAAS,EAAE;CAEtC,MAAM,WAAW,0BAA0B,MAAM,SAAS;CAC1D,IAAI,aAAa,MAAM;CAEvB,6BAA6B,CAAC,YAAY,EAAE,aAAa,MAAM"}
@@ -0,0 +1,56 @@
1
+ //#region src/shims/internal/pages-data-fetch-dedup.d.ts
2
+ /**
3
+ * In-flight request dedup for the Pages Router `/_next/data/<id>/<page>.json`
4
+ * endpoint.
5
+ *
6
+ * Why this exists: when a user (or app code) triggers several near-simultaneous
7
+ * navigations to the same gSSP route — e.g. clicking the same `<Link>` multiple
8
+ * times before the first navigation lands — each call to `Router.push` would
9
+ * otherwise enter its own `navigateClientData()` flow and dispatch its own
10
+ * `fetch()` against the data endpoint. That balloons server load and breaks
11
+ * Next.js' documented "one fetch per unique data URL" guarantee.
12
+ *
13
+ * Ported from Next.js: `fetchNextData()` in
14
+ * `packages/next/src/shared/lib/router/router.ts`. Next.js maintains an
15
+ * `inflightCache` (keyed by the resolved data URL) and reuses the existing
16
+ * Promise when a concurrent caller asks for the same URL. The entry is
17
+ * dropped once the fetch settles (success or rejection) so the next
18
+ * navigation re-fetches fresh.
19
+ *
20
+ * Design notes:
21
+ *
22
+ * - Callers receive a cloned Response, so each can independently consume the
23
+ * body (`.json()`, `.text()`, etc.). The originating Response is never read
24
+ * directly by anyone, which keeps subsequent clones legal even after one
25
+ * caller has consumed its copy.
26
+ *
27
+ * - No `AbortSignal` is honored at the shared layer. Each `Router.push` cycle
28
+ * has its own AbortController that supersedes prior navigations via
29
+ * `_navigationId`; aborting the shared fetch on behalf of one caller would
30
+ * destroy the dedup gain for every other concurrent caller. Cancellation is
31
+ * handled by the caller's `assertStillCurrent()` checkpoints after `await`,
32
+ * not by abort propagation.
33
+ *
34
+ * - The map is module-scoped (one per realm). The Pages Router runs in the
35
+ * browser only, so a single `Map` is sufficient.
36
+ */
37
+ /**
38
+ * Dedupe a `fetch()` against the `_next/data` endpoint. Multiple concurrent
39
+ * callers for the same `dataHref` share one underlying network request.
40
+ *
41
+ * Each call returns a freshly-cloned `Response` so consumers can read the
42
+ * body independently. Once the in-flight Promise settles (resolve or reject)
43
+ * the entry is removed, and the next call will hit the network again.
44
+ *
45
+ * Errors propagate to every concurrent caller — the in-flight entry is
46
+ * dropped on failure so the next navigation can retry.
47
+ */
48
+ declare function dedupedPagesDataFetch(dataHref: string, init?: RequestInit): Promise<Response>;
49
+ /**
50
+ * Drop every cached in-flight entry. Intended for tests; production code
51
+ * does not need to call this because entries self-evict on settle.
52
+ */
53
+ declare function clearPagesDataInflight(): void;
54
+ //#endregion
55
+ export { clearPagesDataInflight, dedupedPagesDataFetch };
56
+ //# sourceMappingURL=pages-data-fetch-dedup.d.ts.map
@@ -0,0 +1,70 @@
1
+ //#region src/shims/internal/pages-data-fetch-dedup.ts
2
+ /**
3
+ * In-flight request dedup for the Pages Router `/_next/data/<id>/<page>.json`
4
+ * endpoint.
5
+ *
6
+ * Why this exists: when a user (or app code) triggers several near-simultaneous
7
+ * navigations to the same gSSP route — e.g. clicking the same `<Link>` multiple
8
+ * times before the first navigation lands — each call to `Router.push` would
9
+ * otherwise enter its own `navigateClientData()` flow and dispatch its own
10
+ * `fetch()` against the data endpoint. That balloons server load and breaks
11
+ * Next.js' documented "one fetch per unique data URL" guarantee.
12
+ *
13
+ * Ported from Next.js: `fetchNextData()` in
14
+ * `packages/next/src/shared/lib/router/router.ts`. Next.js maintains an
15
+ * `inflightCache` (keyed by the resolved data URL) and reuses the existing
16
+ * Promise when a concurrent caller asks for the same URL. The entry is
17
+ * dropped once the fetch settles (success or rejection) so the next
18
+ * navigation re-fetches fresh.
19
+ *
20
+ * Design notes:
21
+ *
22
+ * - Callers receive a cloned Response, so each can independently consume the
23
+ * body (`.json()`, `.text()`, etc.). The originating Response is never read
24
+ * directly by anyone, which keeps subsequent clones legal even after one
25
+ * caller has consumed its copy.
26
+ *
27
+ * - No `AbortSignal` is honored at the shared layer. Each `Router.push` cycle
28
+ * has its own AbortController that supersedes prior navigations via
29
+ * `_navigationId`; aborting the shared fetch on behalf of one caller would
30
+ * destroy the dedup gain for every other concurrent caller. Cancellation is
31
+ * handled by the caller's `assertStillCurrent()` checkpoints after `await`,
32
+ * not by abort propagation.
33
+ *
34
+ * - The map is module-scoped (one per realm). The Pages Router runs in the
35
+ * browser only, so a single `Map` is sufficient.
36
+ */
37
+ /** Inflight fetch promises keyed by the resolved data URL. */
38
+ const inflight = /* @__PURE__ */ new Map();
39
+ /**
40
+ * Dedupe a `fetch()` against the `_next/data` endpoint. Multiple concurrent
41
+ * callers for the same `dataHref` share one underlying network request.
42
+ *
43
+ * Each call returns a freshly-cloned `Response` so consumers can read the
44
+ * body independently. Once the in-flight Promise settles (resolve or reject)
45
+ * the entry is removed, and the next call will hit the network again.
46
+ *
47
+ * Errors propagate to every concurrent caller — the in-flight entry is
48
+ * dropped on failure so the next navigation can retry.
49
+ */
50
+ function dedupedPagesDataFetch(dataHref, init) {
51
+ let entry = inflight.get(dataHref);
52
+ if (!entry) {
53
+ entry = fetch(dataHref, init).finally(() => {
54
+ if (inflight.get(dataHref) === entry) inflight.delete(dataHref);
55
+ });
56
+ inflight.set(dataHref, entry);
57
+ }
58
+ return entry.then((res) => res.clone());
59
+ }
60
+ /**
61
+ * Drop every cached in-flight entry. Intended for tests; production code
62
+ * does not need to call this because entries self-evict on settle.
63
+ */
64
+ function clearPagesDataInflight() {
65
+ inflight.clear();
66
+ }
67
+ //#endregion
68
+ export { clearPagesDataInflight, dedupedPagesDataFetch };
69
+
70
+ //# sourceMappingURL=pages-data-fetch-dedup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pages-data-fetch-dedup.js","names":[],"sources":["../../../src/shims/internal/pages-data-fetch-dedup.ts"],"sourcesContent":["/**\n * In-flight request dedup for the Pages Router `/_next/data/<id>/<page>.json`\n * endpoint.\n *\n * Why this exists: when a user (or app code) triggers several near-simultaneous\n * navigations to the same gSSP route — e.g. clicking the same `<Link>` multiple\n * times before the first navigation lands — each call to `Router.push` would\n * otherwise enter its own `navigateClientData()` flow and dispatch its own\n * `fetch()` against the data endpoint. That balloons server load and breaks\n * Next.js' documented \"one fetch per unique data URL\" guarantee.\n *\n * Ported from Next.js: `fetchNextData()` in\n * `packages/next/src/shared/lib/router/router.ts`. Next.js maintains an\n * `inflightCache` (keyed by the resolved data URL) and reuses the existing\n * Promise when a concurrent caller asks for the same URL. The entry is\n * dropped once the fetch settles (success or rejection) so the next\n * navigation re-fetches fresh.\n *\n * Design notes:\n *\n * - Callers receive a cloned Response, so each can independently consume the\n * body (`.json()`, `.text()`, etc.). The originating Response is never read\n * directly by anyone, which keeps subsequent clones legal even after one\n * caller has consumed its copy.\n *\n * - No `AbortSignal` is honored at the shared layer. Each `Router.push` cycle\n * has its own AbortController that supersedes prior navigations via\n * `_navigationId`; aborting the shared fetch on behalf of one caller would\n * destroy the dedup gain for every other concurrent caller. Cancellation is\n * handled by the caller's `assertStillCurrent()` checkpoints after `await`,\n * not by abort propagation.\n *\n * - The map is module-scoped (one per realm). The Pages Router runs in the\n * browser only, so a single `Map` is sufficient.\n */\n\n/** Inflight fetch promises keyed by the resolved data URL. */\nconst inflight = new Map<string, Promise<Response>>();\n\n/**\n * Dedupe a `fetch()` against the `_next/data` endpoint. Multiple concurrent\n * callers for the same `dataHref` share one underlying network request.\n *\n * Each call returns a freshly-cloned `Response` so consumers can read the\n * body independently. Once the in-flight Promise settles (resolve or reject)\n * the entry is removed, and the next call will hit the network again.\n *\n * Errors propagate to every concurrent caller — the in-flight entry is\n * dropped on failure so the next navigation can retry.\n */\nexport function dedupedPagesDataFetch(dataHref: string, init?: RequestInit): Promise<Response> {\n let entry = inflight.get(dataHref);\n if (!entry) {\n entry = fetch(dataHref, init).finally(() => {\n // Only drop the entry if it still matches the one we set. A racing\n // caller could in principle overwrite, but inflight is keyed by URL\n // and we never overwrite on a hit, so this check is defensive.\n if (inflight.get(dataHref) === entry) inflight.delete(dataHref);\n });\n inflight.set(dataHref, entry);\n }\n // Always return a clone so each consumer gets an independently-readable\n // body. The original `entry` Response is never consumed directly, so\n // cloning remains valid for every caller (including the first).\n return entry.then((res) => res.clone());\n}\n\n/**\n * Drop every cached in-flight entry. Intended for tests; production code\n * does not need to call this because entries self-evict on settle.\n */\nexport function clearPagesDataInflight(): void {\n inflight.clear();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,MAAM,2BAAW,IAAI,KAAgC;;;;;;;;;;;;AAarD,SAAgB,sBAAsB,UAAkB,MAAuC;CAC7F,IAAI,QAAQ,SAAS,IAAI,SAAS;CAClC,IAAI,CAAC,OAAO;EACV,QAAQ,MAAM,UAAU,KAAK,CAAC,cAAc;GAI1C,IAAI,SAAS,IAAI,SAAS,KAAK,OAAO,SAAS,OAAO,SAAS;IAC/D;EACF,SAAS,IAAI,UAAU,MAAM;;CAK/B,OAAO,MAAM,MAAM,QAAQ,IAAI,OAAO,CAAC;;;;;;AAOzC,SAAgB,yBAA+B;CAC7C,SAAS,OAAO"}
@@ -21,7 +21,15 @@ type LinkProps = {
21
21
  * is upgraded to a full prefetch when the user shows navigation intent.
22
22
  */
23
23
  unstable_dynamicOnHover?: boolean; /** Whether to pass the href to the child element */
24
- passHref?: boolean; /** Scroll to top on navigation (default: true) */
24
+ passHref?: boolean;
25
+ /**
26
+ * Pre-Next.js-13 link behaviour. When true, <Link> expects its child to be
27
+ * an `<a>` (or a component that renders one) and forwards `href`, click,
28
+ * and prefetch handlers to the child via `React.cloneElement` instead of
29
+ * rendering its own wrapping `<a>`. Required when the user wants to
30
+ * style/instrument the anchor themselves.
31
+ */
32
+ legacyBehavior?: boolean; /** Scroll to top on navigation (default: true) */
25
33
  scroll?: boolean;
26
34
  /**
27
35
  * Pages Router: update the URL without re-running data fetching methods
@@ -68,7 +76,15 @@ declare const Link: React.ForwardRefExoticComponent<{
68
76
  * is upgraded to a full prefetch when the user shows navigation intent.
69
77
  */
70
78
  unstable_dynamicOnHover?: boolean; /** Whether to pass the href to the child element */
71
- passHref?: boolean; /** Scroll to top on navigation (default: true) */
79
+ passHref?: boolean;
80
+ /**
81
+ * Pre-Next.js-13 link behaviour. When true, <Link> expects its child to be
82
+ * an `<a>` (or a component that renders one) and forwards `href`, click,
83
+ * and prefetch handlers to the child via `React.cloneElement` instead of
84
+ * rendering its own wrapping `<a>`. Required when the user wants to
85
+ * style/instrument the anchor themselves.
86
+ */
87
+ legacyBehavior?: boolean; /** Scroll to top on navigation (default: true) */
72
88
  scroll?: boolean;
73
89
  /**
74
90
  * Pages Router: update the URL without re-running data fetching methods
@@ -3,15 +3,16 @@ 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
5
  import { isDangerousScheme, reportBlockedDangerousNavigation } from "./url-safety.js";
6
- import { appendSearchParamsToUrl, urlQueryToSearchParams } from "../utils/query.js";
7
6
  import { AppElementsWire } from "../server/app-elements-wire.js";
8
7
  import { APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL } from "../server/app-rsc-render-mode.js";
8
+ import "../server/app-elements.js";
9
9
  import { addLocalePrefix, getDomainLocaleUrl } from "../utils/domain-locale.js";
10
10
  import { prefetchPagesData, resolvePagesDataNavigationTarget } from "./internal/pages-data-target.js";
11
+ import { markAppRouteDetectedOnPrefetch } from "./internal/app-route-detection.js";
11
12
  import { isAbsoluteOrProtocolRelativeUrl, normalizePathTrailingSlash, resolveRelativeHref, toBrowserNavigationHref, toSameOriginAppPath, withBasePath } from "./url-utils.js";
13
+ import { appendSearchParamsToUrl, urlQueryToSearchParams } from "../utils/query.js";
12
14
  import { getCurrentBrowserLocale } from "./client-locale.js";
13
15
  import { getNavigationRuntime, hasAppNavigationRuntime, registerNavigationRuntimeFunctions } from "../client/navigation-runtime.js";
14
- import "../server/app-elements.js";
15
16
  import { createRscRequestHeaders, createRscRequestUrl, stripRscCacheBustingSearchParam, stripRscSuffix } from "../server/app-rsc-cache-busting.js";
16
17
  import { getMountedSlotsHeader, getPrefetchCache, getPrefetchInterceptionContext, getPrefetchedUrls, navigateClientSide, prefetchRscResponse } from "./navigation.js";
17
18
  import { navigatePagesRouterLink } from "../client/pages-router-link-navigation.js";
@@ -40,6 +41,7 @@ function useLinkStatus() {
40
41
  const __basePath = process.env.__NEXT_ROUTER_BASEPATH ?? "";
41
42
  /** trailingSlash from next.config.js, injected by the plugin at build time */
42
43
  const __trailingSlash = process.env.__VINEXT_TRAILING_SLASH === "true";
44
+ const __prefetchInlining = process.env.__VINEXT_PREFETCH_INLINING === "true";
43
45
  const linkPrefetchRouteTrieCache = createRouteTrieCache();
44
46
  function resolveHref(href) {
45
47
  if (typeof href === "string") return href;
@@ -167,7 +169,11 @@ function prefetchUrl(href, mode, priority = "low") {
167
169
  });
168
170
  if (prefetchHref == null) return;
169
171
  const fullHref = toBrowserNavigationHref(prefetchHref, window.location.href, __basePath);
170
- (window.requestIdleCallback ?? ((fn) => setTimeout(fn, 100)))(() => {
172
+ const target = new URL(fullHref, window.location.href);
173
+ if (target.origin === window.location.origin && target.pathname === window.location.pathname && target.search === window.location.search) return;
174
+ (priority === "high" ? (fn) => {
175
+ fn();
176
+ } : window.requestIdleCallback ?? ((fn) => setTimeout(fn, 100)))(() => {
171
177
  (async () => {
172
178
  if (hasAppNavigationRuntime()) {
173
179
  const autoPrefetch = mode === "auto" ? resolveAutoAppRoutePrefetch(prefetchHref) : {
@@ -195,7 +201,28 @@ function prefetchUrl(href, mode, priority = "low") {
195
201
  return;
196
202
  }
197
203
  prefetched.add(cacheKey);
198
- prefetchRscResponse(rscUrl, fetch(rscUrl, {
204
+ prefetchRscResponse(rscUrl, __prefetchInlining && autoPrefetch.cacheForNavigation ? (async () => {
205
+ const shellHeaders = createRscRequestHeaders({
206
+ interceptionContext,
207
+ renderMode: APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL
208
+ });
209
+ if (mountedSlotsHeader) shellHeaders.set(VINEXT_MOUNTED_SLOTS_HEADER, mountedSlotsHeader);
210
+ const shellRscUrl = await createRscRequestUrl(fullHref, shellHeaders);
211
+ const shellResponse = await fetch(shellRscUrl, {
212
+ headers: shellHeaders,
213
+ credentials: "include",
214
+ priority,
215
+ purpose: "prefetch"
216
+ });
217
+ if (!shellResponse.ok) return shellResponse;
218
+ await shellResponse.arrayBuffer().catch(() => {});
219
+ return fetch(rscUrl, {
220
+ headers,
221
+ credentials: "include",
222
+ priority,
223
+ purpose: "prefetch"
224
+ });
225
+ })() : fetch(rscUrl, {
199
226
  headers,
200
227
  credentials: "include",
201
228
  priority,
@@ -208,6 +235,7 @@ function prefetchUrl(href, mode, priority = "low") {
208
235
  const dataTarget = resolvePagesDataNavigationTarget(fullHref, __basePath);
209
236
  if (dataTarget) prefetchPagesData(dataTarget);
210
237
  else {
238
+ markAppRouteDetectedOnPrefetch(fullHref, __basePath);
211
239
  const link = document.createElement("link");
212
240
  link.rel = "prefetch";
213
241
  link.href = fullHref;
@@ -334,8 +362,10 @@ function applyLocaleToHref(href, locale) {
334
362
  }
335
363
  return addLocalePrefix(href, resolvedLocale, defaultLocale);
336
364
  }
337
- 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) {
365
+ const Link = forwardRef(function Link({ href, as, replace = false, prefetch: prefetchProp, scroll = true, shallow = false, children: childrenProp, onClick, onMouseEnter, onTouchStart, onNavigate, unstable_dynamicOnHover = false, legacyBehavior = false, passHref = false, ...rest }, forwardedRef) {
338
366
  const { locale, ...restWithoutLocale } = rest;
367
+ let children = childrenProp;
368
+ if (legacyBehavior && (typeof childrenProp === "string" || typeof childrenProp === "number")) children = React.createElement("a", null, childrenProp);
339
369
  const rawResolvedHref = as ?? resolveHref(href);
340
370
  const resolvedHref = typeof rawResolvedHref === "string" ? warnAndNormalizeRepeatedSlashesInHref(rawResolvedHref) : rawResolvedHref;
341
371
  const isDangerous = typeof resolvedHref === "string" && isDangerousScheme(resolvedHref);
@@ -422,8 +452,8 @@ const Link = forwardRef(function Link({ href, as, replace = false, prefetch: pre
422
452
  onTouchStart?.(e);
423
453
  prefetchOnIntent();
424
454
  }, [onTouchStart, prefetchOnIntent]);
425
- const handleClick = async (e) => {
426
- if (onClick) onClick(e);
455
+ const handleClick = async (e, options = {}) => {
456
+ if (!options.skipLinkOnClick && onClick) onClick(e);
427
457
  if (e.defaultPrevented) return;
428
458
  if (e.currentTarget.hasAttribute("download")) return;
429
459
  if (e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
@@ -481,7 +511,7 @@ const Link = forwardRef(function Link({ href, as, replace = false, prefetch: pre
481
511
  window.dispatchEvent(new PopStateEvent("popstate"));
482
512
  }
483
513
  };
484
- const { passHref: _p, ...anchorProps } = restWithoutLocale;
514
+ const anchorProps = restWithoutLocale;
485
515
  const linkStatusValue = React.useMemo(() => ({ pending }), [pending]);
486
516
  if (isDangerous) {
487
517
  if (process.env.NODE_ENV !== "production") console.warn(`<Link> blocked dangerous href: ${resolvedHref}`);
@@ -489,6 +519,26 @@ const Link = forwardRef(function Link({ href, as, replace = false, prefetch: pre
489
519
  if (onClick) onClick(event);
490
520
  reportBlockedDangerousNavigation();
491
521
  };
522
+ if (legacyBehavior) {
523
+ const child = React.Children.only(children);
524
+ const childOnClick = child.props.onClick;
525
+ const childRef = child.props.ref;
526
+ const setDangerousRefs = (node) => {
527
+ internalRef.current = node;
528
+ if (typeof childRef === "function") childRef(node);
529
+ else if (childRef) childRef.current = node;
530
+ };
531
+ return /* @__PURE__ */ jsx(LinkStatusContext.Provider, {
532
+ value: linkStatusValue,
533
+ children: React.cloneElement(child, {
534
+ ref: setDangerousRefs,
535
+ onClick: (event) => {
536
+ if (childOnClick) childOnClick(event);
537
+ reportBlockedDangerousNavigation();
538
+ }
539
+ })
540
+ });
541
+ }
492
542
  return /* @__PURE__ */ jsx(LinkStatusContext.Provider, {
493
543
  value: linkStatusValue,
494
544
  children: /* @__PURE__ */ jsx("a", {
@@ -501,6 +551,46 @@ const Link = forwardRef(function Link({ href, as, replace = false, prefetch: pre
501
551
  })
502
552
  });
503
553
  }
554
+ if (legacyBehavior) {
555
+ const child = React.Children.only(children);
556
+ if (process.env.NODE_ENV !== "production") {
557
+ if (onClick) console.warn(`"onClick" was passed to <Link> with \`href\` of \`${resolveHref(href)}\` but "legacyBehavior" was set. The legacy behavior requires onClick be set on the child of next/link`);
558
+ if (onMouseEnter) console.warn(`"onMouseEnter" was passed to <Link> with \`href\` of \`${resolveHref(href)}\` but "legacyBehavior" was set. The legacy behavior requires onMouseEnter be set on the child of next/link`);
559
+ }
560
+ const childPropsExisting = child.props;
561
+ const childHasOwnHref = child.type === "a" ? "href" in childPropsExisting : false;
562
+ const shouldForwardHref = passHref || child.type === "a" && !childHasOwnHref;
563
+ const childOnClick = childPropsExisting.onClick;
564
+ const childOnMouseEnter = childPropsExisting.onMouseEnter;
565
+ const childOnTouchStart = childPropsExisting.onTouchStart;
566
+ const childRef = childPropsExisting.ref;
567
+ const setLegacyRefs = (node) => {
568
+ internalRef.current = node;
569
+ if (typeof childRef === "function") childRef(node);
570
+ else if (childRef) childRef.current = node;
571
+ };
572
+ const clonedProps = {
573
+ ref: setLegacyRefs,
574
+ onClick: (event) => {
575
+ if (childOnClick) childOnClick(event);
576
+ if (event.defaultPrevented) return;
577
+ handleClick(event, { skipLinkOnClick: true });
578
+ },
579
+ onMouseEnter: (event) => {
580
+ if (childOnMouseEnter) childOnMouseEnter(event);
581
+ prefetchOnIntent();
582
+ },
583
+ onTouchStart: (event) => {
584
+ if (childOnTouchStart) childOnTouchStart(event);
585
+ prefetchOnIntent();
586
+ }
587
+ };
588
+ if (shouldForwardHref) clonedProps.href = fullHref;
589
+ return /* @__PURE__ */ jsx(LinkStatusContext.Provider, {
590
+ value: linkStatusValue,
591
+ children: React.cloneElement(child, clonedProps)
592
+ });
593
+ }
504
594
  return /* @__PURE__ */ jsx(LinkStatusContext.Provider, {
505
595
  value: linkStatusValue,
506
596
  children: /* @__PURE__ */ jsx("a", {