456 lines
19 KiB
JavaScript
456 lines
19 KiB
JavaScript
|
"use client";
|
||
|
|
||
|
"use strict";
|
||
|
Object.defineProperty(exports, "__esModule", {
|
||
|
value: true
|
||
|
});
|
||
|
0 && (module.exports = {
|
||
|
getServerActionDispatcher: null,
|
||
|
urlToUrlWithoutFlightMarker: null,
|
||
|
default: null
|
||
|
});
|
||
|
function _export(target, all) {
|
||
|
for(var name in all)Object.defineProperty(target, name, {
|
||
|
enumerable: true,
|
||
|
get: all[name]
|
||
|
});
|
||
|
}
|
||
|
_export(exports, {
|
||
|
getServerActionDispatcher: function() {
|
||
|
return getServerActionDispatcher;
|
||
|
},
|
||
|
urlToUrlWithoutFlightMarker: function() {
|
||
|
return urlToUrlWithoutFlightMarker;
|
||
|
},
|
||
|
default: function() {
|
||
|
return AppRouter;
|
||
|
}
|
||
|
});
|
||
|
const _interop_require_wildcard = require("@swc/helpers/_/_interop_require_wildcard");
|
||
|
const _react = /*#__PURE__*/ _interop_require_wildcard._(require("react"));
|
||
|
const _approutercontextsharedruntime = require("../../shared/lib/app-router-context.shared-runtime");
|
||
|
const _routerreducer = require("./router-reducer/router-reducer");
|
||
|
const _routerreducertypes = require("./router-reducer/router-reducer-types");
|
||
|
const _createhreffromurl = require("./router-reducer/create-href-from-url");
|
||
|
const _hooksclientcontextsharedruntime = require("../../shared/lib/hooks-client-context.shared-runtime");
|
||
|
const _usereducerwithdevtools = require("./use-reducer-with-devtools");
|
||
|
const _errorboundary = require("./error-boundary");
|
||
|
const _createinitialrouterstate = require("./router-reducer/create-initial-router-state");
|
||
|
const _isbot = require("../../shared/lib/router/utils/is-bot");
|
||
|
const _addbasepath = require("../add-base-path");
|
||
|
const _approuterannouncer = require("./app-router-announcer");
|
||
|
const _redirectboundary = require("./redirect-boundary");
|
||
|
const _findheadincache = require("./router-reducer/reducers/find-head-in-cache");
|
||
|
const _infinitepromise = require("./infinite-promise");
|
||
|
const _approuterheaders = require("./app-router-headers");
|
||
|
const _removebasepath = require("../remove-base-path");
|
||
|
const _hasbasepath = require("../has-base-path");
|
||
|
const isServer = typeof window === "undefined";
|
||
|
// Ensure the initialParallelRoutes are not combined because of double-rendering in the browser with Strict Mode.
|
||
|
let initialParallelRoutes = isServer ? null : new Map();
|
||
|
let globalServerActionDispatcher = null;
|
||
|
function getServerActionDispatcher() {
|
||
|
return globalServerActionDispatcher;
|
||
|
}
|
||
|
let globalMutable = {
|
||
|
refresh: ()=>{}
|
||
|
};
|
||
|
function urlToUrlWithoutFlightMarker(url) {
|
||
|
const urlWithoutFlightParameters = new URL(url, location.origin);
|
||
|
urlWithoutFlightParameters.searchParams.delete(_approuterheaders.NEXT_RSC_UNION_QUERY);
|
||
|
if (process.env.NODE_ENV === "production") {
|
||
|
if (process.env.__NEXT_CONFIG_OUTPUT === "export" && urlWithoutFlightParameters.pathname.endsWith(".txt")) {
|
||
|
const { pathname } = urlWithoutFlightParameters;
|
||
|
const length = pathname.endsWith("/index.txt") ? 10 : 4;
|
||
|
// Slice off `/index.txt` or `.txt` from the end of the pathname
|
||
|
urlWithoutFlightParameters.pathname = pathname.slice(0, -length);
|
||
|
}
|
||
|
}
|
||
|
return urlWithoutFlightParameters;
|
||
|
}
|
||
|
function isExternalURL(url) {
|
||
|
return url.origin !== window.location.origin;
|
||
|
}
|
||
|
function HistoryUpdater(param) {
|
||
|
let { tree, pushRef, canonicalUrl, sync } = param;
|
||
|
(0, _react.useInsertionEffect)(()=>{
|
||
|
// Identifier is shortened intentionally.
|
||
|
// __NA is used to identify if the history entry can be handled by the app-router.
|
||
|
// __N is used to identify if the history entry can be handled by the old router.
|
||
|
const historyState = {
|
||
|
__NA: true,
|
||
|
tree
|
||
|
};
|
||
|
if (pushRef.pendingPush && (0, _createhreffromurl.createHrefFromUrl)(new URL(window.location.href)) !== canonicalUrl) {
|
||
|
// This intentionally mutates React state, pushRef is overwritten to ensure additional push/replace calls do not trigger an additional history entry.
|
||
|
pushRef.pendingPush = false;
|
||
|
window.history.pushState(historyState, "", canonicalUrl);
|
||
|
} else {
|
||
|
window.history.replaceState(historyState, "", canonicalUrl);
|
||
|
}
|
||
|
sync();
|
||
|
}, [
|
||
|
tree,
|
||
|
pushRef,
|
||
|
canonicalUrl,
|
||
|
sync
|
||
|
]);
|
||
|
return null;
|
||
|
}
|
||
|
const createEmptyCacheNode = ()=>({
|
||
|
status: _approutercontextsharedruntime.CacheStates.LAZY_INITIALIZED,
|
||
|
data: null,
|
||
|
subTreeData: null,
|
||
|
parallelRoutes: new Map()
|
||
|
});
|
||
|
function useServerActionDispatcher(dispatch) {
|
||
|
const serverActionDispatcher = (0, _react.useCallback)((actionPayload)=>{
|
||
|
(0, _react.startTransition)(()=>{
|
||
|
dispatch({
|
||
|
...actionPayload,
|
||
|
type: _routerreducertypes.ACTION_SERVER_ACTION,
|
||
|
mutable: {
|
||
|
globalMutable
|
||
|
},
|
||
|
cache: createEmptyCacheNode()
|
||
|
});
|
||
|
});
|
||
|
}, [
|
||
|
dispatch
|
||
|
]);
|
||
|
globalServerActionDispatcher = serverActionDispatcher;
|
||
|
}
|
||
|
/**
|
||
|
* Server response that only patches the cache and tree.
|
||
|
*/ function useChangeByServerResponse(dispatch) {
|
||
|
return (0, _react.useCallback)((previousTree, flightData, overrideCanonicalUrl)=>{
|
||
|
(0, _react.startTransition)(()=>{
|
||
|
dispatch({
|
||
|
type: _routerreducertypes.ACTION_SERVER_PATCH,
|
||
|
flightData,
|
||
|
previousTree,
|
||
|
overrideCanonicalUrl,
|
||
|
cache: createEmptyCacheNode(),
|
||
|
mutable: {
|
||
|
globalMutable
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
}, [
|
||
|
dispatch
|
||
|
]);
|
||
|
}
|
||
|
function useNavigate(dispatch) {
|
||
|
return (0, _react.useCallback)((href, navigateType, forceOptimisticNavigation, shouldScroll)=>{
|
||
|
const url = new URL((0, _addbasepath.addBasePath)(href), location.href);
|
||
|
globalMutable.pendingNavigatePath = (0, _createhreffromurl.createHrefFromUrl)(url);
|
||
|
return dispatch({
|
||
|
type: _routerreducertypes.ACTION_NAVIGATE,
|
||
|
url,
|
||
|
isExternalUrl: isExternalURL(url),
|
||
|
locationSearch: location.search,
|
||
|
forceOptimisticNavigation,
|
||
|
shouldScroll: shouldScroll != null ? shouldScroll : true,
|
||
|
navigateType,
|
||
|
cache: createEmptyCacheNode(),
|
||
|
mutable: {
|
||
|
globalMutable
|
||
|
}
|
||
|
});
|
||
|
}, [
|
||
|
dispatch
|
||
|
]);
|
||
|
}
|
||
|
/**
|
||
|
* The global router that wraps the application components.
|
||
|
*/ function Router(param) {
|
||
|
let { buildId, initialHead, initialTree, initialCanonicalUrl, children, assetPrefix } = param;
|
||
|
const initialState = (0, _react.useMemo)(()=>(0, _createinitialrouterstate.createInitialRouterState)({
|
||
|
buildId,
|
||
|
children,
|
||
|
initialCanonicalUrl,
|
||
|
initialTree,
|
||
|
initialParallelRoutes,
|
||
|
isServer,
|
||
|
location: !isServer ? window.location : null,
|
||
|
initialHead
|
||
|
}), [
|
||
|
buildId,
|
||
|
children,
|
||
|
initialCanonicalUrl,
|
||
|
initialTree,
|
||
|
initialHead
|
||
|
]);
|
||
|
const [{ tree, cache, prefetchCache, pushRef, focusAndScrollRef, canonicalUrl, nextUrl }, dispatch, sync] = (0, _usereducerwithdevtools.useReducerWithReduxDevtools)(_routerreducer.reducer, initialState);
|
||
|
(0, _react.useEffect)(()=>{
|
||
|
// Ensure initialParallelRoutes is cleaned up from memory once it's used.
|
||
|
initialParallelRoutes = null;
|
||
|
}, []);
|
||
|
// Add memoized pathname/query for useSearchParams and usePathname.
|
||
|
const { searchParams, pathname } = (0, _react.useMemo)(()=>{
|
||
|
const url = new URL(canonicalUrl, typeof window === "undefined" ? "http://n" : window.location.href);
|
||
|
return {
|
||
|
// This is turned into a readonly class in `useSearchParams`
|
||
|
searchParams: url.searchParams,
|
||
|
pathname: (0, _hasbasepath.hasBasePath)(url.pathname) ? (0, _removebasepath.removeBasePath)(url.pathname) : url.pathname
|
||
|
};
|
||
|
}, [
|
||
|
canonicalUrl
|
||
|
]);
|
||
|
const changeByServerResponse = useChangeByServerResponse(dispatch);
|
||
|
const navigate = useNavigate(dispatch);
|
||
|
useServerActionDispatcher(dispatch);
|
||
|
/**
|
||
|
* The app router that is exposed through `useRouter`. It's only concerned with dispatching actions to the reducer, does not hold state.
|
||
|
*/ const appRouter = (0, _react.useMemo)(()=>{
|
||
|
const routerInstance = {
|
||
|
back: ()=>window.history.back(),
|
||
|
forward: ()=>window.history.forward(),
|
||
|
prefetch: (href, options)=>{
|
||
|
// Don't prefetch for bots as they don't navigate.
|
||
|
// Don't prefetch during development (improves compilation performance)
|
||
|
if ((0, _isbot.isBot)(window.navigator.userAgent) || process.env.NODE_ENV === "development") {
|
||
|
return;
|
||
|
}
|
||
|
const url = new URL((0, _addbasepath.addBasePath)(href), location.href);
|
||
|
// External urls can't be prefetched in the same way.
|
||
|
if (isExternalURL(url)) {
|
||
|
return;
|
||
|
}
|
||
|
(0, _react.startTransition)(()=>{
|
||
|
var _options_kind;
|
||
|
dispatch({
|
||
|
type: _routerreducertypes.ACTION_PREFETCH,
|
||
|
url,
|
||
|
kind: (_options_kind = options == null ? void 0 : options.kind) != null ? _options_kind : _routerreducertypes.PrefetchKind.FULL
|
||
|
});
|
||
|
});
|
||
|
},
|
||
|
replace: (href, options)=>{
|
||
|
if (options === void 0) options = {};
|
||
|
(0, _react.startTransition)(()=>{
|
||
|
var _options_scroll;
|
||
|
navigate(href, "replace", Boolean(options.forceOptimisticNavigation), (_options_scroll = options.scroll) != null ? _options_scroll : true);
|
||
|
});
|
||
|
},
|
||
|
push: (href, options)=>{
|
||
|
if (options === void 0) options = {};
|
||
|
(0, _react.startTransition)(()=>{
|
||
|
var _options_scroll;
|
||
|
navigate(href, "push", Boolean(options.forceOptimisticNavigation), (_options_scroll = options.scroll) != null ? _options_scroll : true);
|
||
|
});
|
||
|
},
|
||
|
refresh: ()=>{
|
||
|
(0, _react.startTransition)(()=>{
|
||
|
dispatch({
|
||
|
type: _routerreducertypes.ACTION_REFRESH,
|
||
|
cache: createEmptyCacheNode(),
|
||
|
mutable: {
|
||
|
globalMutable
|
||
|
},
|
||
|
origin: window.location.origin
|
||
|
});
|
||
|
});
|
||
|
},
|
||
|
// @ts-ignore we don't want to expose this method at all
|
||
|
fastRefresh: ()=>{
|
||
|
if (process.env.NODE_ENV !== "development") {
|
||
|
throw new Error("fastRefresh can only be used in development mode. Please use refresh instead.");
|
||
|
} else {
|
||
|
(0, _react.startTransition)(()=>{
|
||
|
dispatch({
|
||
|
type: _routerreducertypes.ACTION_FAST_REFRESH,
|
||
|
cache: createEmptyCacheNode(),
|
||
|
mutable: {
|
||
|
globalMutable
|
||
|
},
|
||
|
origin: window.location.origin
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
return routerInstance;
|
||
|
}, [
|
||
|
dispatch,
|
||
|
navigate
|
||
|
]);
|
||
|
(0, _react.useEffect)(()=>{
|
||
|
// Exists for debugging purposes. Don't use in application code.
|
||
|
if (window.next) {
|
||
|
window.next.router = appRouter;
|
||
|
}
|
||
|
}, [
|
||
|
appRouter
|
||
|
]);
|
||
|
(0, _react.useEffect)(()=>{
|
||
|
globalMutable.refresh = appRouter.refresh;
|
||
|
}, [
|
||
|
appRouter.refresh
|
||
|
]);
|
||
|
if (process.env.NODE_ENV !== "production") {
|
||
|
// This hook is in a conditional but that is ok because `process.env.NODE_ENV` never changes
|
||
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||
|
(0, _react.useEffect)(()=>{
|
||
|
// Add `window.nd` for debugging purposes.
|
||
|
// This is not meant for use in applications as concurrent rendering will affect the cache/tree/router.
|
||
|
// @ts-ignore this is for debugging
|
||
|
window.nd = {
|
||
|
router: appRouter,
|
||
|
cache,
|
||
|
prefetchCache,
|
||
|
tree
|
||
|
};
|
||
|
}, [
|
||
|
appRouter,
|
||
|
cache,
|
||
|
prefetchCache,
|
||
|
tree
|
||
|
]);
|
||
|
}
|
||
|
(0, _react.useEffect)(()=>{
|
||
|
// If the app is restored from bfcache, it's possible that
|
||
|
// pushRef.mpaNavigation is true, which would mean that any re-render of this component
|
||
|
// would trigger the mpa navigation logic again from the lines below.
|
||
|
// This will restore the router to the initial state in the event that the app is restored from bfcache.
|
||
|
function handlePageShow(event) {
|
||
|
var _window_history_state;
|
||
|
if (!event.persisted || !((_window_history_state = window.history.state) == null ? void 0 : _window_history_state.tree)) return;
|
||
|
dispatch({
|
||
|
type: _routerreducertypes.ACTION_RESTORE,
|
||
|
url: new URL(window.location.href),
|
||
|
tree: window.history.state.tree
|
||
|
});
|
||
|
}
|
||
|
window.addEventListener("pageshow", handlePageShow);
|
||
|
return ()=>{
|
||
|
window.removeEventListener("pageshow", handlePageShow);
|
||
|
};
|
||
|
}, [
|
||
|
dispatch
|
||
|
]);
|
||
|
// When mpaNavigation flag is set do a hard navigation to the new url.
|
||
|
// Infinitely suspend because we don't actually want to rerender any child
|
||
|
// components with the new URL and any entangled state updates shouldn't
|
||
|
// commit either (eg: useTransition isPending should stay true until the page
|
||
|
// unloads).
|
||
|
//
|
||
|
// This is a side effect in render. Don't try this at home, kids. It's
|
||
|
// probably safe because we know this is a singleton component and it's never
|
||
|
// in <Offscreen>. At least I hope so. (It will run twice in dev strict mode,
|
||
|
// but that's... fine?)
|
||
|
if (pushRef.mpaNavigation) {
|
||
|
// if there's a re-render, we don't want to trigger another redirect if one is already in flight to the same URL
|
||
|
if (globalMutable.pendingMpaPath !== canonicalUrl) {
|
||
|
const location1 = window.location;
|
||
|
if (pushRef.pendingPush) {
|
||
|
location1.assign(canonicalUrl);
|
||
|
} else {
|
||
|
location1.replace(canonicalUrl);
|
||
|
}
|
||
|
globalMutable.pendingMpaPath = canonicalUrl;
|
||
|
}
|
||
|
// TODO-APP: Should we listen to navigateerror here to catch failed
|
||
|
// navigations somehow? And should we call window.stop() if a SPA navigation
|
||
|
// should interrupt an MPA one?
|
||
|
(0, _react.use)((0, _infinitepromise.createInfinitePromise)());
|
||
|
}
|
||
|
/**
|
||
|
* Handle popstate event, this is used to handle back/forward in the browser.
|
||
|
* By default dispatches ACTION_RESTORE, however if the history entry was not pushed/replaced by app-router it will reload the page.
|
||
|
* That case can happen when the old router injected the history entry.
|
||
|
*/ const onPopState = (0, _react.useCallback)((param)=>{
|
||
|
let { state } = param;
|
||
|
if (!state) {
|
||
|
// TODO-APP: this case only happens when pushState/replaceState was called outside of Next.js. It should probably reload the page in this case.
|
||
|
return;
|
||
|
}
|
||
|
// This case happens when the history entry was pushed by the `pages` router.
|
||
|
if (!state.__NA) {
|
||
|
window.location.reload();
|
||
|
return;
|
||
|
}
|
||
|
// @ts-ignore useTransition exists
|
||
|
// TODO-APP: Ideally the back button should not use startTransition as it should apply the updates synchronously
|
||
|
// Without startTransition works if the cache is there for this path
|
||
|
(0, _react.startTransition)(()=>{
|
||
|
dispatch({
|
||
|
type: _routerreducertypes.ACTION_RESTORE,
|
||
|
url: new URL(window.location.href),
|
||
|
tree: state.tree
|
||
|
});
|
||
|
});
|
||
|
}, [
|
||
|
dispatch
|
||
|
]);
|
||
|
// Register popstate event to call onPopstate.
|
||
|
(0, _react.useEffect)(()=>{
|
||
|
window.addEventListener("popstate", onPopState);
|
||
|
return ()=>{
|
||
|
window.removeEventListener("popstate", onPopState);
|
||
|
};
|
||
|
}, [
|
||
|
onPopState
|
||
|
]);
|
||
|
const head = (0, _react.useMemo)(()=>{
|
||
|
return (0, _findheadincache.findHeadInCache)(cache, tree[1]);
|
||
|
}, [
|
||
|
cache,
|
||
|
tree
|
||
|
]);
|
||
|
let content = /*#__PURE__*/ _react.default.createElement(_redirectboundary.RedirectBoundary, null, head, cache.subTreeData, /*#__PURE__*/ _react.default.createElement(_approuterannouncer.AppRouterAnnouncer, {
|
||
|
tree: tree
|
||
|
}));
|
||
|
if (process.env.NODE_ENV !== "production") {
|
||
|
if (typeof window !== "undefined") {
|
||
|
const DevRootNotFoundBoundary = require("./dev-root-not-found-boundary").DevRootNotFoundBoundary;
|
||
|
content = /*#__PURE__*/ _react.default.createElement(DevRootNotFoundBoundary, null, content);
|
||
|
}
|
||
|
const HotReloader = require("./react-dev-overlay/hot-reloader-client").default;
|
||
|
content = /*#__PURE__*/ _react.default.createElement(HotReloader, {
|
||
|
assetPrefix: assetPrefix
|
||
|
}, content);
|
||
|
}
|
||
|
return /*#__PURE__*/ _react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/ _react.default.createElement(HistoryUpdater, {
|
||
|
tree: tree,
|
||
|
pushRef: pushRef,
|
||
|
canonicalUrl: canonicalUrl,
|
||
|
sync: sync
|
||
|
}), /*#__PURE__*/ _react.default.createElement(_hooksclientcontextsharedruntime.PathnameContext.Provider, {
|
||
|
value: pathname
|
||
|
}, /*#__PURE__*/ _react.default.createElement(_hooksclientcontextsharedruntime.SearchParamsContext.Provider, {
|
||
|
value: searchParams
|
||
|
}, /*#__PURE__*/ _react.default.createElement(_approutercontextsharedruntime.GlobalLayoutRouterContext.Provider, {
|
||
|
value: {
|
||
|
buildId,
|
||
|
changeByServerResponse,
|
||
|
tree,
|
||
|
focusAndScrollRef,
|
||
|
nextUrl
|
||
|
}
|
||
|
}, /*#__PURE__*/ _react.default.createElement(_approutercontextsharedruntime.AppRouterContext.Provider, {
|
||
|
value: appRouter
|
||
|
}, /*#__PURE__*/ _react.default.createElement(_approutercontextsharedruntime.LayoutRouterContext.Provider, {
|
||
|
value: {
|
||
|
childNodes: cache.parallelRoutes,
|
||
|
tree: tree,
|
||
|
// Root node always has `url`
|
||
|
// Provided in AppTreeContext to ensure it can be overwritten in layout-router
|
||
|
url: canonicalUrl
|
||
|
}
|
||
|
}, content))))));
|
||
|
}
|
||
|
function AppRouter(props) {
|
||
|
const { globalErrorComponent, ...rest } = props;
|
||
|
return /*#__PURE__*/ _react.default.createElement(_errorboundary.ErrorBoundary, {
|
||
|
errorComponent: globalErrorComponent
|
||
|
}, /*#__PURE__*/ _react.default.createElement(Router, rest));
|
||
|
}
|
||
|
|
||
|
if ((typeof exports.default === 'function' || (typeof exports.default === 'object' && exports.default !== null)) && typeof exports.default.__esModule === 'undefined') {
|
||
|
Object.defineProperty(exports.default, '__esModule', { value: true });
|
||
|
Object.assign(exports.default, exports);
|
||
|
module.exports = exports.default;
|
||
|
}
|
||
|
|
||
|
//# sourceMappingURL=app-router.js.map
|