vinext 0.1.2 → 0.1.3
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/prerender.d.ts +9 -1
- package/dist/build/prerender.js +41 -12
- package/dist/build/run-prerender.d.ts +10 -2
- package/dist/build/run-prerender.js +15 -1
- package/dist/client/app-nav-failure-handler.d.ts +8 -0
- package/dist/client/app-nav-failure-handler.js +44 -0
- package/dist/client/vinext-next-data.d.ts +18 -1
- package/dist/client/window-next.d.ts +2 -1
- package/dist/client/window-next.js +12 -1
- package/dist/cloudflare/src/cache/cdn-adapter.runtime.js +6 -1
- package/dist/config/config-matchers.js +73 -14
- package/dist/config/next-config.d.ts +46 -4
- package/dist/config/next-config.js +147 -48
- package/dist/deploy.d.ts +30 -11
- package/dist/deploy.js +180 -99
- package/dist/entries/app-browser-entry.d.ts +9 -3
- package/dist/entries/app-browser-entry.js +21 -3
- package/dist/entries/app-rsc-entry.d.ts +2 -0
- package/dist/entries/app-rsc-entry.js +64 -5
- package/dist/entries/app-rsc-manifest.js +2 -0
- package/dist/entries/app-ssr-entry.js +1 -1
- package/dist/entries/pages-client-entry.js +53 -8
- package/dist/entries/pages-server-entry.js +41 -5
- package/dist/index.js +200 -62
- package/dist/plugins/extensionless-dynamic-import.d.ts +6 -0
- package/dist/plugins/extensionless-dynamic-import.js +152 -0
- package/dist/plugins/optimize-imports.d.ts +2 -1
- package/dist/plugins/optimize-imports.js +11 -9
- package/dist/plugins/postcss.js +7 -7
- package/dist/plugins/typeof-window.d.ts +14 -0
- package/dist/plugins/typeof-window.js +150 -0
- package/dist/routing/app-route-graph.d.ts +2 -1
- package/dist/routing/app-route-graph.js +44 -14
- package/dist/routing/file-matcher.d.ts +10 -1
- package/dist/routing/file-matcher.js +22 -1
- package/dist/routing/pages-router.js +3 -3
- package/dist/routing/utils.d.ts +35 -6
- package/dist/routing/utils.js +59 -7
- package/dist/server/api-handler.d.ts +6 -1
- package/dist/server/api-handler.js +21 -15
- package/dist/server/app-browser-action-result.d.ts +19 -6
- package/dist/server/app-browser-action-result.js +19 -10
- package/dist/server/app-browser-entry.js +167 -90
- package/dist/server/app-browser-error.d.ts +10 -6
- package/dist/server/app-browser-error.js +43 -8
- package/dist/server/app-browser-hydration.d.ts +2 -0
- package/dist/server/app-browser-hydration.js +1 -0
- package/dist/server/app-browser-navigation-controller.d.ts +4 -2
- package/dist/server/app-browser-navigation-controller.js +23 -2
- package/dist/server/app-browser-server-action-navigation.d.ts +6 -0
- package/dist/server/app-browser-server-action-navigation.js +9 -0
- package/dist/server/app-browser-stream.js +86 -43
- package/dist/server/app-elements-wire.d.ts +6 -1
- package/dist/server/app-elements-wire.js +14 -4
- package/dist/server/app-elements.d.ts +2 -2
- package/dist/server/app-elements.js +2 -2
- package/dist/server/app-fallback-renderer.d.ts +1 -0
- package/dist/server/app-fallback-renderer.js +3 -1
- package/dist/server/app-optimistic-routing.js +2 -2
- package/dist/server/app-page-boundary-render.d.ts +1 -0
- package/dist/server/app-page-boundary-render.js +27 -14
- package/dist/server/app-page-cache-render.d.ts +53 -0
- package/dist/server/app-page-cache-render.js +91 -0
- package/dist/server/app-page-cache.d.ts +16 -2
- package/dist/server/app-page-cache.js +62 -1
- package/dist/server/app-page-dispatch.d.ts +26 -0
- package/dist/server/app-page-dispatch.js +149 -92
- package/dist/server/app-page-element-builder.d.ts +1 -0
- package/dist/server/app-page-element-builder.js +5 -2
- package/dist/server/app-page-execution.d.ts +6 -1
- package/dist/server/app-page-execution.js +21 -1
- package/dist/server/app-page-probe.d.ts +1 -0
- package/dist/server/app-page-probe.js +4 -0
- package/dist/server/app-page-render-observation.d.ts +3 -1
- package/dist/server/app-page-render-observation.js +17 -1
- package/dist/server/app-page-render.d.ts +12 -1
- package/dist/server/app-page-render.js +42 -4
- package/dist/server/app-page-request.d.ts +2 -0
- package/dist/server/app-page-request.js +2 -1
- package/dist/server/app-page-route-wiring.d.ts +3 -1
- package/dist/server/app-page-route-wiring.js +14 -5
- package/dist/server/app-page-stream.d.ts +15 -3
- package/dist/server/app-page-stream.js +11 -5
- package/dist/server/app-pages-bridge.d.ts +18 -0
- package/dist/server/app-pages-bridge.js +22 -5
- package/dist/server/app-ppr-fallback-shell-render.d.ts +17 -0
- package/dist/server/app-ppr-fallback-shell-render.js +26 -0
- package/dist/server/app-ppr-fallback-shell.d.ts +13 -1
- package/dist/server/app-ppr-fallback-shell.js +8 -1
- package/dist/server/app-route-handler-dispatch.js +9 -2
- package/dist/server/app-route-handler-policy.d.ts +1 -0
- package/dist/server/app-router-entry.js +5 -0
- package/dist/server/app-rsc-cache-busting.js +2 -0
- package/dist/server/app-rsc-handler.d.ts +25 -0
- package/dist/server/app-rsc-handler.js +154 -54
- package/dist/server/app-rsc-route-matching.d.ts +3 -0
- package/dist/server/app-rsc-route-matching.js +2 -0
- package/dist/server/app-segment-config.d.ts +9 -1
- package/dist/server/app-segment-config.js +12 -3
- package/dist/server/app-server-action-execution.d.ts +1 -0
- package/dist/server/app-server-action-execution.js +42 -13
- package/dist/server/app-ssr-entry.d.ts +2 -0
- package/dist/server/app-ssr-entry.js +83 -10
- package/dist/server/cache-control.js +4 -0
- package/dist/server/dev-server.d.ts +2 -2
- package/dist/server/dev-server.js +244 -51
- package/dist/server/hybrid-route-priority.d.ts +22 -0
- package/dist/server/hybrid-route-priority.js +33 -0
- package/dist/server/image-optimization.d.ts +18 -9
- package/dist/server/image-optimization.js +37 -23
- package/dist/server/implicit-tags.d.ts +2 -1
- package/dist/server/implicit-tags.js +4 -1
- package/dist/server/navigation-planner.d.ts +133 -30
- package/dist/server/navigation-planner.js +114 -0
- package/dist/server/navigation-trace.d.ts +8 -1
- package/dist/server/navigation-trace.js +8 -1
- package/dist/server/pages-api-route.d.ts +6 -0
- package/dist/server/pages-api-route.js +13 -2
- package/dist/server/pages-asset-tags.d.ts +2 -1
- package/dist/server/pages-asset-tags.js +6 -2
- package/dist/server/pages-data-route.d.ts +8 -1
- package/dist/server/pages-data-route.js +11 -2
- package/dist/server/pages-get-initial-props.d.ts +54 -4
- package/dist/server/pages-get-initial-props.js +43 -1
- package/dist/server/pages-node-compat.js +2 -2
- package/dist/server/pages-page-data.d.ts +11 -2
- package/dist/server/pages-page-data.js +204 -33
- package/dist/server/pages-page-handler.d.ts +4 -2
- package/dist/server/pages-page-handler.js +59 -22
- package/dist/server/pages-page-response.d.ts +2 -1
- package/dist/server/pages-page-response.js +7 -4
- package/dist/server/pages-request-pipeline.d.ts +1 -0
- package/dist/server/pages-request-pipeline.js +73 -36
- package/dist/server/pregenerated-concrete-paths.d.ts +1 -17
- package/dist/server/pregenerated-concrete-paths.js +2 -19
- package/dist/server/prerender-manifest.d.ts +33 -0
- package/dist/server/prerender-manifest.js +54 -0
- package/dist/server/prerender-route-params.d.ts +1 -2
- package/dist/server/prod-server.js +9 -3
- package/dist/server/request-pipeline.d.ts +3 -15
- package/dist/server/request-pipeline.js +58 -47
- package/dist/server/rsc-stream-hints.d.ts +5 -1
- package/dist/server/rsc-stream-hints.js +6 -1
- package/dist/server/seed-cache.js +10 -18
- package/dist/shims/app-router-scroll-state.d.ts +3 -1
- package/dist/shims/app-router-scroll-state.js +14 -2
- package/dist/shims/app-router-scroll.d.ts +3 -0
- package/dist/shims/app-router-scroll.js +28 -18
- package/dist/shims/cache-runtime.js +3 -2
- package/dist/shims/cache.d.ts +1 -0
- package/dist/shims/cache.js +1 -1
- package/dist/shims/cdn-cache.d.ts +5 -5
- package/dist/shims/dynamic-preload-chunks.js +6 -4
- package/dist/shims/error-boundary.d.ts +2 -0
- package/dist/shims/error-boundary.js +7 -0
- package/dist/shims/error.js +3 -2
- package/dist/shims/error.react-server.d.ts +9 -0
- package/dist/shims/error.react-server.js +6 -0
- package/dist/shims/fetch-cache.d.ts +3 -1
- package/dist/shims/fetch-cache.js +45 -20
- package/dist/shims/hash-scroll.js +6 -1
- package/dist/shims/headers.js +29 -4
- package/dist/shims/internal/als-registry.js +28 -1
- package/dist/shims/internal/app-route-detection.js +8 -17
- package/dist/shims/internal/hybrid-client-route-owner.d.ts +31 -0
- package/dist/shims/internal/hybrid-client-route-owner.js +143 -0
- package/dist/shims/internal/navigation-untracked.d.ts +35 -0
- package/dist/shims/internal/navigation-untracked.js +55 -0
- package/dist/shims/internal/pages-data-target.d.ts +7 -2
- package/dist/shims/internal/pages-data-target.js +17 -8
- package/dist/shims/internal/pages-router-accessor.d.ts +19 -0
- package/dist/shims/internal/pages-router-accessor.js +13 -0
- package/dist/shims/internal/router-context.d.ts +2 -1
- package/dist/shims/internal/router-context.js +3 -1
- package/dist/shims/link.js +12 -5
- package/dist/shims/navigation.d.ts +8 -2
- package/dist/shims/navigation.js +61 -31
- package/dist/shims/ppr-fallback-shell.d.ts +5 -1
- package/dist/shims/ppr-fallback-shell.js +28 -7
- package/dist/shims/router.d.ts +13 -2
- package/dist/shims/router.js +419 -128
- package/dist/shims/server.d.ts +16 -1
- package/dist/shims/server.js +44 -12
- package/dist/shims/unified-request-context.js +1 -0
- package/dist/utils/built-asset-url.d.ts +4 -0
- package/dist/utils/built-asset-url.js +11 -0
- package/dist/utils/commonjs-loader.d.ts +16 -0
- package/dist/utils/commonjs-loader.js +100 -0
- package/dist/utils/deployment-id.d.ts +8 -0
- package/dist/utils/deployment-id.js +22 -0
- package/dist/utils/html-limited-bots.d.ts +18 -1
- package/dist/utils/html-limited-bots.js +23 -1
- package/dist/utils/parse-cookie.d.ts +13 -0
- package/dist/utils/parse-cookie.js +52 -0
- package/dist/utils/path.d.ts +7 -1
- package/dist/utils/path.js +9 -1
- package/package.json +2 -2
- package/dist/shims/internal/parse-cookie-header.d.ts +0 -14
- package/dist/shims/internal/parse-cookie-header.js +0 -30
package/dist/shims/router.js
CHANGED
|
@@ -9,16 +9,18 @@ import { applyVinextLocaleGlobals, extractVinextNextDataJson, parseVinextNextDat
|
|
|
9
9
|
import { isValidModulePath } from "../client/validate-module-path.js";
|
|
10
10
|
import { addLocalePrefix, getDomainLocaleUrl, getLocalePathPrefix } from "../utils/domain-locale.js";
|
|
11
11
|
import { buildPagesDataHref } from "./internal/pages-data-url.js";
|
|
12
|
+
import { dedupedPagesDataFetch } from "./internal/pages-data-fetch-dedup.js";
|
|
13
|
+
import { NEXT_DEPLOYMENT_ID_HEADER, getDeploymentId } from "../utils/deployment-id.js";
|
|
12
14
|
import { prefetchPagesData, resolvePagesDataNavigationTarget } from "./internal/pages-data-target.js";
|
|
15
|
+
import { addQueryParam, appendSearchParamsToUrl, mergeRouteParamsIntoQuery, parseQueryString, urlQueryToSearchParams } from "../utils/query.js";
|
|
16
|
+
import { resolveHybridClientRouteOwner } from "./internal/hybrid-client-route-owner.js";
|
|
13
17
|
import { getPagesRouterComponentsMap, markAppRouteDetectedOnPrefetch } from "./internal/app-route-detection.js";
|
|
14
|
-
import { dedupedPagesDataFetch } from "./internal/pages-data-fetch-dedup.js";
|
|
15
18
|
import { installWindowNext } from "../client/window-next.js";
|
|
16
19
|
import { getWindowOrigin, isAbsoluteOrProtocolRelativeUrl, isHashOnlyBrowserUrlChange, normalizePathTrailingSlash, toBrowserNavigationHref, toSameOriginAppPath, withBasePath } from "./url-utils.js";
|
|
17
|
-
import { addQueryParam, appendSearchParamsToUrl, mergeRouteParamsIntoQuery, parseQueryString, urlQueryToSearchParams } from "../utils/query.js";
|
|
18
20
|
import { scrollToHashTarget } from "./hash-scroll.js";
|
|
19
|
-
import { setPagesRouterPopStateHandler, setStampInitialHistoryState } from "./pages-router-runtime.js";
|
|
21
|
+
import { installPagesRouterRuntime, setPagesRouterPopStateHandler, setStampInitialHistoryState } from "./pages-router-runtime.js";
|
|
20
22
|
import { getCurrentBrowserLocale } from "./client-locale.js";
|
|
21
|
-
import { createElement, useContext, useEffect, useMemo, useState } from "react";
|
|
23
|
+
import { Component, Fragment, createElement, useContext, useEffect, useLayoutEffect, useMemo, useState } from "react";
|
|
22
24
|
//#region src/shims/router.ts
|
|
23
25
|
/**
|
|
24
26
|
* next/router shim
|
|
@@ -31,6 +33,134 @@ import { createElement, useContext, useEffect, useMemo, useState } from "react";
|
|
|
31
33
|
const __basePath = process.env.__NEXT_ROUTER_BASEPATH ?? "";
|
|
32
34
|
/** trailingSlash from next.config.js, injected by the plugin at build time */
|
|
33
35
|
const __trailingSlash = process.env.__VINEXT_TRAILING_SLASH === "true";
|
|
36
|
+
/** experimental.scrollRestoration from next.config.js, injected by the plugin at build time */
|
|
37
|
+
const __scrollRestoration = process.env.__NEXT_SCROLL_RESTORATION === "true";
|
|
38
|
+
const noopCommit = () => {};
|
|
39
|
+
const SCROLL_RESTORE_MAX_FRAMES = 60;
|
|
40
|
+
const SCROLL_RESTORE_TOLERANCE_PX = 1;
|
|
41
|
+
/**
|
|
42
|
+
* A version of useLayoutEffect that doesn't warn during SSR.
|
|
43
|
+
* `wrapWithRouterContext` is shared with the server-side Pages Router render
|
|
44
|
+
* path, where a raw useLayoutEffect would log React's "useLayoutEffect does
|
|
45
|
+
* nothing on the server" warning on every render. Same pattern as
|
|
46
|
+
* `shims/image.tsx`; Next.js only runs the commit callback on the client.
|
|
47
|
+
*/
|
|
48
|
+
const useNonWarningLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
|
|
49
|
+
var PagesRouterCommitBoundary = class extends Component {
|
|
50
|
+
componentDidCatch(error) {
|
|
51
|
+
this.props.onError(error);
|
|
52
|
+
}
|
|
53
|
+
render() {
|
|
54
|
+
return createElement(PagesRouterCommitBoundaryHelper, { onCommit: this.props.onCommit }, this.props.children);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
function PagesRouterCommitBoundaryHelper({ children, onCommit }) {
|
|
58
|
+
useNonWarningLayoutEffect(() => {
|
|
59
|
+
onCommit();
|
|
60
|
+
}, [onCommit]);
|
|
61
|
+
return createElement(Fragment, null, children);
|
|
62
|
+
}
|
|
63
|
+
function renderPagesRouterElement(element, scroll) {
|
|
64
|
+
const root = window.__VINEXT_ROOT__;
|
|
65
|
+
if (!root) return Promise.resolve();
|
|
66
|
+
cancelPreviousRenderCommit();
|
|
67
|
+
return new Promise((resolve, reject) => {
|
|
68
|
+
const cancel = () => {
|
|
69
|
+
if (routerRuntimeState.cancelPendingRenderCommit === cancel) routerRuntimeState.cancelPendingRenderCommit = null;
|
|
70
|
+
reject(new NavigationCancelledError("superseded"));
|
|
71
|
+
};
|
|
72
|
+
routerRuntimeState.cancelPendingRenderCommit = cancel;
|
|
73
|
+
const clearIfCurrent = () => {
|
|
74
|
+
if (routerRuntimeState.cancelPendingRenderCommit === cancel) routerRuntimeState.cancelPendingRenderCommit = null;
|
|
75
|
+
};
|
|
76
|
+
const isCurrent = () => routerRuntimeState.cancelPendingRenderCommit === cancel;
|
|
77
|
+
const scrollHandler = async () => {
|
|
78
|
+
if (scroll) await restorePagesRouterScrollPosition(scroll, isCurrent);
|
|
79
|
+
};
|
|
80
|
+
root.render(wrapWithRouterContext(element, () => {
|
|
81
|
+
(async () => {
|
|
82
|
+
if (!isCurrent()) return;
|
|
83
|
+
try {
|
|
84
|
+
await scrollHandler();
|
|
85
|
+
if (!isCurrent()) return;
|
|
86
|
+
clearIfCurrent();
|
|
87
|
+
resolve();
|
|
88
|
+
} catch (err) {
|
|
89
|
+
clearIfCurrent();
|
|
90
|
+
reject(err);
|
|
91
|
+
}
|
|
92
|
+
})();
|
|
93
|
+
}, (error) => {
|
|
94
|
+
clearIfCurrent();
|
|
95
|
+
reject(error);
|
|
96
|
+
}));
|
|
97
|
+
if (!hasBrowserDocument()) {
|
|
98
|
+
clearIfCurrent();
|
|
99
|
+
resolve();
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
function hasBrowserDocument() {
|
|
104
|
+
return typeof document !== "undefined" && document.documentElement !== void 0;
|
|
105
|
+
}
|
|
106
|
+
async function restorePagesRouterScrollPosition(scroll, shouldContinue) {
|
|
107
|
+
if (!shouldContinue()) return;
|
|
108
|
+
scrollToPagesRouterPosition(scroll);
|
|
109
|
+
if (isAtScrollPosition(scroll)) return;
|
|
110
|
+
let previousScrollPosition = getWindowScrollPosition();
|
|
111
|
+
for (let frame = 0; frame < SCROLL_RESTORE_MAX_FRAMES; frame += 1) {
|
|
112
|
+
await waitForNextAnimationFrame();
|
|
113
|
+
if (!shouldContinue()) return;
|
|
114
|
+
scrollToPagesRouterPosition(scroll);
|
|
115
|
+
if (isAtScrollPosition(scroll)) return;
|
|
116
|
+
const currentScrollPosition = getWindowScrollPosition();
|
|
117
|
+
if (currentScrollPosition.x === previousScrollPosition.x && currentScrollPosition.y === previousScrollPosition.y) break;
|
|
118
|
+
previousScrollPosition = currentScrollPosition;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function scrollToPagesRouterPosition({ x, y }) {
|
|
122
|
+
if (!hasBrowserDocument()) {
|
|
123
|
+
window.scrollTo(x, y);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const htmlElement = document.documentElement;
|
|
127
|
+
if (!(htmlElement.dataset.scrollBehavior === "smooth")) {
|
|
128
|
+
window.scrollTo(x, y);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const previousScrollBehavior = htmlElement.style.scrollBehavior;
|
|
132
|
+
htmlElement.style.scrollBehavior = "auto";
|
|
133
|
+
htmlElement.getClientRects();
|
|
134
|
+
window.scrollTo(x, y);
|
|
135
|
+
htmlElement.style.scrollBehavior = previousScrollBehavior;
|
|
136
|
+
}
|
|
137
|
+
function isAtScrollPosition({ x, y }) {
|
|
138
|
+
return Math.abs(window.scrollX - x) <= SCROLL_RESTORE_TOLERANCE_PX && Math.abs(window.scrollY - y) <= SCROLL_RESTORE_TOLERANCE_PX;
|
|
139
|
+
}
|
|
140
|
+
function waitForNextAnimationFrame() {
|
|
141
|
+
return new Promise((resolve) => {
|
|
142
|
+
if (typeof requestAnimationFrame === "function") {
|
|
143
|
+
requestAnimationFrame(() => resolve());
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
setTimeout(resolve, 16);
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
function canUseSessionStorageForScrollRestoration() {
|
|
150
|
+
if (typeof window === "undefined") return false;
|
|
151
|
+
try {
|
|
152
|
+
const key = "__next";
|
|
153
|
+
window.sessionStorage.setItem(key, key);
|
|
154
|
+
window.sessionStorage.removeItem(key);
|
|
155
|
+
return true;
|
|
156
|
+
} catch {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const manualScrollRestoration = __scrollRestoration && typeof window !== "undefined" && window.next?.appDir !== true && "scrollRestoration" in window.history && canUseSessionStorageForScrollRestoration();
|
|
161
|
+
function installManualScrollRestoration() {
|
|
162
|
+
if (manualScrollRestoration) window.history.scrollRestoration = "manual";
|
|
163
|
+
}
|
|
34
164
|
function createRouterEvents() {
|
|
35
165
|
const listeners = /* @__PURE__ */ new Map();
|
|
36
166
|
return {
|
|
@@ -46,7 +176,42 @@ function createRouterEvents() {
|
|
|
46
176
|
}
|
|
47
177
|
};
|
|
48
178
|
}
|
|
49
|
-
const
|
|
179
|
+
const PAGES_ROUTER_RUNTIME_STATE_KEY = Symbol.for("vinext.pagesRouter.runtimeState");
|
|
180
|
+
function createPagesRouterRuntimeState() {
|
|
181
|
+
return {
|
|
182
|
+
events: createRouterEvents(),
|
|
183
|
+
historyKeyCounter: 0,
|
|
184
|
+
navigationId: 0,
|
|
185
|
+
activeAbortController: null,
|
|
186
|
+
cancelPendingRenderCommit: null,
|
|
187
|
+
lastPathnameAndSearch: typeof window !== "undefined" ? window.location.pathname + window.location.search : "",
|
|
188
|
+
isFirstPopStateEvent: true,
|
|
189
|
+
routerDidNavigate: false,
|
|
190
|
+
deprecatedEventBridgeInstalled: false,
|
|
191
|
+
pagesRouterReady: typeof window === "undefined" || !shouldDeferInitialPagesRouterReady()
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
function getPagesRouterRuntimeState() {
|
|
195
|
+
if (typeof window === "undefined") return createPagesRouterRuntimeState();
|
|
196
|
+
const globalObject = window;
|
|
197
|
+
const existing = globalObject[PAGES_ROUTER_RUNTIME_STATE_KEY];
|
|
198
|
+
if (existing) return existing;
|
|
199
|
+
const state = createPagesRouterRuntimeState();
|
|
200
|
+
globalObject[PAGES_ROUTER_RUNTIME_STATE_KEY] = state;
|
|
201
|
+
return state;
|
|
202
|
+
}
|
|
203
|
+
const routerRuntimeState = getPagesRouterRuntimeState();
|
|
204
|
+
const routerEvents = routerRuntimeState.events;
|
|
205
|
+
function getPagesRouterRuntimeComponents() {
|
|
206
|
+
const existing = routerRuntimeState.components;
|
|
207
|
+
if (existing) return existing;
|
|
208
|
+
const components = {
|
|
209
|
+
CommitBoundary: PagesRouterCommitBoundary,
|
|
210
|
+
Provider: PagesRouterProvider
|
|
211
|
+
};
|
|
212
|
+
routerRuntimeState.components = components;
|
|
213
|
+
return components;
|
|
214
|
+
}
|
|
50
215
|
function resolveUrl(url) {
|
|
51
216
|
if (typeof url === "string") return url;
|
|
52
217
|
let result = url.pathname ?? "/";
|
|
@@ -201,8 +366,16 @@ function buildInitialRouterState() {
|
|
|
201
366
|
* locale stamped before any push could overwrite the active locale global.
|
|
202
367
|
*/
|
|
203
368
|
function stampInitialHistoryState() {
|
|
204
|
-
|
|
205
|
-
window.history
|
|
369
|
+
installManualScrollRestoration();
|
|
370
|
+
if (!window.history) return;
|
|
371
|
+
const existingState = window.history.state;
|
|
372
|
+
if (existingState !== null && existingState !== void 0) {
|
|
373
|
+
routerRuntimeState.currentHistoryKey = getRouterStateKey(existingState) ?? routerRuntimeState.currentHistoryKey;
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
const initialState = buildInitialRouterState();
|
|
377
|
+
routerRuntimeState.currentHistoryKey = initialState.key;
|
|
378
|
+
window.history.replaceState(initialState, "");
|
|
206
379
|
}
|
|
207
380
|
setStampInitialHistoryState(stampInitialHistoryState);
|
|
208
381
|
/** Save current scroll position into history state for back/forward restoration.
|
|
@@ -213,22 +386,62 @@ setStampInitialHistoryState(stampInitialHistoryState);
|
|
|
213
386
|
* minting the same shape here so the entry isn't treated as foreign.
|
|
214
387
|
*/
|
|
215
388
|
function saveScrollPosition() {
|
|
216
|
-
const
|
|
389
|
+
const position = getWindowScrollPosition();
|
|
390
|
+
const existing = isUnknownRecord(window.history.state) ? window.history.state : null;
|
|
217
391
|
const scroll = {
|
|
218
|
-
__vinext_scrollX:
|
|
219
|
-
__vinext_scrollY:
|
|
392
|
+
__vinext_scrollX: position.x,
|
|
393
|
+
__vinext_scrollY: position.y
|
|
220
394
|
};
|
|
221
395
|
const base = existing ?? buildInitialRouterState();
|
|
396
|
+
const key = getRouterStateKey(base);
|
|
397
|
+
if (key !== void 0) {
|
|
398
|
+
routerRuntimeState.currentHistoryKey = key;
|
|
399
|
+
saveScrollPositionToSessionStorage(key, position);
|
|
400
|
+
}
|
|
222
401
|
window.history.replaceState({
|
|
223
402
|
...base,
|
|
224
403
|
...scroll
|
|
225
404
|
}, "");
|
|
226
405
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
406
|
+
function getWindowScrollPosition() {
|
|
407
|
+
return {
|
|
408
|
+
x: window.scrollX,
|
|
409
|
+
y: window.scrollY
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
function getScrollStorageKey(historyKey) {
|
|
413
|
+
return `__next_scroll_${historyKey}`;
|
|
414
|
+
}
|
|
415
|
+
function readScrollPosition(value) {
|
|
416
|
+
if (!isUnknownRecord(value)) return null;
|
|
417
|
+
const nextX = value.x;
|
|
418
|
+
const nextY = value.y;
|
|
419
|
+
if (typeof nextX === "number" && typeof nextY === "number") return {
|
|
420
|
+
x: nextX,
|
|
421
|
+
y: nextY
|
|
422
|
+
};
|
|
423
|
+
const vinextX = value.__vinext_scrollX;
|
|
424
|
+
const vinextY = value.__vinext_scrollY;
|
|
425
|
+
if (typeof vinextX === "number" && typeof vinextY === "number") return {
|
|
426
|
+
x: vinextX,
|
|
427
|
+
y: vinextY
|
|
428
|
+
};
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
function saveScrollPositionToSessionStorage(key, position) {
|
|
432
|
+
if (!manualScrollRestoration) return;
|
|
433
|
+
try {
|
|
434
|
+
window.sessionStorage.setItem(getScrollStorageKey(key), JSON.stringify(position));
|
|
435
|
+
} catch {}
|
|
436
|
+
}
|
|
437
|
+
function readScrollPositionFromSessionStorage(key) {
|
|
438
|
+
if (!manualScrollRestoration) return null;
|
|
439
|
+
try {
|
|
440
|
+
const value = window.sessionStorage.getItem(getScrollStorageKey(key));
|
|
441
|
+
if (value === null) return null;
|
|
442
|
+
return readScrollPosition(JSON.parse(value));
|
|
443
|
+
} catch {
|
|
444
|
+
return null;
|
|
232
445
|
}
|
|
233
446
|
}
|
|
234
447
|
let _ssrContext = null;
|
|
@@ -380,6 +593,11 @@ function getRouteParamsFromQuery(pattern, query) {
|
|
|
380
593
|
function getRouteQueryFromNextData(nextData, resolvedPath) {
|
|
381
594
|
const routeQuery = {};
|
|
382
595
|
if (!nextData?.query || !nextData.page) return routeQuery;
|
|
596
|
+
if (extractRouteParamsFromPath(nextData.page, resolvedPath) === null) {
|
|
597
|
+
for (const [key, value] of Object.entries(nextData.query)) if (typeof value === "string") routeQuery[key] = value;
|
|
598
|
+
else if (Array.isArray(value)) routeQuery[key] = [...value];
|
|
599
|
+
return routeQuery;
|
|
600
|
+
}
|
|
383
601
|
const routeParamNames = extractRouteParamNames(nextData.page);
|
|
384
602
|
if (routeParamNames.length === 0) return routeQuery;
|
|
385
603
|
const currentRouteParams = extractRouteParamsFromPath(nextData.page, resolvedPath);
|
|
@@ -439,9 +657,8 @@ function shouldDeferInitialPagesRouterReady() {
|
|
|
439
657
|
if (!nextData) return false;
|
|
440
658
|
return !getPagesNavigationIsReadyFromSerializedState(nextData.page, window.location.search, nextData);
|
|
441
659
|
}
|
|
442
|
-
let _pagesRouterReady = typeof window === "undefined" ? true : !shouldDeferInitialPagesRouterReady();
|
|
443
660
|
function isPagesRouterReady() {
|
|
444
|
-
return
|
|
661
|
+
return routerRuntimeState.pagesRouterReady;
|
|
445
662
|
}
|
|
446
663
|
function isPagesRouterDocumentActive() {
|
|
447
664
|
if (typeof window === "undefined") return true;
|
|
@@ -451,10 +668,24 @@ function isPagesRouterDocumentActive() {
|
|
|
451
668
|
return Boolean(window.__VINEXT_APP__ || window.__VINEXT_APP_LOADER__);
|
|
452
669
|
}
|
|
453
670
|
function markPagesRouterReady() {
|
|
454
|
-
if (typeof window === "undefined" ||
|
|
455
|
-
|
|
671
|
+
if (typeof window === "undefined" || routerRuntimeState.pagesRouterReady) return false;
|
|
672
|
+
routerRuntimeState.pagesRouterReady = true;
|
|
456
673
|
return true;
|
|
457
674
|
}
|
|
675
|
+
function markPagesRouterHydrated() {
|
|
676
|
+
if (typeof window === "undefined" || window.__NEXT_HYDRATED === true) return;
|
|
677
|
+
const hydratedAt = performance.now();
|
|
678
|
+
window.__VINEXT_HYDRATED_AT = hydratedAt;
|
|
679
|
+
window.__NEXT_HYDRATED = true;
|
|
680
|
+
window.__NEXT_HYDRATED_AT = hydratedAt;
|
|
681
|
+
window.__NEXT_HYDRATED_CB?.();
|
|
682
|
+
}
|
|
683
|
+
function PagesRouterHydrationMarker() {
|
|
684
|
+
useEffect(() => {
|
|
685
|
+
markPagesRouterHydrated();
|
|
686
|
+
}, []);
|
|
687
|
+
return null;
|
|
688
|
+
}
|
|
458
689
|
function getRouterSnapshot() {
|
|
459
690
|
const isReady = typeof window === "undefined" ? _getSSRContext()?.navigationIsReady ?? true : isPagesRouterReady();
|
|
460
691
|
return {
|
|
@@ -488,20 +719,10 @@ var HardNavigationScheduledError = class extends Error {
|
|
|
488
719
|
this.name = "HardNavigationScheduledError";
|
|
489
720
|
}
|
|
490
721
|
};
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
* the active one. If a newer navigation has started, the stale one
|
|
496
|
-
* throws NavigationCancelledError so the caller can emit routeChangeError
|
|
497
|
-
* and skip routeChangeComplete.
|
|
498
|
-
*
|
|
499
|
-
* Replaces the old boolean `_navInProgress` guard which silently dropped
|
|
500
|
-
* the second navigation, causing URL/content mismatch.
|
|
501
|
-
*/
|
|
502
|
-
let _navigationId = 0;
|
|
503
|
-
/** AbortController for the in-flight fetch, so superseded navigations abort network I/O. */
|
|
504
|
-
let _activeAbortController = null;
|
|
722
|
+
function cancelPreviousRenderCommit() {
|
|
723
|
+
routerRuntimeState.cancelPendingRenderCommit?.();
|
|
724
|
+
routerRuntimeState.cancelPendingRenderCommit = null;
|
|
725
|
+
}
|
|
505
726
|
function scheduleHardNavigationAndThrow(url, message) {
|
|
506
727
|
if (typeof window === "undefined") throw new HardNavigationScheduledError(message);
|
|
507
728
|
window.location.href = url;
|
|
@@ -566,15 +787,20 @@ function getMiddlewarePagesDataFetchUrl(browserUrl) {
|
|
|
566
787
|
if (parsed.origin !== getWindowOrigin()) return null;
|
|
567
788
|
return buildPagesDataHref(__basePath, buildId, stripBasePath(parsed.pathname, __basePath), parsed.search);
|
|
568
789
|
}
|
|
569
|
-
async function
|
|
790
|
+
async function resolveMiddlewareDataEffect(browserUrl, signal) {
|
|
570
791
|
const dataUrl = getMiddlewarePagesDataFetchUrl(browserUrl);
|
|
571
792
|
if (!dataUrl) return null;
|
|
572
793
|
if (signal.aborted) throw new DOMException("Aborted", "AbortError");
|
|
573
794
|
try {
|
|
574
|
-
|
|
795
|
+
const res = await dedupedPagesDataFetch(dataUrl, { headers: {
|
|
575
796
|
Accept: "application/json",
|
|
576
797
|
"x-nextjs-data": "1"
|
|
577
|
-
} })
|
|
798
|
+
} });
|
|
799
|
+
return {
|
|
800
|
+
redirectLocation: res.headers.get("x-nextjs-redirect"),
|
|
801
|
+
rewriteTarget: res.headers.get("x-nextjs-rewrite"),
|
|
802
|
+
response: res
|
|
803
|
+
};
|
|
578
804
|
} catch {
|
|
579
805
|
return null;
|
|
580
806
|
}
|
|
@@ -611,19 +837,21 @@ function handleDataRedirect(destination, redirectBasePath, mode = "push") {
|
|
|
611
837
|
* data endpoint returns 404 and the client lands on the new build via a
|
|
612
838
|
* full document load.
|
|
613
839
|
*/
|
|
614
|
-
async function navigateClientData(url,
|
|
615
|
-
|
|
616
|
-
if (!root) {
|
|
840
|
+
async function navigateClientData(url, initialTarget, controller, navId, assertStillCurrent, options = {}, prefetchedResponse) {
|
|
841
|
+
if (!window.__VINEXT_ROOT__) {
|
|
617
842
|
window.location.href = url;
|
|
618
843
|
return;
|
|
619
844
|
}
|
|
620
845
|
if (controller.signal.aborted) throw new NavigationCancelledError(url);
|
|
621
|
-
let res;
|
|
622
|
-
try {
|
|
623
|
-
|
|
846
|
+
let res = prefetchedResponse;
|
|
847
|
+
if (!res) try {
|
|
848
|
+
const headers = {
|
|
624
849
|
Accept: "application/json",
|
|
625
850
|
"x-nextjs-data": "1"
|
|
626
|
-
}
|
|
851
|
+
};
|
|
852
|
+
const deploymentId = getDeploymentId();
|
|
853
|
+
if (deploymentId) headers[NEXT_DEPLOYMENT_ID_HEADER] = deploymentId;
|
|
854
|
+
res = await dedupedPagesDataFetch(initialTarget.dataHref, { headers });
|
|
627
855
|
} catch (err) {
|
|
628
856
|
if (err instanceof DOMException && err.name === "AbortError") throw new NavigationCancelledError(url);
|
|
629
857
|
throw err;
|
|
@@ -634,11 +862,14 @@ async function navigateClientData(url, target, controller, navId, assertStillCur
|
|
|
634
862
|
const redirectedUrl = resolveLocalRedirectUrl(softRedirect);
|
|
635
863
|
if (!redirectedUrl) scheduleHardNavigationAndThrow(softRedirect, "Navigation redirected externally");
|
|
636
864
|
window.history.replaceState(window.history.state ?? {}, "", redirectedUrl);
|
|
637
|
-
|
|
865
|
+
routerRuntimeState.lastPathnameAndSearch = window.location.pathname + window.location.search;
|
|
638
866
|
await navigateClientHtml(redirectedUrl, redirectedUrl, controller, navId, assertStillCurrent);
|
|
639
867
|
return;
|
|
640
868
|
}
|
|
641
869
|
if (!res.ok) scheduleHardNavigationAndThrow(url, `Data navigation failed: ${res.status} ${res.statusText}`);
|
|
870
|
+
const rewriteTarget = res.headers.get("x-nextjs-rewrite");
|
|
871
|
+
const target = rewriteTarget ? resolvePagesDataNavigationTarget(rewriteTarget, __basePath) : initialTarget;
|
|
872
|
+
if (!target) scheduleHardNavigationAndThrow(url, "Data navigation failed: rewrite target has no page loader");
|
|
642
873
|
let body;
|
|
643
874
|
try {
|
|
644
875
|
body = await res.json();
|
|
@@ -646,7 +877,9 @@ async function navigateClientData(url, target, controller, navId, assertStillCur
|
|
|
646
877
|
scheduleHardNavigationAndThrow(url, "Data navigation failed: invalid JSON response");
|
|
647
878
|
}
|
|
648
879
|
assertStillCurrent();
|
|
649
|
-
const
|
|
880
|
+
const props = isUnknownRecord(body) ? body : {};
|
|
881
|
+
const rawPageProps = props.pageProps;
|
|
882
|
+
const pageProps = isUnknownRecord(rawPageProps) ? rawPageProps : {};
|
|
650
883
|
const redirectDestination = pageProps.__N_REDIRECT;
|
|
651
884
|
if (typeof redirectDestination === "string") {
|
|
652
885
|
handleDataRedirect(redirectDestination, pageProps.__N_REDIRECT_BASE_PATH, options.mode);
|
|
@@ -673,17 +906,18 @@ async function navigateClientData(url, target, controller, navId, assertStillCur
|
|
|
673
906
|
assertStillCurrent();
|
|
674
907
|
let element;
|
|
675
908
|
if (AppComponent) element = React.createElement(AppComponent, {
|
|
909
|
+
...props,
|
|
676
910
|
Component: PageComponent,
|
|
677
|
-
pageProps
|
|
911
|
+
pageProps: rawPageProps,
|
|
912
|
+
router: Router
|
|
678
913
|
});
|
|
679
914
|
else element = React.createElement(PageComponent, pageProps);
|
|
680
|
-
element = wrapWithRouterContext(element);
|
|
681
915
|
const mergedQuery = mergeRouteParamsIntoQuery(parseQueryString(target.search), target.params);
|
|
682
916
|
const prev = window.__NEXT_DATA__;
|
|
683
917
|
const nextLocale = (window.__VINEXT_LOCALES__?.length ?? 0) > 0 ? target.locale ?? window.__VINEXT_DEFAULT_LOCALE__ : prev?.locale;
|
|
684
918
|
const nextData = {
|
|
685
919
|
...prev,
|
|
686
|
-
props
|
|
920
|
+
props,
|
|
687
921
|
page: target.pattern,
|
|
688
922
|
query: mergedQuery,
|
|
689
923
|
buildId: target.buildId,
|
|
@@ -692,7 +926,8 @@ async function navigateClientData(url, target, controller, navId, assertStillCur
|
|
|
692
926
|
};
|
|
693
927
|
window.__NEXT_DATA__ = nextData;
|
|
694
928
|
applyVinextLocaleGlobals(window, nextData);
|
|
695
|
-
|
|
929
|
+
await renderPagesRouterElement(element, options.scroll);
|
|
930
|
+
assertStillCurrent();
|
|
696
931
|
}
|
|
697
932
|
/**
|
|
698
933
|
* Perform client-side navigation by fetching the page's full HTML and
|
|
@@ -707,8 +942,7 @@ async function navigateClientData(url, target, controller, navId, assertStillCur
|
|
|
707
942
|
async function navigateClientHtml(url, fetchUrl, controller, navId, assertStillCurrent, options = {}) {
|
|
708
943
|
let browserUrl = url;
|
|
709
944
|
let pendingRedirectHistoryUrl = fetchUrl === url ? null : url;
|
|
710
|
-
|
|
711
|
-
if (!root) {
|
|
945
|
+
if (!window.__VINEXT_ROOT__) {
|
|
712
946
|
window.location.href = browserUrl;
|
|
713
947
|
return;
|
|
714
948
|
}
|
|
@@ -736,7 +970,9 @@ async function navigateClientHtml(url, fetchUrl, controller, navId, assertStillC
|
|
|
736
970
|
const nextDataJson = extractVinextNextDataJson(html);
|
|
737
971
|
if (!nextDataJson) scheduleHardNavigationAndThrow(url, "Navigation failed: missing __NEXT_DATA__ in response");
|
|
738
972
|
const nextData = parseVinextNextDataJson(nextDataJson);
|
|
739
|
-
const
|
|
973
|
+
const props = nextData.props && typeof nextData.props === "object" ? nextData.props : {};
|
|
974
|
+
const rawPageProps = props.pageProps;
|
|
975
|
+
const pageProps = isUnknownRecord(rawPageProps) ? rawPageProps : {};
|
|
740
976
|
let pageModuleUrl = nextData.__vinext?.pageModuleUrl;
|
|
741
977
|
if (!pageModuleUrl) {
|
|
742
978
|
const moduleMatch = html.match(/import\("([^"]+)"\);\s*\n\s*const PageComponent/);
|
|
@@ -744,11 +980,10 @@ async function navigateClientHtml(url, fetchUrl, controller, navId, assertStillC
|
|
|
744
980
|
pageModuleUrl = moduleMatch?.[1] ?? altMatch?.[1] ?? void 0;
|
|
745
981
|
}
|
|
746
982
|
let pageModule;
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
} else {
|
|
983
|
+
const loader = window.__VINEXT_PAGE_LOADERS__?.[nextData.page];
|
|
984
|
+
if (loader) pageModule = await loader();
|
|
985
|
+
else if (!pageModuleUrl) scheduleHardNavigationAndThrow(browserUrl, "Navigation failed: no page module URL found");
|
|
986
|
+
else {
|
|
752
987
|
if (!isValidModulePath(pageModuleUrl)) {
|
|
753
988
|
console.error("[vinext] Blocked import of invalid page module path:", pageModuleUrl);
|
|
754
989
|
scheduleHardNavigationAndThrow(browserUrl, "Navigation failed: invalid page module path");
|
|
@@ -777,18 +1012,20 @@ async function navigateClientHtml(url, fetchUrl, controller, navId, assertStillC
|
|
|
777
1012
|
assertStillCurrent();
|
|
778
1013
|
let element;
|
|
779
1014
|
if (AppComponent) element = React.createElement(AppComponent, {
|
|
1015
|
+
...props,
|
|
780
1016
|
Component: PageComponent,
|
|
781
|
-
pageProps
|
|
1017
|
+
pageProps: rawPageProps,
|
|
1018
|
+
router: Router
|
|
782
1019
|
});
|
|
783
1020
|
else element = React.createElement(PageComponent, pageProps);
|
|
784
|
-
element = wrapWithRouterContext(element);
|
|
785
1021
|
if (pendingRedirectHistoryUrl) {
|
|
786
1022
|
window.history.replaceState(window.history.state ?? {}, "", pendingRedirectHistoryUrl);
|
|
787
|
-
|
|
1023
|
+
routerRuntimeState.lastPathnameAndSearch = window.location.pathname + window.location.search;
|
|
788
1024
|
}
|
|
789
1025
|
window.__NEXT_DATA__ = nextData;
|
|
790
1026
|
applyVinextLocaleGlobals(window, nextData);
|
|
791
|
-
|
|
1027
|
+
await renderPagesRouterElement(element, options.scroll);
|
|
1028
|
+
assertStillCurrent();
|
|
792
1029
|
}
|
|
793
1030
|
/**
|
|
794
1031
|
* Perform client-side navigation. Prefers the JSON data endpoint when the
|
|
@@ -806,43 +1043,49 @@ async function navigateClientHtml(url, fetchUrl, controller, navId, assertStillC
|
|
|
806
1043
|
*/
|
|
807
1044
|
async function navigateClient(url, fetchUrl = url, options = {}) {
|
|
808
1045
|
if (typeof window === "undefined") return;
|
|
809
|
-
|
|
1046
|
+
routerRuntimeState.activeAbortController?.abort();
|
|
1047
|
+
cancelPreviousRenderCommit();
|
|
810
1048
|
const controller = new AbortController();
|
|
811
|
-
|
|
812
|
-
const navId = ++
|
|
1049
|
+
routerRuntimeState.activeAbortController = controller;
|
|
1050
|
+
const navId = ++routerRuntimeState.navigationId;
|
|
813
1051
|
/** Check if this navigation is still the active one. If not, throw. */
|
|
814
1052
|
function assertStillCurrent() {
|
|
815
|
-
if (navId !==
|
|
1053
|
+
if (navId !== routerRuntimeState.navigationId) throw new NavigationCancelledError(url);
|
|
816
1054
|
}
|
|
817
1055
|
try {
|
|
818
1056
|
if (options.allowNotFoundResponse === true) await navigateClientHtml(url, fetchUrl, controller, navId, assertStillCurrent, options);
|
|
819
1057
|
else {
|
|
820
1058
|
let browserUrl = url;
|
|
821
1059
|
let htmlFetchUrl = fetchUrl;
|
|
822
|
-
|
|
1060
|
+
let dataTarget = resolvePagesDataNavigationTarget(browserUrl, __basePath);
|
|
1061
|
+
let middlewareDataResponse;
|
|
823
1062
|
if (!dataTarget) {
|
|
824
|
-
let
|
|
1063
|
+
let middlewareEffect;
|
|
825
1064
|
try {
|
|
826
|
-
|
|
1065
|
+
middlewareEffect = await resolveMiddlewareDataEffect(browserUrl, controller.signal);
|
|
827
1066
|
} catch (err) {
|
|
828
1067
|
if (err instanceof DOMException && err.name === "AbortError") throw new NavigationCancelledError(browserUrl);
|
|
829
1068
|
throw err;
|
|
830
1069
|
}
|
|
831
1070
|
assertStillCurrent();
|
|
1071
|
+
const redirectLocation = middlewareEffect?.redirectLocation ?? null;
|
|
832
1072
|
if (redirectLocation) {
|
|
833
1073
|
const redirectedUrl = resolveLocalRedirectUrl(redirectLocation);
|
|
834
1074
|
if (!redirectedUrl) scheduleHardNavigationAndThrow(redirectLocation, "Navigation redirected externally");
|
|
835
1075
|
window.history.replaceState(window.history.state ?? {}, "", redirectedUrl);
|
|
836
|
-
|
|
1076
|
+
routerRuntimeState.lastPathnameAndSearch = window.location.pathname + window.location.search;
|
|
837
1077
|
browserUrl = redirectedUrl;
|
|
838
1078
|
htmlFetchUrl = redirectedUrl;
|
|
1079
|
+
} else if (middlewareEffect?.rewriteTarget) {
|
|
1080
|
+
dataTarget = resolvePagesDataNavigationTarget(middlewareEffect.rewriteTarget, __basePath);
|
|
1081
|
+
if (dataTarget) middlewareDataResponse = middlewareEffect.response;
|
|
839
1082
|
}
|
|
840
1083
|
}
|
|
841
|
-
if (dataTarget) await navigateClientData(browserUrl, dataTarget, controller, navId, assertStillCurrent, options);
|
|
1084
|
+
if (dataTarget) await navigateClientData(browserUrl, dataTarget, controller, navId, assertStillCurrent, options, middlewareDataResponse);
|
|
842
1085
|
else await navigateClientHtml(browserUrl, htmlFetchUrl, controller, navId, assertStillCurrent, options);
|
|
843
1086
|
}
|
|
844
1087
|
} finally {
|
|
845
|
-
if (navId ===
|
|
1088
|
+
if (navId === routerRuntimeState.navigationId) routerRuntimeState.activeAbortController = null;
|
|
846
1089
|
}
|
|
847
1090
|
}
|
|
848
1091
|
/**
|
|
@@ -928,8 +1171,8 @@ function dispatchNavigateEvent() {
|
|
|
928
1171
|
* locale and the canonical app-relative `as` path.
|
|
929
1172
|
*/
|
|
930
1173
|
function updateHistory(mode, fullUrl, navState) {
|
|
931
|
-
const
|
|
932
|
-
const key = mode === "push" ? createHistoryKey() :
|
|
1174
|
+
const previousKey = getRouterStateKey(window.history.state);
|
|
1175
|
+
const key = mode === "push" ? createHistoryKey() : previousKey ?? routerRuntimeState.currentHistoryKey ?? createHistoryKey();
|
|
933
1176
|
const state = {
|
|
934
1177
|
url: navState?.url ?? fullUrl,
|
|
935
1178
|
as: navState?.as ?? fullUrl,
|
|
@@ -939,13 +1182,13 @@ function updateHistory(mode, fullUrl, navState) {
|
|
|
939
1182
|
};
|
|
940
1183
|
if (mode === "push") window.history.pushState(state, "", fullUrl);
|
|
941
1184
|
else window.history.replaceState(state, "", fullUrl);
|
|
942
|
-
|
|
943
|
-
|
|
1185
|
+
routerRuntimeState.currentHistoryKey = key;
|
|
1186
|
+
routerRuntimeState.lastPathnameAndSearch = window.location.pathname + window.location.search;
|
|
1187
|
+
routerRuntimeState.routerDidNavigate = true;
|
|
944
1188
|
}
|
|
945
|
-
let _historyKeyCounter = 0;
|
|
946
1189
|
function createHistoryKey() {
|
|
947
|
-
|
|
948
|
-
return `vinext_${
|
|
1190
|
+
routerRuntimeState.historyKeyCounter += 1;
|
|
1191
|
+
return `vinext_${routerRuntimeState.historyKeyCounter.toString(36)}_${Math.random().toString(36).slice(2, 9)}`;
|
|
949
1192
|
}
|
|
950
1193
|
/**
|
|
951
1194
|
* Throw the canonical "no router instance" error used when a Pages Router
|
|
@@ -993,12 +1236,21 @@ async function performNavigation(url, as, options, mode, onStateUpdate) {
|
|
|
993
1236
|
const full = normalizePathTrailingSlash(toBrowserNavigationHref(resolved, window.location.href, __basePath), __trailingSlash);
|
|
994
1237
|
const errorRouteHtmlFetchUrl = resolvePagesErrorHtmlFetchUrl(url, navigationLocale);
|
|
995
1238
|
const htmlFetchUrl = errorRouteHtmlFetchUrl ?? getPagesHtmlFetchUrl(full, navigationLocale);
|
|
996
|
-
const navigateOptions = errorRouteHtmlFetchUrl ? {
|
|
997
|
-
allowNotFoundResponse: true,
|
|
998
|
-
mode
|
|
999
|
-
} : { mode };
|
|
1000
1239
|
const shallow = options?.shallow ?? false;
|
|
1001
1240
|
const doScroll = options?.scroll !== false;
|
|
1241
|
+
const hash = extractHash(resolved);
|
|
1242
|
+
const scrollTarget = doScroll ? {
|
|
1243
|
+
x: 0,
|
|
1244
|
+
y: 0
|
|
1245
|
+
} : null;
|
|
1246
|
+
const navigateOptions = errorRouteHtmlFetchUrl ? {
|
|
1247
|
+
allowNotFoundResponse: true,
|
|
1248
|
+
mode,
|
|
1249
|
+
scroll: scrollTarget
|
|
1250
|
+
} : {
|
|
1251
|
+
mode,
|
|
1252
|
+
scroll: scrollTarget
|
|
1253
|
+
};
|
|
1002
1254
|
const navStateOptions = { shallow };
|
|
1003
1255
|
if (navigationLocale !== void 0) navStateOptions.locale = navigationLocale;
|
|
1004
1256
|
const resolvedNoHash = stripHash(resolved);
|
|
@@ -1007,7 +1259,8 @@ async function performNavigation(url, as, options, mode, onStateUpdate) {
|
|
|
1007
1259
|
as: resolvedNoHash,
|
|
1008
1260
|
options: navStateOptions
|
|
1009
1261
|
};
|
|
1010
|
-
if (isHashOnlyChange(full)) {
|
|
1262
|
+
if (options?._h !== 1 && isHashOnlyChange(full)) {
|
|
1263
|
+
if (mode === "push") saveScrollPosition();
|
|
1011
1264
|
const eventUrl = resolveHashUrl(full);
|
|
1012
1265
|
routerEvents.emit("hashChangeStart", eventUrl, { shallow });
|
|
1013
1266
|
updateHistory(mode, resolved.startsWith("#") ? resolved : full, navState);
|
|
@@ -1020,25 +1273,25 @@ async function performNavigation(url, as, options, mode, onStateUpdate) {
|
|
|
1020
1273
|
const appPath = getLocalPathname(resolved);
|
|
1021
1274
|
const appPathNorm = appPath !== null ? removeTrailingSlash(appPath) : null;
|
|
1022
1275
|
const appPathEntry = appPathNorm !== null ? getPagesRouterComponentsMap()[appPathNorm] : void 0;
|
|
1023
|
-
if (appPathEntry !== void 0 && "__appRouter" in appPathEntry && appPathEntry.__appRouter) {
|
|
1276
|
+
if (appPathEntry !== void 0 && "__appRouter" in appPathEntry && appPathEntry.__appRouter || ["app", "document"].includes(resolveHybridClientRouteOwner(resolved, __basePath) ?? "")) {
|
|
1024
1277
|
if (mode === "push") window.location.assign(full);
|
|
1025
1278
|
else window.location.replace(full);
|
|
1026
1279
|
return new Promise(() => {});
|
|
1027
1280
|
}
|
|
1028
1281
|
if (mode === "push") saveScrollPosition();
|
|
1029
|
-
|
|
1282
|
+
const isQueryUpdating = options?._h === 1;
|
|
1283
|
+
if (!isQueryUpdating) routerEvents.emit("routeChangeStart", resolved, { shallow });
|
|
1030
1284
|
routerEvents.emit("beforeHistoryChange", resolved, { shallow });
|
|
1031
1285
|
updateHistory(mode, full, navState);
|
|
1032
1286
|
if (!shallow) {
|
|
1033
1287
|
const result = await runNavigateClient(full, resolved, htmlFetchUrl, navigateOptions);
|
|
1034
1288
|
if (result === "cancelled") return true;
|
|
1035
1289
|
if (result === "failed") return false;
|
|
1036
|
-
}
|
|
1037
|
-
onStateUpdate?.();
|
|
1038
|
-
routerEvents.emit("routeChangeComplete", resolved, { shallow });
|
|
1039
|
-
const hash = extractHash(resolved);
|
|
1040
|
-
if (doScroll) if (hash) scrollToHashTarget(hash);
|
|
1290
|
+
} else if (doScroll) if (hash) scrollToHashTarget(hash);
|
|
1041
1291
|
else window.scrollTo(0, 0);
|
|
1292
|
+
onStateUpdate?.();
|
|
1293
|
+
if (!isQueryUpdating) routerEvents.emit("routeChangeComplete", resolved, { shallow });
|
|
1294
|
+
if (doScroll && hash && !shallow) scrollToHashTarget(hash);
|
|
1042
1295
|
dispatchNavigateEvent();
|
|
1043
1296
|
return true;
|
|
1044
1297
|
}
|
|
@@ -1083,8 +1336,9 @@ async function prefetchUrl(url) {
|
|
|
1083
1336
|
*/
|
|
1084
1337
|
function useRouter() {
|
|
1085
1338
|
const router = useContext(RouterContext);
|
|
1086
|
-
if (
|
|
1087
|
-
return
|
|
1339
|
+
if (router) return router;
|
|
1340
|
+
if (typeof window !== "undefined" && window.__VINEXT_PAGE_LOADERS__ !== void 0) return Router;
|
|
1341
|
+
throw new Error("NextRouter was not mounted. https://nextjs.org/docs/messages/next-router-not-mounted");
|
|
1088
1342
|
}
|
|
1089
1343
|
function PagesRouterProvider({ children }) {
|
|
1090
1344
|
const [{ pathname, query, asPath, isReady }, setState] = useState(getRouterSnapshot);
|
|
@@ -1095,9 +1349,11 @@ function PagesRouterProvider({ children }) {
|
|
|
1095
1349
|
window.addEventListener("vinext:navigate", onNavigate);
|
|
1096
1350
|
let cancelled = false;
|
|
1097
1351
|
const readyTimer = window.setTimeout(() => {
|
|
1098
|
-
if (cancelled
|
|
1099
|
-
|
|
1100
|
-
|
|
1352
|
+
if (cancelled) return;
|
|
1353
|
+
if (markPagesRouterReady()) {
|
|
1354
|
+
setState(getRouterSnapshot());
|
|
1355
|
+
notifyNextNavigationPagesContext();
|
|
1356
|
+
}
|
|
1101
1357
|
}, 0);
|
|
1102
1358
|
return () => {
|
|
1103
1359
|
cancelled = true;
|
|
@@ -1140,36 +1396,47 @@ function PagesRouterProvider({ children }) {
|
|
|
1140
1396
|
Router.prefetch(href);
|
|
1141
1397
|
}
|
|
1142
1398
|
}), []);
|
|
1143
|
-
const content = createElement(RouterContext.Provider, { value: router }, children);
|
|
1399
|
+
const content = createElement(RouterContext.Provider, { value: router }, createElement(Fragment, null, children, createElement(PagesRouterHydrationMarker)));
|
|
1144
1400
|
return AppRouterContext ? createElement(AppRouterContext.Provider, { value: appRouter }, content) : content;
|
|
1145
1401
|
}
|
|
1146
|
-
let _beforePopStateCb;
|
|
1147
|
-
let _lastPathnameAndSearch = typeof window !== "undefined" ? window.location.pathname + window.location.search : "";
|
|
1148
|
-
let _isFirstPopStateEvent = true;
|
|
1149
|
-
let _routerDidNavigate = false;
|
|
1150
1402
|
function isNextRouterState(state) {
|
|
1151
1403
|
return typeof state === "object" && state !== null && "__N" in state && state.__N === true;
|
|
1152
1404
|
}
|
|
1405
|
+
function getRouterStateKey(state) {
|
|
1406
|
+
if (!isNextRouterState(state)) return void 0;
|
|
1407
|
+
return typeof state.key === "string" ? state.key : void 0;
|
|
1408
|
+
}
|
|
1153
1409
|
function handlePagesRouterPopState(e) {
|
|
1154
1410
|
const browserUrl = window.location.pathname + window.location.search;
|
|
1155
1411
|
const appUrl = stripBasePath(window.location.pathname, __basePath) + window.location.search;
|
|
1156
1412
|
const state = e.state;
|
|
1157
|
-
const wasFirst =
|
|
1158
|
-
|
|
1413
|
+
const wasFirst = routerRuntimeState.isFirstPopStateEvent;
|
|
1414
|
+
routerRuntimeState.isFirstPopStateEvent = false;
|
|
1159
1415
|
if (state !== null && state !== void 0 && !isNextRouterState(state)) return;
|
|
1160
|
-
if (wasFirst && !
|
|
1416
|
+
if (wasFirst && !routerRuntimeState.routerDidNavigate && isNextRouterState(state)) {
|
|
1161
1417
|
const currentLocale = window.__VINEXT_LOCALE__;
|
|
1162
|
-
if (state.options?.locale === currentLocale && typeof state.as === "string" && withBasePath(state.as, __basePath) ===
|
|
1418
|
+
if (state.options?.locale === currentLocale && typeof state.as === "string" && withBasePath(state.as, __basePath) === routerRuntimeState.lastPathnameAndSearch) return;
|
|
1163
1419
|
}
|
|
1164
|
-
const isHashOnly = browserUrl ===
|
|
1165
|
-
|
|
1166
|
-
|
|
1420
|
+
const isHashOnly = browserUrl === routerRuntimeState.lastPathnameAndSearch;
|
|
1421
|
+
const targetKey = getRouterStateKey(state);
|
|
1422
|
+
let forcedScroll;
|
|
1423
|
+
if (manualScrollRestoration) {
|
|
1424
|
+
const currentKey = routerRuntimeState.currentHistoryKey;
|
|
1425
|
+
if (currentKey !== void 0 && currentKey !== targetKey) saveScrollPositionToSessionStorage(currentKey, getWindowScrollPosition());
|
|
1426
|
+
if (targetKey !== void 0 && currentKey !== targetKey) forcedScroll = readScrollPositionFromSessionStorage(targetKey) ?? {
|
|
1427
|
+
x: 0,
|
|
1428
|
+
y: 0
|
|
1429
|
+
};
|
|
1430
|
+
}
|
|
1431
|
+
if (routerRuntimeState.beforePopStateCb !== void 0) {
|
|
1432
|
+
if (!routerRuntimeState.beforePopStateCb({
|
|
1167
1433
|
url: appUrl,
|
|
1168
1434
|
as: appUrl,
|
|
1169
1435
|
options: { shallow: false }
|
|
1170
1436
|
})) return;
|
|
1171
1437
|
}
|
|
1172
|
-
|
|
1438
|
+
if (targetKey !== void 0) routerRuntimeState.currentHistoryKey = targetKey;
|
|
1439
|
+
routerRuntimeState.lastPathnameAndSearch = browserUrl;
|
|
1173
1440
|
if (isHashOnly) {
|
|
1174
1441
|
const hashUrl = appUrl + window.location.hash;
|
|
1175
1442
|
routerEvents.emit("hashChangeStart", hashUrl, { shallow: false });
|
|
@@ -1183,9 +1450,12 @@ function handlePagesRouterPopState(e) {
|
|
|
1183
1450
|
routerEvents.emit("routeChangeStart", fullAppUrl, { shallow: false });
|
|
1184
1451
|
routerEvents.emit("beforeHistoryChange", fullAppUrl, { shallow: false });
|
|
1185
1452
|
(async () => {
|
|
1186
|
-
|
|
1453
|
+
const scrollTarget = manualScrollRestoration ? forcedScroll ?? readScrollPosition(state) ?? {
|
|
1454
|
+
x: 0,
|
|
1455
|
+
y: 0
|
|
1456
|
+
} : readScrollPosition(state);
|
|
1457
|
+
if (await runNavigateClient(browserUrl, fullAppUrl, getPagesHtmlFetchUrl(browserUrl, effectiveLocale), { scroll: scrollTarget }) === "completed") {
|
|
1187
1458
|
routerEvents.emit("routeChangeComplete", fullAppUrl, { shallow: false });
|
|
1188
|
-
restoreScrollPosition(e.state);
|
|
1189
1459
|
dispatchNavigateEvent();
|
|
1190
1460
|
}
|
|
1191
1461
|
})();
|
|
@@ -1198,9 +1468,22 @@ setPagesRouterPopStateHandler(handlePagesRouterPopState);
|
|
|
1198
1468
|
* The provider owns the reactive Pages Router snapshot so next/router and
|
|
1199
1469
|
* next/compat/router consumers share one context value instead of each hook
|
|
1200
1470
|
* installing its own global URL-change listener.
|
|
1471
|
+
*
|
|
1472
|
+
* The PagesRouterCommitBoundary exists for client navigations: its layout
|
|
1473
|
+
* callback runs scroll restoration at commit time and resolves the navigation
|
|
1474
|
+
* at the same root-commit boundary Next.js awaits before routeChangeComplete.
|
|
1475
|
+
* Its onError rejection drives the hard-navigation fallback in runNavigateClient.
|
|
1476
|
+
* The same boundary intentionally also wraps SSR and initial hydration, where
|
|
1477
|
+
* callbacks default to noopCommit: a hydration-time render error is caught
|
|
1478
|
+
* here (React still console.error's it) instead of propagating, matching the
|
|
1479
|
+
* navigation-path containment.
|
|
1201
1480
|
*/
|
|
1202
|
-
function wrapWithRouterContext(element) {
|
|
1203
|
-
|
|
1481
|
+
function wrapWithRouterContext(element, onCommit = noopCommit, onError = noopCommit) {
|
|
1482
|
+
const { CommitBoundary, Provider } = getPagesRouterRuntimeComponents();
|
|
1483
|
+
return createElement(CommitBoundary, {
|
|
1484
|
+
onCommit,
|
|
1485
|
+
onError
|
|
1486
|
+
}, createElement(Provider, null, element));
|
|
1204
1487
|
}
|
|
1205
1488
|
/**
|
|
1206
1489
|
* Higher-order component that injects the Pages Router `router` instance as
|
|
@@ -1263,7 +1546,7 @@ const RouterMethods = {
|
|
|
1263
1546
|
},
|
|
1264
1547
|
beforePopState: (cb) => {
|
|
1265
1548
|
if (typeof window === "undefined") throwNoRouterInstance();
|
|
1266
|
-
|
|
1549
|
+
routerRuntimeState.beforePopStateCb = cb;
|
|
1267
1550
|
},
|
|
1268
1551
|
events: routerEvents
|
|
1269
1552
|
};
|
|
@@ -1330,7 +1613,7 @@ const Router = Object.defineProperties(RouterMethods, {
|
|
|
1330
1613
|
isReady: {
|
|
1331
1614
|
enumerable: true,
|
|
1332
1615
|
get() {
|
|
1333
|
-
return isPagesRouterReady();
|
|
1616
|
+
return isPagesRouterReady() && (typeof window === "undefined" || window.__NEXT_HYDRATED === true);
|
|
1334
1617
|
}
|
|
1335
1618
|
},
|
|
1336
1619
|
isPreview: {
|
|
@@ -1346,27 +1629,35 @@ const Router = Object.defineProperties(RouterMethods, {
|
|
|
1346
1629
|
}
|
|
1347
1630
|
}
|
|
1348
1631
|
});
|
|
1349
|
-
|
|
1632
|
+
routerRuntimeState.publicRouter = Router;
|
|
1633
|
+
const deprecatedRouterEvents = [
|
|
1350
1634
|
"routeChangeStart",
|
|
1351
1635
|
"beforeHistoryChange",
|
|
1352
1636
|
"routeChangeComplete",
|
|
1353
1637
|
"routeChangeError",
|
|
1354
1638
|
"hashChangeStart",
|
|
1355
1639
|
"hashChangeComplete"
|
|
1356
|
-
]
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1640
|
+
];
|
|
1641
|
+
if (!routerRuntimeState.deprecatedEventBridgeInstalled) {
|
|
1642
|
+
routerRuntimeState.deprecatedEventBridgeInstalled = true;
|
|
1643
|
+
for (const event of deprecatedRouterEvents) {
|
|
1644
|
+
const eventField = `on${event.charAt(0).toUpperCase()}${event.substring(1)}`;
|
|
1645
|
+
routerEvents.on(event, (...args) => {
|
|
1646
|
+
const handler = (routerRuntimeState.publicRouter ?? Router)[eventField];
|
|
1647
|
+
if (typeof handler === "function") try {
|
|
1648
|
+
handler(...args);
|
|
1649
|
+
} catch (err) {
|
|
1650
|
+
console.error(`Error when running the Router event: ${eventField}`);
|
|
1651
|
+
console.error(err instanceof Error ? `${err.message}\n${err.stack}` : String(err));
|
|
1652
|
+
}
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
if (typeof window !== "undefined") {
|
|
1657
|
+
installPagesRouterRuntime();
|
|
1658
|
+
installWindowNext({ router: Router });
|
|
1367
1659
|
}
|
|
1368
|
-
if (typeof window !== "undefined") installWindowNext({ router: Router });
|
|
1369
1660
|
const _PAGES_NAVIGATION_ACCESSOR_KEY = Symbol.for("vinext.navigation.pagesNavigationContextAccessor");
|
|
1370
1661
|
globalThis[_PAGES_NAVIGATION_ACCESSOR_KEY] = getPagesNavigationContext;
|
|
1371
1662
|
//#endregion
|
|
1372
|
-
export { _registerRouterStateAccessors, applyNavigationLocale, Router as default, getPagesNavigationContext, getPagesNavigationIsReadyFromSerializedState, isExternalUrl, isHashOnlyChange, setSSRContext, useRouter, withRouter, wrapWithRouterContext };
|
|
1663
|
+
export { markPagesRouterReady as _markPagesRouterReady, _registerRouterStateAccessors, applyNavigationLocale, Router as default, getPagesNavigationContext, getPagesNavigationIsReadyFromSerializedState, isExternalUrl, isHashOnlyChange, setSSRContext, useRouter, withRouter, wrapWithRouterContext };
|