Second frontend, written in Next.JS + Typescript.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1 lines
21 KiB

{"ast":null,"code":"\"use strict\";\n\nvar __importDefault = this && this.__importDefault || function (mod) {\n return mod && mod.__esModule ? mod : {\n \"default\": mod\n };\n};\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nconst url_1 = require(\"url\");\n\nconst mitt_1 = __importDefault(require(\"../mitt\"));\n\nconst utils_1 = require(\"../utils\");\n\nconst is_dynamic_1 = require(\"./utils/is-dynamic\");\n\nconst route_matcher_1 = require(\"./utils/route-matcher\");\n\nconst route_regex_1 = require(\"./utils/route-regex\");\n\nconst basePath = process.env.__NEXT_ROUTER_BASEPATH || '';\n\nfunction addBasePath(path) {\n return path.indexOf(basePath) !== 0 ? basePath + path : path;\n}\n\nexports.addBasePath = addBasePath;\n\nfunction delBasePath(path) {\n return path.indexOf(basePath) === 0 ? path.substr(basePath.length) || '/' : path;\n}\n\nfunction toRoute(path) {\n return path.replace(/\\/$/, '') || '/';\n}\n\nconst prepareRoute = path => toRoute(!path || path === '/' ? '/index' : path);\n\nfunction fetchNextData(pathname, query, isServerRender, cb) {\n let attempts = isServerRender ? 3 : 1;\n\n function getResponse() {\n return fetch(utils_1.formatWithValidation({\n // @ts-ignore __NEXT_DATA__\n pathname: `/_next/data/${__NEXT_DATA__.buildId}${pathname}.json`,\n query\n }), {\n // Cookies are required to be present for Next.js' SSG \"Preview Mode\".\n // Cookies may also be required for `getServerSideProps`.\n //\n // > `fetch` won’t send cookies, unless you set the credentials init\n // > option.\n // https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch\n //\n // > For maximum browser compatibility when it comes to sending &\n // > receiving cookies, always supply the `credentials: 'same-origin'`\n // > option instead of relying on the default.\n // https://github.com/github/fetch#caveats\n credentials: 'same-origin'\n }).then(res => {\n if (!res.ok) {\n if (--attempts > 0 && res.status >= 500) {\n return getResponse();\n }\n\n throw new Error(`Failed to load static props`);\n }\n\n return res.json();\n });\n }\n\n return getResponse().then(data => {\n return cb ? cb(data) : data;\n }).catch(err => {\n // We should only trigger a server-side transition if this was caused\n // on a client-side transition. Otherwise, we'd get into an infinite\n // loop.\n if (!isServerRender) {\n ;\n err.code = 'PAGE_LOAD_ERROR';\n }\n\n throw err;\n });\n}\n\nclass Router {\n constructor(pathname, query, as, {\n initialProps,\n pageLoader,\n App,\n wrapApp,\n Component,\n err,\n subscription,\n isFallback\n }) {\n // Static Data Cache\n this.sdc = {};\n\n this.onPopState = e => {\n if (!e.state) {\n // We get state as undefined for two reasons.\n // 1. With older safari (< 8) and older chrome (< 34)\n // 2. When the URL changed with #\n //\n // In the both cases, we don't need to proceed and change the route.\n // (as it's already changed)\n // But we can simply replace the state with the new changes.\n // Actually, for (1) we don't need to nothing. But it's hard to detect that event.\n // So, doing the following for (1) does no harm.\n const {\n pathname,\n query\n } = this;\n this.changeState('replaceState', utils_1.formatWithValidation({\n pathname,\n query\n }), utils_1.getURL());\n return;\n } // Make sure we don't re-render on initial load,\n // can be caused by navigating back from an external site\n\n\n if (e.state && this.isSsr && e.state.as === this.asPath && url_1.parse(e.state.url).pathname === this.pathname) {\n return;\n } // If the downstream application returns falsy, return.\n // They will then be responsible for handling the event.\n\n\n if (this._bps && !this._bps(e.state)) {\n return;\n }\n\n const {\n url,\n as,\n options\n } = e.state;\n\n if (false) {\n if (typeof url === 'undefined' || typeof as === 'undefined') {\n console.warn('`popstate` event triggered but `event.state` did not have `url` or `as` https://err.sh/zeit/next.js/popstate-state-empty');\n }\n }\n\n this.replace(url, as, options);\n };\n\n this._getStaticData = asPath => {\n const pathname = prepareRoute(url_1.parse(asPath).pathname);\n return true && this.sdc[pathname] ? Promise.resolve(this.sdc[pathname]) : fetchNextData(pathname, null, this.isSsr, data => this.sdc[pathname] = data);\n };\n\n this._getServerData = asPath => {\n let {\n pathname,\n query\n } = url_1.parse(asPath, true);\n pathname = prepareRoute(pathname);\n return fetchNextData(pathname, query, this.isSsr);\n }; // represents the current component key\n\n\n this.route = toRoute(pathname); // set up the component cache (by route keys)\n\n this.components = {}; // We should not keep the cache, if there's an error\n // Otherwise, this cause issues when when going back and\n // come again to the errored page.\n\n if (pathname !== '/_error') {\n this.components[this.route] = {\n Component,\n props: initialProps,\n err,\n __N_SSG: initialProps && initialProps.__N_SSG,\n __N_SSP: initialProps && initialProps.__N_SSP\n };\n }\n\n this.components['/_app'] = {\n Component: App\n }; // Backwards compat for Router.router.events\n // TODO: Should be remove the following major version as it was never documented\n\n this.events = Router.events;\n this.pageLoader = pageLoader;\n this.pathname = pathname;\n this.query = query; // if auto prerendered and dynamic route wait to update asPath\n // until after mount to prevent hydration mismatch\n\n this.asPath = // @ts-ignore this is temporarily global (attached to window)\n is_dynamic_1.isDynamicRoute(pathname) && __NEXT_DATA__.autoExport ? pathname : as;\n this.basePath = basePath;\n this.sub = subscription;\n this.clc = null;\n this._wrapApp = wrapApp; // make sure to ignore extra popState in safari on navigating\n // back from external site\n\n this.isSsr = true;\n this.isFallback = isFallback;\n\n if (false) {\n // in order for `e.state` to work on the `onpopstate` event\n // we have to register the initial route upon initialization\n this.changeState('replaceState', utils_1.formatWithValidation({\n pathname,\n query\n }), as);\n window.addEventListener('popstate', this.onPopState);\n }\n } // @deprecated backwards compatibility even though it's a private method.\n\n\n static _rewriteUrlForNextExport(url) {\n if (process.env.__NEXT_EXPORT_TRAILING_SLASH) {\n const rewriteUrlForNextExport = require('./rewrite-url-for-export').rewriteUrlForNextExport;\n\n return rewriteUrlForNextExport(url);\n } else {\n return url;\n }\n }\n\n update(route, mod) {\n const Component = mod.default || mod;\n const data = this.components[route];\n\n if (!data) {\n throw new Error(`Cannot update unavailable route: ${route}`);\n }\n\n const newData = Object.assign(Object.assign({}, data), {\n Component,\n __N_SSG: mod.__N_SSG,\n __N_SSP: mod.__N_SSP\n });\n this.components[route] = newData; // pages/_app.js updated\n\n if (route === '/_app') {\n this.notify(this.components[this.route]);\n return;\n }\n\n if (route === this.route) {\n this.notify(newData);\n }\n }\n\n reload() {\n window.location.reload();\n }\n /**\n * Go back in history\n */\n\n\n back() {\n window.history.back();\n }\n /**\n * Performs a `pushState` with arguments\n * @param url of the route\n * @param as masks `url` for the browser\n * @param options object you can define `shallow` and other options\n */\n\n\n push(url, as = url, options = {}) {\n return this.change('pushState', url, as, options);\n }\n /**\n * Performs a `replaceState` with arguments\n * @param url of the route\n * @param as masks `url` for the browser\n * @param options object you can define `shallow` and other options\n */\n\n\n replace(url, as = url, options = {}) {\n return this.change('replaceState', url, as, options);\n }\n\n change(method, _url, _as, options) {\n return new Promise((resolve, reject) => {\n if (!options._h) {\n this.isSsr = false;\n } // marking route changes as a navigation start entry\n\n\n if (utils_1.ST) {\n performance.mark('routeChange');\n } // If url and as provided as an object representation,\n // we'll format them into the string version here.\n\n\n let url = typeof _url === 'object' ? utils_1.formatWithValidation(_url) : _url;\n let as = typeof _as === 'object' ? utils_1.formatWithValidation(_as) : _as;\n url = addBasePath(url);\n as = addBasePath(as); // Add the ending slash to the paths. So, we can serve the\n // \"<page>/index.html\" directly for the SSR page.\n\n if (process.env.__NEXT_EXPORT_TRAILING_SLASH) {\n const rewriteUrlForNextExport = require('./rewrite-url-for-export').rewriteUrlForNextExport; // @ts-ignore this is temporarily global (attached to window)\n\n\n if (__NEXT_DATA__.nextExport) {\n as = rewriteUrlForNextExport(as);\n }\n }\n\n this.abortComponentLoad(as); // If the url change is only related to a hash change\n // We should not proceed. We should only change the state.\n // WARNING: `_h` is an internal option for handing Next.js client-side\n // hydration. Your app should _never_ use this property. It may change at\n // any time without notice.\n\n if (!options._h && this.onlyAHashChange(as)) {\n this.asPath = as;\n Router.events.emit('hashChangeStart', as);\n this.changeState(method, url, as, options);\n this.scrollToHash(as);\n Router.events.emit('hashChangeComplete', as);\n return resolve(true);\n }\n\n const {\n pathname,\n query,\n protocol\n } = url_1.parse(url, true);\n\n if (!pathname || protocol) {\n if (false) {\n throw new Error(`Invalid href passed to router: ${url} https://err.sh/zeit/next.js/invalid-href-passed`);\n }\n\n return resolve(false);\n } // If asked to change the current URL we should reload the current page\n // (not location.reload() but reload getInitialProps and other Next.js stuffs)\n // We also need to set the method = replaceState always\n // as this should not go into the history (That's how browsers work)\n // We should compare the new asPath to the current asPath, not the url\n\n\n if (!this.urlIsNew(as)) {\n method = 'replaceState';\n }\n\n const route = toRoute(pathname);\n const {\n shallow = false\n } = options;\n\n if (is_dynamic_1.isDynamicRoute(route)) {\n const {\n pathname: asPathname\n } = url_1.parse(as);\n const routeRegex = route_regex_1.getRouteRegex(route);\n const routeMatch = route_matcher_1.getRouteMatcher(routeRegex)(asPathname);\n\n if (!routeMatch) {\n const missingParams = Object.keys(routeRegex.groups).filter(param => !query[param]);\n\n if (missingParams.length > 0) {\n if (false) {\n console.warn(`Mismatching \\`as\\` and \\`href\\` failed to manually provide ` + `the params: ${missingParams.join(', ')} in the \\`href\\`'s \\`query\\``);\n }\n\n return reject(new Error(`The provided \\`as\\` value (${asPathname}) is incompatible with the \\`href\\` value (${route}). ` + `Read more: https://err.sh/zeit/next.js/incompatible-href-as`));\n }\n } else {\n // Merge params into `query`, overwriting any specified in search\n Object.assign(query, routeMatch);\n }\n }\n\n Router.events.emit('routeChangeStart', as); // If shallow is true and the route exists in the router cache we reuse the previous result\n\n this.getRouteInfo(route, pathname, query, as, shallow).then(routeInfo => {\n const {\n error\n } = routeInfo;\n\n if (error && error.cancelled) {\n return resolve(false);\n }\n\n Router.events.emit('beforeHistoryChange', as);\n this.changeState(method, url, as, options);\n\n if (false) {\n const appComp = this.components['/_app'].Component;\n window.next.isPrerendered = appComp.getInitialProps === appComp.origGetInitialProps && !routeInfo.Component.getInitialProps;\n }\n\n this.set(route, pathname, query, as, routeInfo);\n\n if (error) {\n Router.events.emit('routeChangeError', error, as);\n throw error;\n }\n\n Router.events.emit('routeChangeComplete', as);\n return resolve(true);\n }, reject);\n });\n }\n\n changeState(method, url, as, options = {}) {\n if (false) {\n if (typeof window.history === 'undefined') {\n console.error(`Warning: window.history is not available.`);\n return;\n }\n\n if (typeof window.history[method] === 'undefined') {\n console.error(`Warning: window.history.${method} is not available`);\n return;\n }\n }\n\n if (method !== 'pushState' || utils_1.getURL() !== as) {\n window.history[method]({\n url,\n as,\n options\n }, // Most browsers currently ignores this parameter, although they may use it in the future.\n // Passing the empty string here should be safe against future changes to the method.\n // https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState\n '', as);\n }\n }\n\n getRouteInfo(route, pathname, query, as, shallow = false) {\n const cachedRouteInfo = this.components[route]; // If there is a shallow route transition possible\n // If the route is already rendered on the screen.\n\n if (shallow && cachedRouteInfo && this.route === route) {\n return Promise.resolve(cachedRouteInfo);\n }\n\n const handleError = (err, loadErrorFail) => {\n return new Promise(resolve => {\n if (err.code === 'PAGE_LOAD_ERROR' || loadErrorFail) {\n // If we can't load the page it could be one of following reasons\n // 1. Page doesn't exists\n // 2. Page does exist in a different zone\n // 3. Internal error while loading the page\n // So, doing a hard reload is the proper way to deal with this.\n window.location.href = as; // Changing the URL doesn't block executing the current code path.\n // So, we need to mark it as a cancelled error and stop the routing logic.\n\n err.cancelled = true; // @ts-ignore TODO: fix the control flow here\n\n return resolve({\n error: err\n });\n }\n\n if (err.cancelled) {\n // @ts-ignore TODO: fix the control flow here\n return resolve({\n error: err\n });\n }\n\n resolve(this.fetchComponent('/_error').then(res => {\n const {\n page: Component\n } = res;\n const routeInfo = {\n Component,\n err\n };\n return new Promise(resolve => {\n this.getInitialProps(Component, {\n err,\n pathname,\n query\n }).then(props => {\n routeInfo.props = props;\n routeInfo.error = err;\n resolve(routeInfo);\n }, gipErr => {\n console.error('Error in error page `getInitialProps`: ', gipErr);\n routeInfo.error = err;\n routeInfo.props = {};\n resolve(routeInfo);\n });\n });\n }).catch(err => handleError(err, true)));\n });\n };\n\n return new Promise((resolve, reject) => {\n if (cachedRouteInfo) {\n return resolve(cachedRouteInfo);\n }\n\n this.fetchComponent(route).then(res => resolve({\n Component: res.page,\n __N_SSG: res.mod.__N_SSG,\n __N_SSP: res.mod.__N_SSP\n }), reject);\n }).then(routeInfo => {\n const {\n Component,\n __N_SSG,\n __N_SSP\n } = routeInfo;\n\n if (false) {\n const {\n isValidElementType\n } = require('react-is');\n\n if (!isValidElementType(Component)) {\n throw new Error(`The default export is not a React Component in page: \"${pathname}\"`);\n }\n }\n\n return this._getData(() => __N_SSG ? this._getStaticData(as) : __N_SSP ? this._getServerData(as) : this.getInitialProps(Component, // we provide AppTree later so this needs to be `any`\n {\n pathname,\n query,\n asPath: as\n })).then(props => {\n routeInfo.props = props;\n this.components[route] = routeInfo;\n return routeInfo;\n });\n }).catch(handleError);\n }\n\n set(route, pathname, query, as, data) {\n this.isFallback = false;\n this.route = route;\n this.pathname = pathname;\n this.query = query;\n this.asPath = as;\n this.notify(data);\n }\n /**\n * Callback to execute before replacing router state\n * @param cb callback to be executed\n */\n\n\n beforePopState(cb) {\n this._bps = cb;\n }\n\n onlyAHashChange(as) {\n if (!this.asPath) return false;\n const [oldUrlNoHash, oldHash] = this.asPath.split('#');\n const [newUrlNoHash, newHash] = as.split('#'); // Makes sure we scroll to the provided hash if the url/hash are the same\n\n if (newHash && oldUrlNoHash === newUrlNoHash && oldHash === newHash) {\n return true;\n } // If the urls are change, there's more than a hash change\n\n\n if (oldUrlNoHash !== newUrlNoHash) {\n return false;\n } // If the hash has changed, then it's a hash only change.\n // This check is necessary to handle both the enter and\n // leave hash === '' cases. The identity case falls through\n // and is treated as a next reload.\n\n\n return oldHash !== newHash;\n }\n\n scrollToHash(as) {\n const [, hash] = as.split('#'); // Scroll to top if the hash is just `#` with no value\n\n if (hash === '') {\n window.scrollTo(0, 0);\n return;\n } // First we check if the element by id is found\n\n\n const idEl = document.getElementById(hash);\n\n if (idEl) {\n idEl.scrollIntoView();\n return;\n } // If there's no element with the id, we check the `name` property\n // To mirror browsers\n\n\n const nameEl = document.getElementsByName(hash)[0];\n\n if (nameEl) {\n nameEl.scrollIntoView();\n }\n }\n\n urlIsNew(asPath) {\n return this.asPath !== asPath;\n }\n /**\n * Prefetch page code, you may wait for the data during page rendering.\n * This feature only works in production!\n * @param url the href of prefetched page\n * @param asPath the as path of the prefetched page\n */\n\n\n prefetch(url, asPath = url, options = {}) {\n return new Promise((resolve, reject) => {\n const {\n pathname,\n protocol\n } = url_1.parse(url);\n\n if (!pathname || protocol) {\n if (false) {\n throw new Error(`Invalid href passed to router: ${url} https://err.sh/zeit/next.js/invalid-href-passed`);\n }\n\n return;\n } // Prefetch is not supported in development mode because it would trigger on-demand-entries\n\n\n if (false) {\n return;\n }\n\n const route = delBasePath(toRoute(pathname));\n Promise.all([this.pageLoader.prefetchData(url, delBasePath(asPath)), this.pageLoader[options.priority ? 'loadPage' : 'prefetch'](route)]).then(() => resolve(), reject);\n });\n }\n\n async fetchComponent(route) {\n let cancelled = false;\n\n const cancel = this.clc = () => {\n cancelled = true;\n };\n\n route = delBasePath(route);\n const componentResult = await this.pageLoader.loadPage(route);\n\n if (cancelled) {\n const error = new Error(`Abort fetching component for route: \"${route}\"`);\n error.cancelled = true;\n throw error;\n }\n\n if (cancel === this.clc) {\n this.clc = null;\n }\n\n return componentResult;\n }\n\n _getData(fn) {\n let cancelled = false;\n\n const cancel = () => {\n cancelled = true;\n };\n\n this.clc = cancel;\n return fn().then(data => {\n if (cancel === this.clc) {\n this.clc = null;\n }\n\n if (cancelled) {\n const err = new Error('Loading initial props cancelled');\n err.cancelled = true;\n throw err;\n }\n\n return data;\n });\n }\n\n getInitialProps(Component, ctx) {\n const {\n Component: App\n } = this.components['/_app'];\n\n const AppTree = this._wrapApp(App);\n\n ctx.AppTree = AppTree;\n return utils_1.loadGetInitialProps(App, {\n AppTree,\n Component,\n router: this,\n ctx\n });\n }\n\n abortComponentLoad(as) {\n if (this.clc) {\n const e = new Error('Route Cancelled');\n e.cancelled = true;\n Router.events.emit('routeChangeError', e, as);\n this.clc();\n this.clc = null;\n }\n }\n\n notify(data) {\n this.sub(data, this.components['/_app'].Component);\n }\n\n}\n\nexports.default = Router;\nRouter.events = mitt_1.default();","map":null,"metadata":{},"sourceType":"script"}