create project
This commit is contained in:
372
kitabcitab/node_modules/next/dist/client/components/layout-router.js
generated
vendored
Normal file
372
kitabcitab/node_modules/next/dist/client/components/layout-router.js
generated
vendored
Normal file
@@ -0,0 +1,372 @@
|
||||
"use client";
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = OuterLayoutRouter;
|
||||
exports.InnerLayoutRouter = InnerLayoutRouter;
|
||||
var _extends = require("@swc/helpers/lib/_extends.js").default;
|
||||
var _interop_require_default = require("@swc/helpers/lib/_interop_require_default.js").default;
|
||||
var _interop_require_wildcard = require("@swc/helpers/lib/_interop_require_wildcard.js").default;
|
||||
var _react = _interop_require_wildcard(require("react"));
|
||||
var _reactDom = _interop_require_default(require("react-dom"));
|
||||
var _appRouterContext = require("../../shared/lib/app-router-context");
|
||||
var _appRouter = require("./app-router");
|
||||
var _infinitePromise = require("./infinite-promise");
|
||||
var _errorBoundary = require("./error-boundary");
|
||||
var _matchSegments = require("./match-segments");
|
||||
var _navigation = require("./navigation");
|
||||
function OuterLayoutRouter({ parallelRouterKey , segmentPath , childProp , error , errorStyles , templateStyles , loading , loadingStyles , hasLoading , template , notFound , notFoundStyles , rootLayoutIncluded }) {
|
||||
const context = (0, _react).useContext(_appRouterContext.LayoutRouterContext);
|
||||
if (!context) {
|
||||
throw new Error('invariant expected layout router to be mounted');
|
||||
}
|
||||
const { childNodes , tree , url } = context;
|
||||
// Get the current parallelRouter cache node
|
||||
let childNodesForParallelRouter = childNodes.get(parallelRouterKey);
|
||||
// If the parallel router cache node does not exist yet, create it.
|
||||
// This writes to the cache when there is no item in the cache yet. It never *overwrites* existing cache items which is why it's safe in concurrent mode.
|
||||
if (!childNodesForParallelRouter) {
|
||||
childNodes.set(parallelRouterKey, new Map());
|
||||
childNodesForParallelRouter = childNodes.get(parallelRouterKey);
|
||||
}
|
||||
// Get the active segment in the tree
|
||||
// The reason arrays are used in the data format is that these are transferred from the server to the browser so it's optimized to save bytes.
|
||||
const treeSegment = tree[1][parallelRouterKey][0];
|
||||
const childPropSegment = Array.isArray(childProp.segment) ? childProp.segment[1] : childProp.segment;
|
||||
// If segment is an array it's a dynamic route and we want to read the dynamic route value as the segment to get from the cache.
|
||||
const currentChildSegment = Array.isArray(treeSegment) ? treeSegment[1] : treeSegment;
|
||||
/**
|
||||
* Decides which segments to keep rendering, all segments that are not active will be wrapped in `<Offscreen>`.
|
||||
*/ // TODO-APP: Add handling of `<Offscreen>` when it's available.
|
||||
const preservedSegments = [
|
||||
currentChildSegment
|
||||
];
|
||||
return /*#__PURE__*/ _react.default.createElement(_react.default.Fragment, null, preservedSegments.map((preservedSegment)=>{
|
||||
return(/*
|
||||
- Error boundary
|
||||
- Only renders error boundary if error component is provided.
|
||||
- Rendered for each segment to ensure they have their own error state.
|
||||
- Loading boundary
|
||||
- Only renders suspense boundary if loading components is provided.
|
||||
- Rendered for each segment to ensure they have their own loading state.
|
||||
- Passed to the router during rendering to ensure it can be immediately rendered when suspending on a Flight fetch.
|
||||
*/ /*#__PURE__*/ _react.default.createElement(_appRouterContext.TemplateContext.Provider, {
|
||||
key: preservedSegment,
|
||||
value: /*#__PURE__*/ _react.default.createElement(_errorBoundary.ErrorBoundary, {
|
||||
errorComponent: error,
|
||||
errorStyles: errorStyles
|
||||
}, /*#__PURE__*/ _react.default.createElement(LoadingBoundary, {
|
||||
hasLoading: hasLoading,
|
||||
loading: loading,
|
||||
loadingStyles: loadingStyles
|
||||
}, /*#__PURE__*/ _react.default.createElement(NotFoundBoundary, {
|
||||
notFound: notFound,
|
||||
notFoundStyles: notFoundStyles
|
||||
}, /*#__PURE__*/ _react.default.createElement(RedirectBoundary, null, /*#__PURE__*/ _react.default.createElement(InnerLayoutRouter, {
|
||||
parallelRouterKey: parallelRouterKey,
|
||||
url: url,
|
||||
tree: tree,
|
||||
childNodes: childNodesForParallelRouter,
|
||||
childProp: childPropSegment === preservedSegment ? childProp : null,
|
||||
segmentPath: segmentPath,
|
||||
path: preservedSegment,
|
||||
isActive: currentChildSegment === preservedSegment,
|
||||
rootLayoutIncluded: rootLayoutIncluded
|
||||
})))))
|
||||
}, /*#__PURE__*/ _react.default.createElement(_react.default.Fragment, null, templateStyles, template)));
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add refetch marker to router state at the point of the current layout segment.
|
||||
* This ensures the response returned is not further down than the current layout segment.
|
||||
*/ function walkAddRefetch(segmentPathToWalk, treeToRecreate) {
|
||||
if (segmentPathToWalk) {
|
||||
const [segment, parallelRouteKey] = segmentPathToWalk;
|
||||
const isLast = segmentPathToWalk.length === 2;
|
||||
if ((0, _matchSegments).matchSegment(treeToRecreate[0], segment)) {
|
||||
if (treeToRecreate[1].hasOwnProperty(parallelRouteKey)) {
|
||||
if (isLast) {
|
||||
const subTree = walkAddRefetch(undefined, treeToRecreate[1][parallelRouteKey]);
|
||||
return [
|
||||
treeToRecreate[0],
|
||||
_extends({}, treeToRecreate[1], {
|
||||
[parallelRouteKey]: [
|
||||
subTree[0],
|
||||
subTree[1],
|
||||
subTree[2],
|
||||
'refetch',
|
||||
]
|
||||
}),
|
||||
];
|
||||
}
|
||||
return [
|
||||
treeToRecreate[0],
|
||||
_extends({}, treeToRecreate[1], {
|
||||
[parallelRouteKey]: walkAddRefetch(segmentPathToWalk.slice(2), treeToRecreate[1][parallelRouteKey])
|
||||
}),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
return treeToRecreate;
|
||||
}
|
||||
// TODO-APP: Replace with new React API for finding dom nodes without a `ref` when available
|
||||
/**
|
||||
* Wraps ReactDOM.findDOMNode with additional logic to hide React Strict Mode warning
|
||||
*/ function findDOMNode(instance) {
|
||||
// Tree-shake for server bundle
|
||||
if (typeof window === undefined) return null;
|
||||
// Only apply strict mode warning when not in production
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const originalConsoleError = console.error;
|
||||
try {
|
||||
console.error = (...messages)=>{
|
||||
// Ignore strict mode warning for the findDomNode call below
|
||||
if (!messages[0].includes('Warning: %s is deprecated in StrictMode.')) {
|
||||
originalConsoleError(...messages);
|
||||
}
|
||||
};
|
||||
return _reactDom.default.findDOMNode(instance);
|
||||
} finally{
|
||||
console.error = originalConsoleError;
|
||||
}
|
||||
}
|
||||
return _reactDom.default.findDOMNode(instance);
|
||||
}
|
||||
/**
|
||||
* Check if the top of the HTMLElement is in the viewport.
|
||||
*/ function topOfElementInViewport(element) {
|
||||
const rect = element.getBoundingClientRect();
|
||||
return rect.top >= 0;
|
||||
}
|
||||
class ScrollAndFocusHandler extends _react.default.Component {
|
||||
componentDidMount() {
|
||||
// Handle scroll and focus, it's only applied once in the first useEffect that triggers that changed.
|
||||
const { focusAndScrollRef } = this.props;
|
||||
const domNode = findDOMNode(this);
|
||||
if (focusAndScrollRef.apply && domNode instanceof HTMLElement) {
|
||||
// State is mutated to ensure that the focus and scroll is applied only once.
|
||||
focusAndScrollRef.apply = false;
|
||||
// Set focus on the element
|
||||
domNode.focus();
|
||||
// Only scroll into viewport when the layout is not visible currently.
|
||||
if (!topOfElementInViewport(domNode)) {
|
||||
const htmlElement = document.documentElement;
|
||||
const existing = htmlElement.style.scrollBehavior;
|
||||
htmlElement.style.scrollBehavior = 'auto';
|
||||
// In Chrome-based browsers we need to force reflow before calling `scrollTo`.
|
||||
// Otherwise it will not pickup the change in scrollBehavior
|
||||
// More info here: https://github.com/vercel/next.js/issues/40719#issuecomment-1336248042
|
||||
htmlElement.getClientRects();
|
||||
domNode.scrollIntoView();
|
||||
htmlElement.style.scrollBehavior = existing;
|
||||
}
|
||||
}
|
||||
}
|
||||
render() {
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
function InnerLayoutRouter({ parallelRouterKey , url , childNodes , childProp , segmentPath , tree , // TODO-APP: implement `<Offscreen>` when available.
|
||||
// isActive,
|
||||
path , rootLayoutIncluded }) {
|
||||
const context = (0, _react).useContext(_appRouterContext.GlobalLayoutRouterContext);
|
||||
if (!context) {
|
||||
throw new Error('invariant global layout router not mounted');
|
||||
}
|
||||
const { changeByServerResponse , tree: fullTree , focusAndScrollRef } = context;
|
||||
// Read segment path from the parallel router cache node.
|
||||
let childNode = childNodes.get(path);
|
||||
// If childProp is available this means it's the Flight / SSR case.
|
||||
if (childProp && // TODO-APP: verify if this can be null based on user code
|
||||
childProp.current !== null) {
|
||||
if (childNode && childNode.status === _appRouterContext.CacheStates.LAZY_INITIALIZED) {
|
||||
// @ts-expect-error TODO-APP: handle changing of the type
|
||||
childNode.status = _appRouterContext.CacheStates.READY;
|
||||
// @ts-expect-error TODO-APP: handle changing of the type
|
||||
childNode.subTreeData = childProp.current;
|
||||
// Mutates the prop in order to clean up the memory associated with the subTreeData as it is now part of the cache.
|
||||
childProp.current = null;
|
||||
} else {
|
||||
// Add the segment's subTreeData to the cache.
|
||||
// This writes to the cache when there is no item in the cache yet. It never *overwrites* existing cache items which is why it's safe in concurrent mode.
|
||||
childNodes.set(path, {
|
||||
status: _appRouterContext.CacheStates.READY,
|
||||
data: null,
|
||||
subTreeData: childProp.current,
|
||||
parallelRoutes: new Map()
|
||||
});
|
||||
// Mutates the prop in order to clean up the memory associated with the subTreeData as it is now part of the cache.
|
||||
childProp.current = null;
|
||||
// In the above case childNode was set on childNodes, so we have to get it from the cacheNodes again.
|
||||
childNode = childNodes.get(path);
|
||||
}
|
||||
}
|
||||
// When childNode is not available during rendering client-side we need to fetch it from the server.
|
||||
if (!childNode || childNode.status === _appRouterContext.CacheStates.LAZY_INITIALIZED) {
|
||||
/**
|
||||
* Router state with refetch marker added
|
||||
*/ // TODO-APP: remove ''
|
||||
const refetchTree = walkAddRefetch([
|
||||
'',
|
||||
...segmentPath
|
||||
], fullTree);
|
||||
/**
|
||||
* Flight data fetch kicked off during render and put into the cache.
|
||||
*/ childNodes.set(path, {
|
||||
status: _appRouterContext.CacheStates.DATA_FETCH,
|
||||
data: (0, _appRouter).fetchServerResponse(new URL(url, location.origin), refetchTree),
|
||||
subTreeData: null,
|
||||
head: childNode && childNode.status === _appRouterContext.CacheStates.LAZY_INITIALIZED ? childNode.head : undefined,
|
||||
parallelRoutes: childNode && childNode.status === _appRouterContext.CacheStates.LAZY_INITIALIZED ? childNode.parallelRoutes : new Map()
|
||||
});
|
||||
// In the above case childNode was set on childNodes, so we have to get it from the cacheNodes again.
|
||||
childNode = childNodes.get(path);
|
||||
}
|
||||
// This case should never happen so it throws an error. It indicates there's a bug in the Next.js.
|
||||
if (!childNode) {
|
||||
throw new Error('Child node should always exist');
|
||||
}
|
||||
// This case should never happen so it throws an error. It indicates there's a bug in the Next.js.
|
||||
if (childNode.subTreeData && childNode.data) {
|
||||
throw new Error('Child node should not have both subTreeData and data');
|
||||
}
|
||||
// If cache node has a data request we have to unwrap response by `use` and update the cache.
|
||||
if (childNode.data) {
|
||||
/**
|
||||
* Flight response data
|
||||
*/ // When the data has not resolved yet `use` will suspend here.
|
||||
const [flightData, overrideCanonicalUrl] = (0, _react).use(childNode.data);
|
||||
// Handle case when navigating to page in `pages` from `app`
|
||||
if (typeof flightData === 'string') {
|
||||
window.location.href = url;
|
||||
return null;
|
||||
}
|
||||
// segmentPath from the server does not match the layout's segmentPath
|
||||
childNode.data = null;
|
||||
// setTimeout is used to start a new transition during render, this is an intentional hack around React.
|
||||
setTimeout(()=>{
|
||||
// @ts-ignore startTransition exists
|
||||
_react.default.startTransition(()=>{
|
||||
changeByServerResponse(fullTree, flightData, overrideCanonicalUrl);
|
||||
});
|
||||
});
|
||||
// Suspend infinitely as `changeByServerResponse` will cause a different part of the tree to be rendered.
|
||||
(0, _react).use((0, _infinitePromise).createInfinitePromise());
|
||||
}
|
||||
// If cache node has no subTreeData and no data request we have to infinitely suspend as the data will likely flow in from another place.
|
||||
// TODO-APP: double check users can't return null in a component that will kick in here.
|
||||
if (!childNode.subTreeData) {
|
||||
(0, _react).use((0, _infinitePromise).createInfinitePromise());
|
||||
}
|
||||
const subtree = // The layout router context narrows down tree and childNodes at each level.
|
||||
/*#__PURE__*/ _react.default.createElement(_appRouterContext.LayoutRouterContext.Provider, {
|
||||
value: {
|
||||
tree: tree[1][parallelRouterKey],
|
||||
childNodes: childNode.parallelRoutes,
|
||||
// TODO-APP: overriding of url for parallel routes
|
||||
url: url
|
||||
}
|
||||
}, childNode.subTreeData);
|
||||
// Ensure root layout is not wrapped in a div as the root layout renders `<html>`
|
||||
return rootLayoutIncluded ? /*#__PURE__*/ _react.default.createElement(ScrollAndFocusHandler, {
|
||||
focusAndScrollRef: focusAndScrollRef
|
||||
}, subtree) : subtree;
|
||||
}
|
||||
/**
|
||||
* Renders suspense boundary with the provided "loading" property as the fallback.
|
||||
* If no loading property is provided it renders the children without a suspense boundary.
|
||||
*/ function LoadingBoundary({ children , loading , loadingStyles , hasLoading }) {
|
||||
if (hasLoading) {
|
||||
return /*#__PURE__*/ _react.default.createElement(_react.default.Suspense, {
|
||||
fallback: /*#__PURE__*/ _react.default.createElement(_react.default.Fragment, null, loadingStyles, loading)
|
||||
}, children);
|
||||
}
|
||||
return /*#__PURE__*/ _react.default.createElement(_react.default.Fragment, null, children);
|
||||
}
|
||||
function HandleRedirect({ redirect }) {
|
||||
const router = (0, _navigation).useRouter();
|
||||
(0, _react).useEffect(()=>{
|
||||
router.replace(redirect, {});
|
||||
}, [
|
||||
redirect,
|
||||
router
|
||||
]);
|
||||
return null;
|
||||
}
|
||||
class RedirectErrorBoundary extends _react.default.Component {
|
||||
static getDerivedStateFromError(error) {
|
||||
var ref;
|
||||
if (error == null ? void 0 : (ref = error.digest) == null ? void 0 : ref.startsWith('NEXT_REDIRECT')) {
|
||||
const url = error.digest.split(';')[1];
|
||||
return {
|
||||
redirect: url
|
||||
};
|
||||
}
|
||||
// Re-throw if error is not for redirect
|
||||
throw error;
|
||||
}
|
||||
render() {
|
||||
const redirect = this.state.redirect;
|
||||
if (redirect !== null) {
|
||||
return /*#__PURE__*/ _react.default.createElement(HandleRedirect, {
|
||||
redirect: redirect
|
||||
});
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
constructor(props){
|
||||
super(props);
|
||||
this.state = {
|
||||
redirect: null
|
||||
};
|
||||
}
|
||||
}
|
||||
function RedirectBoundary({ children }) {
|
||||
const router = (0, _navigation).useRouter();
|
||||
return /*#__PURE__*/ _react.default.createElement(RedirectErrorBoundary, {
|
||||
router: router
|
||||
}, children);
|
||||
}
|
||||
class NotFoundErrorBoundary extends _react.default.Component {
|
||||
static getDerivedStateFromError(error) {
|
||||
if ((error == null ? void 0 : error.digest) === 'NEXT_NOT_FOUND') {
|
||||
return {
|
||||
notFoundTriggered: true
|
||||
};
|
||||
}
|
||||
// Re-throw if error is not for 404
|
||||
throw error;
|
||||
}
|
||||
render() {
|
||||
if (this.state.notFoundTriggered) {
|
||||
return /*#__PURE__*/ _react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/ _react.default.createElement("meta", {
|
||||
name: "robots",
|
||||
content: "noindex"
|
||||
}), this.props.notFoundStyles, this.props.notFound);
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
constructor(props){
|
||||
super(props);
|
||||
this.state = {
|
||||
notFoundTriggered: false
|
||||
};
|
||||
}
|
||||
}
|
||||
function NotFoundBoundary({ notFound , notFoundStyles , children }) {
|
||||
return notFound ? /*#__PURE__*/ _react.default.createElement(NotFoundErrorBoundary, {
|
||||
notFound: notFound,
|
||||
notFoundStyles: notFoundStyles
|
||||
}, children) : /*#__PURE__*/ _react.default.createElement(_react.default.Fragment, null, children);
|
||||
}
|
||||
|
||||
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=layout-router.js.map
|
||||
Reference in New Issue
Block a user