"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.matchesMiddleware = matchesMiddleware; exports.isLocalURL = isLocalURL; exports.interpolateAs = interpolateAs; exports.resolveHref = resolveHref; exports.createKey = createKey; exports.default = void 0; var _async_to_generator = require("@swc/helpers/lib/_async_to_generator.js").default; 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 _normalizeTrailingSlash = require("../../../client/normalize-trailing-slash"); var _removeTrailingSlash = require("./utils/remove-trailing-slash"); var _routeLoader = require("../../../client/route-loader"); var _script = require("../../../client/script"); var _isError = _interop_require_wildcard(require("../../../lib/is-error")); var _denormalizePagePath = require("../page-path/denormalize-page-path"); var _normalizeLocalePath = require("../i18n/normalize-locale-path"); var _mitt = _interop_require_default(require("../mitt")); var _utils = require("../utils"); var _isDynamic = require("./utils/is-dynamic"); var _parseRelativeUrl = require("./utils/parse-relative-url"); var _querystring = require("./utils/querystring"); var _resolveRewrites = _interop_require_default(require("./utils/resolve-rewrites")); var _routeMatcher = require("./utils/route-matcher"); var _routeRegex = require("./utils/route-regex"); var _formatUrl = require("./utils/format-url"); var _detectDomainLocale = require("../../../client/detect-domain-locale"); var _parsePath = require("./utils/parse-path"); var _addLocale = require("../../../client/add-locale"); var _removeLocale = require("../../../client/remove-locale"); var _removeBasePath = require("../../../client/remove-base-path"); var _addBasePath = require("../../../client/add-base-path"); var _hasBasePath = require("../../../client/has-base-path"); var _getNextPathnameInfo = require("./utils/get-next-pathname-info"); var _formatNextPathnameInfo = require("./utils/format-next-pathname-info"); var _compareStates = require("./utils/compare-states"); var _isBot = require("./utils/is-bot"); function buildCancellationError() { return Object.assign(new Error('Route Cancelled'), { cancelled: true }); } function matchesMiddleware(options) { return _matchesMiddleware.apply(this, arguments); } function _matchesMiddleware() { _matchesMiddleware = _async_to_generator(function*(options) { const matchers = yield Promise.resolve(options.router.pageLoader.getMiddleware()); if (!matchers) return false; const { pathname: asPathname } = (0, _parsePath).parsePath(options.asPath); // remove basePath first since path prefix has to be in the order of `/${basePath}/${locale}` const cleanedAs = (0, _hasBasePath).hasBasePath(asPathname) ? (0, _removeBasePath).removeBasePath(asPathname) : asPathname; const asWithBasePathAndLocale = (0, _addBasePath).addBasePath((0, _addLocale).addLocale(cleanedAs, options.locale)); // Check only path match on client. Matching "has" should be done on server // where we can access more info such as headers, HttpOnly cookie, etc. return matchers.some((m)=>new RegExp(m.regexp).test(asWithBasePathAndLocale)); }); return _matchesMiddleware.apply(this, arguments); } function stripOrigin(url) { const origin = (0, _utils).getLocationOrigin(); return url.startsWith(origin) ? url.substring(origin.length) : url; } function omit(object, keys) { const omitted = {}; Object.keys(object).forEach((key)=>{ if (!keys.includes(key)) { omitted[key] = object[key]; } }); return omitted; } function isLocalURL(url) { // prevent a hydration mismatch on href for url with anchor refs if (!(0, _utils).isAbsoluteUrl(url)) return true; try { // absolute urls can be local if they are on the same origin const locationOrigin = (0, _utils).getLocationOrigin(); const resolved = new URL(url, locationOrigin); return resolved.origin === locationOrigin && (0, _hasBasePath).hasBasePath(resolved.pathname); } catch (_) { return false; } } function interpolateAs(route, asPathname, query) { let interpolatedRoute = ''; const dynamicRegex = (0, _routeRegex).getRouteRegex(route); const dynamicGroups = dynamicRegex.groups; const dynamicMatches = // Try to match the dynamic route against the asPath (asPathname !== route ? (0, _routeMatcher).getRouteMatcher(dynamicRegex)(asPathname) : '') || // Fall back to reading the values from the href // TODO: should this take priority; also need to change in the router. query; interpolatedRoute = route; const params = Object.keys(dynamicGroups); if (!params.every((param)=>{ let value = dynamicMatches[param] || ''; const { repeat , optional } = dynamicGroups[param]; // support single-level catch-all // TODO: more robust handling for user-error (passing `/`) let replaced = `[${repeat ? '...' : ''}${param}]`; if (optional) { replaced = `${!value ? '/' : ''}[${replaced}]`; } if (repeat && !Array.isArray(value)) value = [ value ]; return (optional || param in dynamicMatches) && // Interpolate group into data URL if present (interpolatedRoute = interpolatedRoute.replace(replaced, repeat ? value.map(// these values should be fully encoded instead of just // path delimiter escaped since they are being inserted // into the URL and we expect URL encoded segments // when parsing dynamic route params (segment)=>encodeURIComponent(segment)).join('/') : encodeURIComponent(value)) || '/'); })) { interpolatedRoute = '' // did not satisfy all requirements ; // n.b. We ignore this error because we handle warning for this case in // development in the `` component directly. } return { params, result: interpolatedRoute }; } function resolveHref(router, href, resolveAs) { // we use a dummy base url for relative urls let base; let urlAsString = typeof href === 'string' ? href : (0, _formatUrl).formatWithValidation(href); // repeated slashes and backslashes in the URL are considered // invalid and will never match a Next.js page/file const urlProtoMatch = urlAsString.match(/^[a-zA-Z]{1,}:\/\//); const urlAsStringNoProto = urlProtoMatch ? urlAsString.slice(urlProtoMatch[0].length) : urlAsString; const urlParts = urlAsStringNoProto.split('?'); if ((urlParts[0] || '').match(/(\/\/|\\)/)) { console.error(`Invalid href passed to next/router: ${urlAsString}, repeated forward-slashes (//) or backslashes \\ are not valid in the href`); const normalizedUrl = (0, _utils).normalizeRepeatedSlashes(urlAsStringNoProto); urlAsString = (urlProtoMatch ? urlProtoMatch[0] : '') + normalizedUrl; } // Return because it cannot be routed by the Next.js router if (!isLocalURL(urlAsString)) { return resolveAs ? [ urlAsString ] : urlAsString; } try { base = new URL(urlAsString.startsWith('#') ? router.asPath : router.pathname, 'http://n'); } catch (_) { // fallback to / for invalid asPath values e.g. // base = new URL('/', 'http://n'); } try { const finalUrl = new URL(urlAsString, base); finalUrl.pathname = (0, _normalizeTrailingSlash).normalizePathTrailingSlash(finalUrl.pathname); let interpolatedAs = ''; if ((0, _isDynamic).isDynamicRoute(finalUrl.pathname) && finalUrl.searchParams && resolveAs) { const query = (0, _querystring).searchParamsToUrlQuery(finalUrl.searchParams); const { result , params } = interpolateAs(finalUrl.pathname, finalUrl.pathname, query); if (result) { interpolatedAs = (0, _formatUrl).formatWithValidation({ pathname: result, hash: finalUrl.hash, query: omit(query, params) }); } } // if the origin didn't change, it means we received a relative href const resolvedHref = finalUrl.origin === base.origin ? finalUrl.href.slice(finalUrl.origin.length) : finalUrl.href; return resolveAs ? [ resolvedHref, interpolatedAs || resolvedHref ] : resolvedHref; } catch (_1) { return resolveAs ? [ urlAsString ] : urlAsString; } } function prepareUrlAs(router, url, as) { // If url and as provided as an object representation, // we'll format them into the string version here. let [resolvedHref, resolvedAs] = resolveHref(router, url, true); const origin = (0, _utils).getLocationOrigin(); const hrefHadOrigin = resolvedHref.startsWith(origin); const asHadOrigin = resolvedAs && resolvedAs.startsWith(origin); resolvedHref = stripOrigin(resolvedHref); resolvedAs = resolvedAs ? stripOrigin(resolvedAs) : resolvedAs; const preparedUrl = hrefHadOrigin ? resolvedHref : (0, _addBasePath).addBasePath(resolvedHref); const preparedAs = as ? stripOrigin(resolveHref(router, as)) : resolvedAs || resolvedHref; return { url: preparedUrl, as: asHadOrigin ? preparedAs : (0, _addBasePath).addBasePath(preparedAs) }; } function resolveDynamicRoute(pathname, pages) { const cleanPathname = (0, _removeTrailingSlash).removeTrailingSlash((0, _denormalizePagePath).denormalizePagePath(pathname)); if (cleanPathname === '/404' || cleanPathname === '/_error') { return pathname; } // handle resolving href for dynamic routes if (!pages.includes(cleanPathname)) { // eslint-disable-next-line array-callback-return pages.some((page)=>{ if ((0, _isDynamic).isDynamicRoute(page) && (0, _routeRegex).getRouteRegex(page).re.test(cleanPathname)) { pathname = page; return true; } }); } return (0, _removeTrailingSlash).removeTrailingSlash(pathname); } function getMiddlewareData(source, response, options) { const nextConfig = { basePath: options.router.basePath, i18n: { locales: options.router.locales }, trailingSlash: Boolean(process.env.__NEXT_TRAILING_SLASH) }; const rewriteHeader = response.headers.get('x-nextjs-rewrite'); let rewriteTarget = rewriteHeader || response.headers.get('x-nextjs-matched-path'); const matchedPath = response.headers.get('x-matched-path'); if (matchedPath && !rewriteTarget && !matchedPath.includes('__next_data_catchall') && !matchedPath.includes('/_error') && !matchedPath.includes('/404')) { // leverage x-matched-path to detect next.config.js rewrites rewriteTarget = matchedPath; } if (rewriteTarget) { if (rewriteTarget.startsWith('/')) { const parsedRewriteTarget = (0, _parseRelativeUrl).parseRelativeUrl(rewriteTarget); const pathnameInfo = (0, _getNextPathnameInfo).getNextPathnameInfo(parsedRewriteTarget.pathname, { nextConfig, parseData: true }); let fsPathname = (0, _removeTrailingSlash).removeTrailingSlash(pathnameInfo.pathname); return Promise.all([ options.router.pageLoader.getPageList(), (0, _routeLoader).getClientBuildManifest(), ]).then(([pages, { __rewrites: rewrites }])=>{ let as = (0, _addLocale).addLocale(pathnameInfo.pathname, pathnameInfo.locale); if ((0, _isDynamic).isDynamicRoute(as) || !rewriteHeader && pages.includes((0, _normalizeLocalePath).normalizeLocalePath((0, _removeBasePath).removeBasePath(as), options.router.locales).pathname)) { const parsedSource = (0, _getNextPathnameInfo).getNextPathnameInfo((0, _parseRelativeUrl).parseRelativeUrl(source).pathname, { parseData: true }); as = (0, _addBasePath).addBasePath(parsedSource.pathname); parsedRewriteTarget.pathname = as; } if (process.env.__NEXT_HAS_REWRITES) { const result = (0, _resolveRewrites).default(as, pages, rewrites, parsedRewriteTarget.query, (path)=>resolveDynamicRoute(path, pages), options.router.locales); if (result.matchedPage) { parsedRewriteTarget.pathname = result.parsedAs.pathname; as = parsedRewriteTarget.pathname; Object.assign(parsedRewriteTarget.query, result.parsedAs.query); } } else if (!pages.includes(fsPathname)) { const resolvedPathname = resolveDynamicRoute(fsPathname, pages); if (resolvedPathname !== fsPathname) { fsPathname = resolvedPathname; } } const resolvedHref = !pages.includes(fsPathname) ? resolveDynamicRoute((0, _normalizeLocalePath).normalizeLocalePath((0, _removeBasePath).removeBasePath(parsedRewriteTarget.pathname), options.router.locales).pathname, pages) : fsPathname; if ((0, _isDynamic).isDynamicRoute(resolvedHref)) { const matches = (0, _routeMatcher).getRouteMatcher((0, _routeRegex).getRouteRegex(resolvedHref))(as); Object.assign(parsedRewriteTarget.query, matches || {}); } return { type: 'rewrite', parsedAs: parsedRewriteTarget, resolvedHref }; }); } const src = (0, _parsePath).parsePath(source); const pathname = (0, _formatNextPathnameInfo).formatNextPathnameInfo(_extends({}, (0, _getNextPathnameInfo).getNextPathnameInfo(src.pathname, { nextConfig, parseData: true }), { defaultLocale: options.router.defaultLocale, buildId: '' })); return Promise.resolve({ type: 'redirect-external', destination: `${pathname}${src.query}${src.hash}` }); } const redirectTarget = response.headers.get('x-nextjs-redirect'); if (redirectTarget) { if (redirectTarget.startsWith('/')) { const src = (0, _parsePath).parsePath(redirectTarget); const pathname = (0, _formatNextPathnameInfo).formatNextPathnameInfo(_extends({}, (0, _getNextPathnameInfo).getNextPathnameInfo(src.pathname, { nextConfig, parseData: true }), { defaultLocale: options.router.defaultLocale, buildId: '' })); return Promise.resolve({ type: 'redirect-internal', newAs: `${pathname}${src.query}${src.hash}`, newUrl: `${pathname}${src.query}${src.hash}` }); } return Promise.resolve({ type: 'redirect-external', destination: redirectTarget }); } return Promise.resolve({ type: 'next' }); } function withMiddlewareEffects(options) { return matchesMiddleware(options).then((matches)=>{ if (matches && options.fetchData) { return options.fetchData().then((data)=>getMiddlewareData(data.dataHref, data.response, options).then((effect)=>({ dataHref: data.dataHref, cacheKey: data.cacheKey, json: data.json, response: data.response, text: data.text, effect }))).catch((_err)=>{ /** * TODO: Revisit this in the future. * For now we will not consider middleware data errors to be fatal. * maybe we should revisit in the future. */ return null; }); } return null; }); } const manualScrollRestoration = process.env.__NEXT_SCROLL_RESTORATION && typeof window !== 'undefined' && 'scrollRestoration' in window.history && !!function() { try { let v = '__next'; // eslint-disable-next-line no-sequences return sessionStorage.setItem(v, v), sessionStorage.removeItem(v), true; } catch (n) {} }(); const SSG_DATA_NOT_FOUND = Symbol('SSG_DATA_NOT_FOUND'); function fetchRetry(url, attempts, options) { return fetch(url, { // Cookies are required to be present for Next.js' SSG "Preview Mode". // Cookies may also be required for `getServerSideProps`. // // > `fetch` won’t send cookies, unless you set the credentials init // > option. // https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch // // > For maximum browser compatibility when it comes to sending & // > receiving cookies, always supply the `credentials: 'same-origin'` // > option instead of relying on the default. // https://github.com/github/fetch#caveats credentials: 'same-origin', method: options.method || 'GET', headers: Object.assign({}, options.headers, { 'x-nextjs-data': '1' }) }).then((response)=>{ return !response.ok && attempts > 1 && response.status >= 500 ? fetchRetry(url, attempts - 1, options) : response; }); } const backgroundCache = {}; function handleSmoothScroll(fn) { const htmlElement = document.documentElement; const existing = htmlElement.style.scrollBehavior; htmlElement.style.scrollBehavior = 'auto'; fn(); htmlElement.style.scrollBehavior = existing; } function tryToParseAsJSON(text) { try { return JSON.parse(text); } catch (error) { return null; } } function fetchNextData({ dataHref , inflightCache , isPrefetch , hasMiddleware , isServerRender , parseJSON , persistCache , isBackground , unstable_skipClientCache }) { const { href: cacheKey } = new URL(dataHref, window.location.href); var ref1; const getData = (params)=>{ return fetchRetry(dataHref, isServerRender ? 3 : 1, { headers: isPrefetch ? { purpose: 'prefetch' } : {}, method: (ref1 = params == null ? void 0 : params.method) != null ? ref1 : 'GET' }).then((response)=>{ if (response.ok && (params == null ? void 0 : params.method) === 'HEAD') { return { dataHref, response, text: '', json: {}, cacheKey }; } return response.text().then((text)=>{ if (!response.ok) { /** * When the data response is a redirect because of a middleware * we do not consider it an error. The headers must bring the * mapped location. * TODO: Change the status code in the handler. */ if (hasMiddleware && [ 301, 302, 307, 308 ].includes(response.status)) { return { dataHref, response, text, json: {}, cacheKey }; } if (!hasMiddleware && response.status === 404) { var ref; if ((ref = tryToParseAsJSON(text)) == null ? void 0 : ref.notFound) { return { dataHref, json: { notFound: SSG_DATA_NOT_FOUND }, response, text, cacheKey }; } } const error = new Error(`Failed to load static props`); /** * We should only trigger a server-side transition if this was * caused on a client-side transition. Otherwise, we'd get into * an infinite loop. */ if (!isServerRender) { (0, _routeLoader).markAssetError(error); } throw error; } return { dataHref, json: parseJSON ? tryToParseAsJSON(text) : null, response, text, cacheKey }; }); }).then((data)=>{ if (!persistCache || process.env.NODE_ENV !== 'production' || data.response.headers.get('x-middleware-cache') === 'no-cache') { delete inflightCache[cacheKey]; } return data; }).catch((err)=>{ delete inflightCache[cacheKey]; throw err; }); }; // when skipping client cache we wait to update // inflight cache until successful data response // this allows racing click event with fetching newer data // without blocking navigation when stale data is available if (unstable_skipClientCache && persistCache) { return getData({}).then((data)=>{ inflightCache[cacheKey] = Promise.resolve(data); return data; }); } if (inflightCache[cacheKey] !== undefined) { return inflightCache[cacheKey]; } return inflightCache[cacheKey] = getData(isBackground ? { method: 'HEAD' } : {}); } function createKey() { return Math.random().toString(36).slice(2, 10); } function handleHardNavigation({ url , router }) { // ensure we don't trigger a hard navigation to the same // URL as this can end up with an infinite refresh if (url === (0, _addBasePath).addBasePath((0, _addLocale).addLocale(router.asPath, router.locale))) { throw new Error(`Invariant: attempted to hard navigate to the same URL ${url} ${location.href}`); } window.location.href = url; } const getCancelledHandler = ({ route , router })=>{ let cancelled = false; const cancel = router.clc = ()=>{ cancelled = true; }; const handleCancelled = ()=>{ if (cancelled) { const error = new Error(`Abort fetching component for route: "${route}"`); error.cancelled = true; throw error; } if (cancel === router.clc) { router.clc = null; } }; return handleCancelled; }; class Router { reload() { window.location.reload(); } /** * Go back in history */ back() { window.history.back(); } /** * Performs a `pushState` with arguments * @param url of the route * @param as masks `url` for the browser * @param options object you can define `shallow` and other options */ push(url, as, options = {}) { if (process.env.__NEXT_SCROLL_RESTORATION) { // TODO: remove in the future when we update history before route change // is complete, as the popstate event should handle this capture. if (manualScrollRestoration) { try { // Snapshot scroll position right before navigating to a new page: sessionStorage.setItem('__next_scroll_' + this._key, JSON.stringify({ x: self.pageXOffset, y: self.pageYOffset })); } catch (e) {} } } ({ url , as } = prepareUrlAs(this, url, as)); return this.change('pushState', url, as, options); } /** * Performs a `replaceState` with arguments * @param url of the route * @param as masks `url` for the browser * @param options object you can define `shallow` and other options */ replace(url, as, options = {}) { ({ url , as } = prepareUrlAs(this, url, as)); return this.change('replaceState', url, as, options); } change(method, url, as, options, forcedScroll) { var _this = this; return _async_to_generator(function*() { if (!isLocalURL(url)) { handleHardNavigation({ url, router: _this }); return false; } // WARNING: `_h` is an internal option for handing Next.js client-side // hydration. Your app should _never_ use this property. It may change at // any time without notice. const isQueryUpdating = options._h; const shouldResolveHref = isQueryUpdating || options._shouldResolveHref || (0, _parsePath).parsePath(url).pathname === (0, _parsePath).parsePath(as).pathname; const nextState = _extends({}, _this.state); // for static pages with query params in the URL we delay // marking the router ready until after the query is updated // or a navigation has occurred const readyStateChange = _this.isReady !== true; _this.isReady = true; const isSsr = _this.isSsr; if (!isQueryUpdating) { _this.isSsr = false; } // if a route transition is already in progress before // the query updating is triggered ignore query updating if (isQueryUpdating && _this.clc) { return false; } const prevLocale = nextState.locale; if (process.env.__NEXT_I18N_SUPPORT) { nextState.locale = options.locale === false ? _this.defaultLocale : options.locale || nextState.locale; if (typeof options.locale === 'undefined') { options.locale = nextState.locale; } const parsedAs = (0, _parseRelativeUrl).parseRelativeUrl((0, _hasBasePath).hasBasePath(as) ? (0, _removeBasePath).removeBasePath(as) : as); const localePathResult = (0, _normalizeLocalePath).normalizeLocalePath(parsedAs.pathname, _this.locales); if (localePathResult.detectedLocale) { nextState.locale = localePathResult.detectedLocale; parsedAs.pathname = (0, _addBasePath).addBasePath(parsedAs.pathname); as = (0, _formatUrl).formatWithValidation(parsedAs); url = (0, _addBasePath).addBasePath((0, _normalizeLocalePath).normalizeLocalePath((0, _hasBasePath).hasBasePath(url) ? (0, _removeBasePath).removeBasePath(url) : url, _this.locales).pathname); } let didNavigate = false; // we need to wrap this in the env check again since regenerator runtime // moves this on its own due to the return if (process.env.__NEXT_I18N_SUPPORT) { var ref; // if the locale isn't configured hard navigate to show 404 page if (!((ref = _this.locales) == null ? void 0 : ref.includes(nextState.locale))) { parsedAs.pathname = (0, _addLocale).addLocale(parsedAs.pathname, nextState.locale); handleHardNavigation({ url: (0, _formatUrl).formatWithValidation(parsedAs), router: _this }); // this was previously a return but was removed in favor // of better dead code elimination with regenerator runtime didNavigate = true; } } const detectedDomain = (0, _detectDomainLocale).detectDomainLocale(_this.domainLocales, undefined, nextState.locale); // we need to wrap this in the env check again since regenerator runtime // moves this on its own due to the return if (process.env.__NEXT_I18N_SUPPORT) { // if we are navigating to a domain locale ensure we redirect to the // correct domain if (!didNavigate && detectedDomain && _this.isLocaleDomain && self.location.hostname !== detectedDomain.domain) { const asNoBasePath = (0, _removeBasePath).removeBasePath(as); handleHardNavigation({ url: `http${detectedDomain.http ? '' : 's'}://${detectedDomain.domain}${(0, _addBasePath).addBasePath(`${nextState.locale === detectedDomain.defaultLocale ? '' : `/${nextState.locale}`}${asNoBasePath === '/' ? '' : asNoBasePath}` || '/')}`, router: _this }); // this was previously a return but was removed in favor // of better dead code elimination with regenerator runtime didNavigate = true; } } if (didNavigate) { return new Promise(()=>{}); } } // marking route changes as a navigation start entry if (_utils.ST) { performance.mark('routeChange'); } const { shallow =false , scroll =true } = options; const routeProps = { shallow }; if (_this._inFlightRoute && _this.clc) { if (!isSsr) { Router.events.emit('routeChangeError', buildCancellationError(), _this._inFlightRoute, routeProps); } _this.clc(); _this.clc = null; } as = (0, _addBasePath).addBasePath((0, _addLocale).addLocale((0, _hasBasePath).hasBasePath(as) ? (0, _removeBasePath).removeBasePath(as) : as, options.locale, _this.defaultLocale)); const cleanedAs = (0, _removeLocale).removeLocale((0, _hasBasePath).hasBasePath(as) ? (0, _removeBasePath).removeBasePath(as) : as, nextState.locale); _this._inFlightRoute = as; const localeChange = prevLocale !== nextState.locale; // If the url change is only related to a hash change // We should not proceed. We should only change the state. if (!isQueryUpdating && _this.onlyAHashChange(cleanedAs) && !localeChange) { nextState.asPath = cleanedAs; Router.events.emit('hashChangeStart', as, routeProps); // TODO: do we need the resolved href when only a hash change? _this.changeState(method, url, as, _extends({}, options, { scroll: false })); if (scroll) { _this.scrollToHash(cleanedAs); } try { yield _this.set(nextState, _this.components[nextState.route], null); } catch (err) { if ((0, _isError).default(err) && err.cancelled) { Router.events.emit('routeChangeError', err, cleanedAs, routeProps); } throw err; } Router.events.emit('hashChangeComplete', as, routeProps); return true; } let parsed = (0, _parseRelativeUrl).parseRelativeUrl(url); let { pathname , query } = parsed; // The build manifest needs to be loaded before auto-static dynamic pages // get their query parameters to allow ensuring they can be parsed properly // when rewritten to let pages, rewrites; try { [pages, { __rewrites: rewrites }] = yield Promise.all([ _this.pageLoader.getPageList(), (0, _routeLoader).getClientBuildManifest(), _this.pageLoader.getMiddleware(), ]); } catch (err) { // If we fail to resolve the page list or client-build manifest, we must // do a server-side transition: handleHardNavigation({ url: as, router: _this }); return false; } // If asked to change the current URL we should reload the current page // (not location.reload() but reload getInitialProps and other Next.js stuffs) // We also need to set the method = replaceState always // as this should not go into the history (That's how browsers work) // We should compare the new asPath to the current asPath, not the url if (!_this.urlIsNew(cleanedAs) && !localeChange) { method = 'replaceState'; } // we need to resolve the as value using rewrites for dynamic SSG // pages to allow building the data URL correctly let resolvedAs = as; // url and as should always be prefixed with basePath by this // point by either next/link or router.push/replace so strip the // basePath from the pathname to match the pages dir 1-to-1 pathname = pathname ? (0, _removeTrailingSlash).removeTrailingSlash((0, _removeBasePath).removeBasePath(pathname)) : pathname; // we don't attempt resolve asPath when we need to execute // middleware as the resolving will occur server-side const isMiddlewareMatch = yield matchesMiddleware({ asPath: as, locale: nextState.locale, router: _this }); if (options.shallow && isMiddlewareMatch) { pathname = _this.pathname; } if (shouldResolveHref && pathname !== '/_error') { options._shouldResolveHref = true; if (process.env.__NEXT_HAS_REWRITES && as.startsWith('/')) { const rewritesResult = (0, _resolveRewrites).default((0, _addBasePath).addBasePath((0, _addLocale).addLocale(cleanedAs, nextState.locale), true), pages, rewrites, query, (p)=>resolveDynamicRoute(p, pages), _this.locales); if (rewritesResult.externalDest) { handleHardNavigation({ url: as, router: _this }); return true; } if (!isMiddlewareMatch) { resolvedAs = rewritesResult.asPath; } if (rewritesResult.matchedPage && rewritesResult.resolvedHref) { // if this directly matches a page we need to update the href to // allow the correct page chunk to be loaded pathname = rewritesResult.resolvedHref; parsed.pathname = (0, _addBasePath).addBasePath(pathname); if (!isMiddlewareMatch) { url = (0, _formatUrl).formatWithValidation(parsed); } } } else { parsed.pathname = resolveDynamicRoute(pathname, pages); if (parsed.pathname !== pathname) { pathname = parsed.pathname; parsed.pathname = (0, _addBasePath).addBasePath(pathname); if (!isMiddlewareMatch) { url = (0, _formatUrl).formatWithValidation(parsed); } } } } if (!isLocalURL(as)) { if (process.env.NODE_ENV !== 'production') { throw new Error(`Invalid href: "${url}" and as: "${as}", received relative href and external as` + `\nSee more info: https://nextjs.org/docs/messages/invalid-relative-url-external-as`); } handleHardNavigation({ url: as, router: _this }); return false; } resolvedAs = (0, _removeLocale).removeLocale((0, _removeBasePath).removeBasePath(resolvedAs), nextState.locale); let route = (0, _removeTrailingSlash).removeTrailingSlash(pathname); let routeMatch = false; if ((0, _isDynamic).isDynamicRoute(route)) { const parsedAs = (0, _parseRelativeUrl).parseRelativeUrl(resolvedAs); const asPathname = parsedAs.pathname; const routeRegex = (0, _routeRegex).getRouteRegex(route); routeMatch = (0, _routeMatcher).getRouteMatcher(routeRegex)(asPathname); const shouldInterpolate = route === asPathname; const interpolatedAs = shouldInterpolate ? interpolateAs(route, asPathname, query) : {}; if (!routeMatch || shouldInterpolate && !interpolatedAs.result) { const missingParams = Object.keys(routeRegex.groups).filter((param)=>!query[param]); if (missingParams.length > 0 && !isMiddlewareMatch) { if (process.env.NODE_ENV !== 'production') { console.warn(`${shouldInterpolate ? `Interpolating href` : `Mismatching \`as\` and \`href\``} failed to manually provide ` + `the params: ${missingParams.join(', ')} in the \`href\`'s \`query\``); } throw new Error((shouldInterpolate ? `The provided \`href\` (${url}) value is missing query values (${missingParams.join(', ')}) to be interpolated properly. ` : `The provided \`as\` value (${asPathname}) is incompatible with the \`href\` value (${route}). `) + `Read more: https://nextjs.org/docs/messages/${shouldInterpolate ? 'href-interpolation-failed' : 'incompatible-href-as'}`); } } else if (shouldInterpolate) { as = (0, _formatUrl).formatWithValidation(Object.assign({}, parsedAs, { pathname: interpolatedAs.result, query: omit(query, interpolatedAs.params) })); } else { // Merge params into `query`, overwriting any specified in search Object.assign(query, routeMatch); } } if (!isQueryUpdating) { Router.events.emit('routeChangeStart', as, routeProps); } try { var ref2, ref3; let routeInfo = yield _this.getRouteInfo({ route, pathname, query, as, resolvedAs, routeProps, locale: nextState.locale, isPreview: nextState.isPreview, hasMiddleware: isMiddlewareMatch }); if ('route' in routeInfo && isMiddlewareMatch) { pathname = routeInfo.route || route; route = pathname; if (!routeProps.shallow) { query = Object.assign({}, routeInfo.query || {}, query); } const cleanedParsedPathname = (0, _hasBasePath).hasBasePath(parsed.pathname) ? (0, _removeBasePath).removeBasePath(parsed.pathname) : parsed.pathname; if (routeMatch && pathname !== cleanedParsedPathname) { Object.keys(routeMatch).forEach((key)=>{ if (routeMatch && query[key] === routeMatch[key]) { delete query[key]; } }); } if ((0, _isDynamic).isDynamicRoute(pathname)) { const prefixedAs = !routeProps.shallow && routeInfo.resolvedAs ? routeInfo.resolvedAs : (0, _addBasePath).addBasePath((0, _addLocale).addLocale(new URL(as, location.href).pathname, nextState.locale), true); let rewriteAs = prefixedAs; if ((0, _hasBasePath).hasBasePath(rewriteAs)) { rewriteAs = (0, _removeBasePath).removeBasePath(rewriteAs); } if (process.env.__NEXT_I18N_SUPPORT) { const localeResult = (0, _normalizeLocalePath).normalizeLocalePath(rewriteAs, _this.locales); nextState.locale = localeResult.detectedLocale || nextState.locale; rewriteAs = localeResult.pathname; } const routeRegex = (0, _routeRegex).getRouteRegex(pathname); const curRouteMatch = (0, _routeMatcher).getRouteMatcher(routeRegex)(rewriteAs); if (curRouteMatch) { Object.assign(query, curRouteMatch); } } } // If the routeInfo brings a redirect we simply apply it. if ('type' in routeInfo) { if (routeInfo.type === 'redirect-internal') { return _this.change(method, routeInfo.newUrl, routeInfo.newAs, options); } else { handleHardNavigation({ url: routeInfo.destination, router: _this }); return new Promise(()=>{}); } } let { error , props , __N_SSG , __N_SSP } = routeInfo; const component = routeInfo.Component; if (component && component.unstable_scriptLoader) { const scripts = [].concat(component.unstable_scriptLoader()); scripts.forEach((script)=>{ (0, _script).handleClientScriptLoad(script.props); }); } // handle redirect on client-transition if ((__N_SSG || __N_SSP) && props) { if (props.pageProps && props.pageProps.__N_REDIRECT) { // Use the destination from redirect without adding locale options.locale = false; const destination = props.pageProps.__N_REDIRECT; // check if destination is internal (resolves to a page) and attempt // client-navigation if it is falling back to hard navigation if // it's not if (destination.startsWith('/') && props.pageProps.__N_REDIRECT_BASE_PATH !== false) { const parsedHref = (0, _parseRelativeUrl).parseRelativeUrl(destination); parsedHref.pathname = resolveDynamicRoute(parsedHref.pathname, pages); const { url: newUrl , as: newAs } = prepareUrlAs(_this, destination, destination); return _this.change(method, newUrl, newAs, options); } handleHardNavigation({ url: destination, router: _this }); return new Promise(()=>{}); } nextState.isPreview = !!props.__N_PREVIEW; // handle SSG data 404 if (props.notFound === SSG_DATA_NOT_FOUND) { let notFoundRoute; try { yield _this.fetchComponent('/404'); notFoundRoute = '/404'; } catch (_) { notFoundRoute = '/_error'; } routeInfo = yield _this.getRouteInfo({ route: notFoundRoute, pathname: notFoundRoute, query, as, resolvedAs, routeProps: { shallow: false }, locale: nextState.locale, isPreview: nextState.isPreview }); if ('type' in routeInfo) { throw new Error(`Unexpected middleware effect on /404`); } } } Router.events.emit('beforeHistoryChange', as, routeProps); _this.changeState(method, url, as, options); if (isQueryUpdating && pathname === '/_error' && ((ref2 = self.__NEXT_DATA__.props) == null ? void 0 : (ref3 = ref2.pageProps) == null ? void 0 : ref3.statusCode) === 500 && (props == null ? void 0 : props.pageProps)) { // ensure statusCode is still correct for static 500 page // when updating query information props.pageProps.statusCode = 500; } var _route; // shallow routing is only allowed for same page URL changes. const isValidShallowRoute = options.shallow && nextState.route === ((_route = routeInfo.route) != null ? _route : route); var _scroll; const shouldScroll = (_scroll = options.scroll) != null ? _scroll : !options._h && !isValidShallowRoute; const resetScroll = shouldScroll ? { x: 0, y: 0 } : null; // the new state that the router gonna set const upcomingRouterState = _extends({}, nextState, { route, pathname, query, asPath: cleanedAs, isFallback: false }); const upcomingScrollState = forcedScroll != null ? forcedScroll : resetScroll; // for query updates we can skip it if the state is unchanged and we don't // need to scroll // https://github.com/vercel/next.js/issues/37139 const canSkipUpdating = options._h && !upcomingScrollState && !readyStateChange && !localeChange && (0, _compareStates).compareRouterStates(upcomingRouterState, _this.state); if (!canSkipUpdating) { yield _this.set(upcomingRouterState, routeInfo, upcomingScrollState).catch((e)=>{ if (e.cancelled) error = error || e; else throw e; }); if (error) { if (!isQueryUpdating) { Router.events.emit('routeChangeError', error, cleanedAs, routeProps); } throw error; } if (process.env.__NEXT_I18N_SUPPORT) { if (nextState.locale) { document.documentElement.lang = nextState.locale; } } if (!isQueryUpdating) { Router.events.emit('routeChangeComplete', as, routeProps); } // A hash mark # is the optional last part of a URL const hashRegex = /#.+$/; if (shouldScroll && hashRegex.test(as)) { _this.scrollToHash(as); } } return true; } catch (err1) { if ((0, _isError).default(err1) && err1.cancelled) { return false; } throw err1; } })(); } changeState(method, url, as, options = {}) { if (process.env.NODE_ENV !== 'production') { if (typeof window.history === 'undefined') { console.error(`Warning: window.history is not available.`); return; } if (typeof window.history[method] === 'undefined') { console.error(`Warning: window.history.${method} is not available`); return; } } if (method !== 'pushState' || (0, _utils).getURL() !== as) { this._shallow = options.shallow; window.history[method]({ url, as, options, __N: true, key: this._key = method !== 'pushState' ? this._key : createKey() }, // Most browsers currently ignores this parameter, although they may use it in the future. // Passing the empty string here should be safe against future changes to the method. // https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState '', as); } } handleRouteInfoError(err, pathname, query, as, routeProps, loadErrorFail) { var _this = this; return _async_to_generator(function*() { console.error(err); if (err.cancelled) { // bubble up cancellation errors throw err; } if ((0, _routeLoader).isAssetError(err) || loadErrorFail) { Router.events.emit('routeChangeError', err, as, routeProps); // If we can't load the page it could be one of following reasons // 1. Page doesn't exists // 2. Page does exist in a different zone // 3. Internal error while loading the page // So, doing a hard reload is the proper way to deal with this. handleHardNavigation({ url: as, router: _this }); // Changing the URL doesn't block executing the current code path. // So let's throw a cancellation error stop the routing logic. throw buildCancellationError(); } try { let props; const { page: Component , styleSheets } = yield _this.fetchComponent('/_error'); const routeInfo = { props, Component, styleSheets, err, error: err }; if (!routeInfo.props) { try { routeInfo.props = yield _this.getInitialProps(Component, { err, pathname, query }); } catch (gipErr) { console.error('Error in error page `getInitialProps`: ', gipErr); routeInfo.props = {}; } } return routeInfo; } catch (routeInfoErr) { return _this.handleRouteInfoError((0, _isError).default(routeInfoErr) ? routeInfoErr : new Error(routeInfoErr + ''), pathname, query, as, routeProps, true); } })(); } getRouteInfo({ route: requestedRoute , pathname , query , as , resolvedAs , routeProps , locale , hasMiddleware , isPreview , unstable_skipClientCache }) { var _this = this; return _async_to_generator(function*() { /** * This `route` binding can change if there's a rewrite * so we keep a reference to the original requested route * so we can store the cache for it and avoid re-requesting every time * for shallow routing purposes. */ let route = requestedRoute; try { var ref, ref4, ref5; const handleCancelled = getCancelledHandler({ route, router: _this }); let existingInfo = _this.components[route]; if (routeProps.shallow && existingInfo && _this.route === route) { return existingInfo; } if (hasMiddleware) { existingInfo = undefined; } let cachedRouteInfo = existingInfo && !('initial' in existingInfo) && process.env.NODE_ENV !== 'development' ? existingInfo : undefined; const fetchNextDataParams = { dataHref: _this.pageLoader.getDataHref({ href: (0, _formatUrl).formatWithValidation({ pathname, query }), skipInterpolation: true, asPath: resolvedAs, locale }), hasMiddleware: true, isServerRender: _this.isSsr, parseJSON: true, inflightCache: _this.sdc, persistCache: !isPreview, isPrefetch: false, unstable_skipClientCache }; const data = yield withMiddlewareEffects({ fetchData: ()=>fetchNextData(fetchNextDataParams), asPath: resolvedAs, locale: locale, router: _this }); handleCancelled(); if ((data == null ? void 0 : (ref = data.effect) == null ? void 0 : ref.type) === 'redirect-internal' || (data == null ? void 0 : (ref4 = data.effect) == null ? void 0 : ref4.type) === 'redirect-external') { return data.effect; } if ((data == null ? void 0 : (ref5 = data.effect) == null ? void 0 : ref5.type) === 'rewrite') { route = (0, _removeTrailingSlash).removeTrailingSlash(data.effect.resolvedHref); pathname = data.effect.resolvedHref; query = _extends({}, query, data.effect.parsedAs.query); resolvedAs = (0, _removeBasePath).removeBasePath((0, _normalizeLocalePath).normalizeLocalePath(data.effect.parsedAs.pathname, _this.locales).pathname); // Check again the cache with the new destination. existingInfo = _this.components[route]; if (routeProps.shallow && existingInfo && _this.route === route && !hasMiddleware) { // If we have a match with the current route due to rewrite, // we can copy the existing information to the rewritten one. // Then, we return the information along with the matched route. return _extends({}, existingInfo, { route }); } } if (route === '/api' || route.startsWith('/api/')) { handleHardNavigation({ url: as, router: _this }); return new Promise(()=>{}); } const routeInfo = cachedRouteInfo || (yield _this.fetchComponent(route).then((res)=>({ Component: res.page, styleSheets: res.styleSheets, __N_SSG: res.mod.__N_SSG, __N_SSP: res.mod.__N_SSP }))); if (process.env.NODE_ENV !== 'production') { const { isValidElementType } = require('next/dist/compiled/react-is'); if (!isValidElementType(routeInfo.Component)) { throw new Error(`The default export is not a React Component in page: "${pathname}"`); } } const shouldFetchData = routeInfo.__N_SSG || routeInfo.__N_SSP; const { props , cacheKey } = yield _this._getData(_async_to_generator(function*() { if (shouldFetchData) { const { json , cacheKey: _cacheKey } = (data == null ? void 0 : data.json) ? data : yield fetchNextData({ dataHref: _this.pageLoader.getDataHref({ href: (0, _formatUrl).formatWithValidation({ pathname, query }), asPath: resolvedAs, locale }), isServerRender: _this.isSsr, parseJSON: true, inflightCache: _this.sdc, persistCache: !isPreview, isPrefetch: false, unstable_skipClientCache }); return { cacheKey: _cacheKey, props: json || {} }; } return { headers: {}, cacheKey: '', props: yield _this.getInitialProps(routeInfo.Component, // we provide AppTree later so this needs to be `any` { pathname, query, asPath: as, locale, locales: _this.locales, defaultLocale: _this.defaultLocale }) }; })); // Only bust the data cache for SSP routes although // middleware can skip cache per request with // x-middleware-cache: no-cache as well if (routeInfo.__N_SSP && fetchNextDataParams.dataHref) { delete _this.sdc[cacheKey]; } // we kick off a HEAD request in the background // when a non-prefetch request is made to signal revalidation if (!_this.isPreview && routeInfo.__N_SSG && process.env.NODE_ENV !== 'development') { fetchNextData(Object.assign({}, fetchNextDataParams, { isBackground: true, persistCache: false, inflightCache: backgroundCache })).catch(()=>{}); } props.pageProps = Object.assign({}, props.pageProps); routeInfo.props = props; routeInfo.route = route; routeInfo.query = query; routeInfo.resolvedAs = resolvedAs; _this.components[route] = routeInfo; return routeInfo; } catch (err) { return _this.handleRouteInfoError((0, _isError).getProperError(err), pathname, query, as, routeProps); } })(); } set(state, data, resetScroll) { this.state = state; return this.sub(data, this.components['/_app'].Component, resetScroll); } /** * Callback to execute before replacing router state * @param cb callback to be executed */ beforePopState(cb) { this._bps = cb; } onlyAHashChange(as) { if (!this.asPath) return false; const [oldUrlNoHash, oldHash] = this.asPath.split('#'); const [newUrlNoHash, newHash] = as.split('#'); // Makes sure we scroll to the provided hash if the url/hash are the same if (newHash && oldUrlNoHash === newUrlNoHash && oldHash === newHash) { return true; } // If the urls are change, there's more than a hash change if (oldUrlNoHash !== newUrlNoHash) { return false; } // If the hash has changed, then it's a hash only change. // This check is necessary to handle both the enter and // leave hash === '' cases. The identity case falls through // and is treated as a next reload. return oldHash !== newHash; } scrollToHash(as) { const [, hash = ''] = as.split('#'); // Scroll to top if the hash is just `#` with no value or `#top` // To mirror browsers if (hash === '' || hash === 'top') { handleSmoothScroll(()=>window.scrollTo(0, 0)); return; } // Decode hash to make non-latin anchor works. const rawHash = decodeURIComponent(hash); // First we check if the element by id is found const idEl = document.getElementById(rawHash); if (idEl) { handleSmoothScroll(()=>idEl.scrollIntoView()); return; } // If there's no element with the id, we check the `name` property // To mirror browsers const nameEl = document.getElementsByName(rawHash)[0]; if (nameEl) { handleSmoothScroll(()=>nameEl.scrollIntoView()); } } urlIsNew(asPath) { return this.asPath !== asPath; } /** * Prefetch page code, you may wait for the data during page rendering. * This feature only works in production! * @param url the href of prefetched page * @param asPath the as path of the prefetched page */ prefetch(url, asPath = url, options = {}) { var _this = this; return _async_to_generator(function*() { if (typeof window !== 'undefined' && (0, _isBot).isBot(window.navigator.userAgent)) { // No prefetches for bots that render the link since they are typically navigating // links via the equivalent of a hard navigation and hence never utilize these // prefetches. return; } let parsed = (0, _parseRelativeUrl).parseRelativeUrl(url); let { pathname , query } = parsed; if (process.env.__NEXT_I18N_SUPPORT) { if (options.locale === false) { pathname = (0, _normalizeLocalePath).normalizeLocalePath(pathname, _this.locales).pathname; parsed.pathname = pathname; url = (0, _formatUrl).formatWithValidation(parsed); let parsedAs = (0, _parseRelativeUrl).parseRelativeUrl(asPath); const localePathResult = (0, _normalizeLocalePath).normalizeLocalePath(parsedAs.pathname, _this.locales); parsedAs.pathname = localePathResult.pathname; options.locale = localePathResult.detectedLocale || _this.defaultLocale; asPath = (0, _formatUrl).formatWithValidation(parsedAs); } } const pages = yield _this.pageLoader.getPageList(); let resolvedAs = asPath; const locale = typeof options.locale !== 'undefined' ? options.locale || undefined : _this.locale; if (process.env.__NEXT_HAS_REWRITES && asPath.startsWith('/')) { let rewrites; ({ __rewrites: rewrites } = yield (0, _routeLoader).getClientBuildManifest()); const rewritesResult = (0, _resolveRewrites).default((0, _addBasePath).addBasePath((0, _addLocale).addLocale(asPath, _this.locale), true), pages, rewrites, parsed.query, (p)=>resolveDynamicRoute(p, pages), _this.locales); if (rewritesResult.externalDest) { return; } resolvedAs = (0, _removeLocale).removeLocale((0, _removeBasePath).removeBasePath(rewritesResult.asPath), _this.locale); if (rewritesResult.matchedPage && rewritesResult.resolvedHref) { // if this directly matches a page we need to update the href to // allow the correct page chunk to be loaded pathname = rewritesResult.resolvedHref; parsed.pathname = pathname; url = (0, _formatUrl).formatWithValidation(parsed); } } parsed.pathname = resolveDynamicRoute(parsed.pathname, pages); if ((0, _isDynamic).isDynamicRoute(parsed.pathname)) { pathname = parsed.pathname; parsed.pathname = pathname; Object.assign(query, (0, _routeMatcher).getRouteMatcher((0, _routeRegex).getRouteRegex(parsed.pathname))((0, _parsePath).parsePath(asPath).pathname) || {}); url = (0, _formatUrl).formatWithValidation(parsed); } // Prefetch is not supported in development mode because it would trigger on-demand-entries if (process.env.NODE_ENV !== 'production') { return; } const route = (0, _removeTrailingSlash).removeTrailingSlash(pathname); yield Promise.all([ _this.pageLoader._isSsg(route).then((isSsg)=>{ return isSsg ? fetchNextData({ dataHref: _this.pageLoader.getDataHref({ href: url, asPath: resolvedAs, locale: locale }), isServerRender: false, parseJSON: true, inflightCache: _this.sdc, persistCache: !_this.isPreview, isPrefetch: true, unstable_skipClientCache: options.unstable_skipClientCache || options.priority && !!process.env.__NEXT_OPTIMISTIC_CLIENT_CACHE }).then(()=>false) : false; }), _this.pageLoader[options.priority ? 'loadPage' : 'prefetch'](route), ]); })(); } fetchComponent(route) { var _this = this; return _async_to_generator(function*() { const handleCancelled = getCancelledHandler({ route, router: _this }); try { const componentResult = yield _this.pageLoader.loadPage(route); handleCancelled(); return componentResult; } catch (err) { handleCancelled(); throw err; } })(); } _getData(fn) { let cancelled = false; const cancel = ()=>{ cancelled = true; }; this.clc = cancel; return fn().then((data)=>{ if (cancel === this.clc) { this.clc = null; } if (cancelled) { const err = new Error('Loading initial props cancelled'); err.cancelled = true; throw err; } return data; }); } _getFlightData(dataHref) { // Do not cache RSC flight response since it's not a static resource return fetchNextData({ dataHref, isServerRender: true, parseJSON: false, inflightCache: this.sdc, persistCache: false, isPrefetch: false }).then(({ text })=>({ data: text })); } getInitialProps(Component, ctx) { const { Component: App } = this.components['/_app']; const AppTree = this._wrapApp(App); ctx.AppTree = AppTree; return (0, _utils).loadGetInitialProps(App, { AppTree, Component, router: this, ctx }); } get route() { return this.state.route; } get pathname() { return this.state.pathname; } get query() { return this.state.query; } get asPath() { return this.state.asPath; } get locale() { return this.state.locale; } get isFallback() { return this.state.isFallback; } get isPreview() { return this.state.isPreview; } constructor(pathname1, query1, as1, { initialProps , pageLoader , App , wrapApp , Component , err , subscription , isFallback , locale , locales , defaultLocale , domainLocales , isPreview }){ // Server Data Cache this.sdc = {}; this.isFirstPopStateEvent = true; this._key = createKey(); this.onPopState = (e)=>{ const { isFirstPopStateEvent } = this; this.isFirstPopStateEvent = false; const state = e.state; if (!state) { // We get state as undefined for two reasons. // 1. With older safari (< 8) and older chrome (< 34) // 2. When the URL changed with # // // In the both cases, we don't need to proceed and change the route. // (as it's already changed) // But we can simply replace the state with the new changes. // Actually, for (1) we don't need to nothing. But it's hard to detect that event. // So, doing the following for (1) does no harm. const { pathname , query } = this; this.changeState('replaceState', (0, _formatUrl).formatWithValidation({ pathname: (0, _addBasePath).addBasePath(pathname), query }), (0, _utils).getURL()); return; } // __NA is used to identify if the history entry can be handled by the app-router. if (state.__NA) { window.location.reload(); return; } if (!state.__N) { return; } // Safari fires popstateevent when reopening the browser. if (isFirstPopStateEvent && this.locale === state.options.locale && state.as === this.asPath) { return; } let forcedScroll; const { url , as , options , key } = state; if (process.env.__NEXT_SCROLL_RESTORATION) { if (manualScrollRestoration) { if (this._key !== key) { // Snapshot current scroll position: try { sessionStorage.setItem('__next_scroll_' + this._key, JSON.stringify({ x: self.pageXOffset, y: self.pageYOffset })); } catch (e) {} // Restore old scroll position: try { const v = sessionStorage.getItem('__next_scroll_' + key); forcedScroll = JSON.parse(v); } catch (e1) { forcedScroll = { x: 0, y: 0 }; } } } } this._key = key; const { pathname } = (0, _parseRelativeUrl).parseRelativeUrl(url); // Make sure we don't re-render on initial load, // can be caused by navigating back from an external site if (this.isSsr && as === (0, _addBasePath).addBasePath(this.asPath) && pathname === (0, _addBasePath).addBasePath(this.pathname)) { return; } // If the downstream application returns falsy, return. // They will then be responsible for handling the event. if (this._bps && !this._bps(state)) { return; } this.change('replaceState', url, as, Object.assign({}, options, { shallow: options.shallow && this._shallow, locale: options.locale || this.defaultLocale, // @ts-ignore internal value not exposed on types _h: 0 }), forcedScroll); }; // represents the current component key const route = (0, _removeTrailingSlash).removeTrailingSlash(pathname1); // set up the component cache (by route keys) this.components = {}; // We should not keep the cache, if there's an error // Otherwise, this cause issues when when going back and // come again to the errored page. if (pathname1 !== '/_error') { this.components[route] = { Component, initial: true, props: initialProps, err, __N_SSG: initialProps && initialProps.__N_SSG, __N_SSP: initialProps && initialProps.__N_SSP }; } this.components['/_app'] = { Component: App, styleSheets: [] }; // Backwards compat for Router.router.events // TODO: Should be remove the following major version as it was never documented this.events = Router.events; this.pageLoader = pageLoader; // if auto prerendered and dynamic route wait to update asPath // until after mount to prevent hydration mismatch const autoExportDynamic = (0, _isDynamic).isDynamicRoute(pathname1) && self.__NEXT_DATA__.autoExport; this.basePath = process.env.__NEXT_ROUTER_BASEPATH || ''; this.sub = subscription; this.clc = null; this._wrapApp = wrapApp; // make sure to ignore extra popState in safari on navigating // back from external site this.isSsr = true; this.isLocaleDomain = false; this.isReady = !!(self.__NEXT_DATA__.gssp || self.__NEXT_DATA__.gip || self.__NEXT_DATA__.appGip && !self.__NEXT_DATA__.gsp || !autoExportDynamic && !self.location.search && !process.env.__NEXT_HAS_REWRITES); if (process.env.__NEXT_I18N_SUPPORT) { this.locales = locales; this.defaultLocale = defaultLocale; this.domainLocales = domainLocales; this.isLocaleDomain = !!(0, _detectDomainLocale).detectDomainLocale(domainLocales, self.location.hostname); } this.state = { route, pathname: pathname1, query: query1, asPath: autoExportDynamic ? pathname1 : as1, isPreview: !!isPreview, locale: process.env.__NEXT_I18N_SUPPORT ? locale : undefined, isFallback }; this._initialMatchesMiddlewarePromise = Promise.resolve(false); if (typeof window !== 'undefined') { // make sure "as" doesn't start with double slashes or else it can // throw an error as it's considered invalid if (!as1.startsWith('//')) { // in order for `e.state` to work on the `onpopstate` event // we have to register the initial route upon initialization const options = { locale }; const asPath = (0, _utils).getURL(); this._initialMatchesMiddlewarePromise = matchesMiddleware({ router: this, locale, asPath }).then((matches)=>{ options._shouldResolveHref = as1 !== pathname1; this.changeState('replaceState', matches ? asPath : (0, _formatUrl).formatWithValidation({ pathname: (0, _addBasePath).addBasePath(pathname1), query: query1 }), asPath, options); return matches; }); } window.addEventListener('popstate', this.onPopState); // enable custom scroll restoration handling when available // otherwise fallback to browser's default handling if (process.env.__NEXT_SCROLL_RESTORATION) { if (manualScrollRestoration) { window.history.scrollRestoration = 'manual'; } } } } } Router.events = (0, _mitt).default(); exports.default = Router; //# sourceMappingURL=router.js.map