123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294 |
- "use strict";
- Object.defineProperty(exports, "__esModule", {
- value: true
- });
- exports.markAssetError = markAssetError;
- exports.isAssetError = isAssetError;
- exports.getClientBuildManifest = getClientBuildManifest;
- exports.createRouteLoader = createRouteLoader;
- var _interop_require_default = require("@swc/helpers/lib/_interop_require_default.js").default;
- var _getAssetPathFromRoute = _interop_require_default(require("../shared/lib/router/utils/get-asset-path-from-route"));
- var _trustedTypes = require("./trusted-types");
- var _requestIdleCallback = require("./request-idle-callback");
- // 3.8s was arbitrarily chosen as it's what https://web.dev/interactive
- // considers as "Good" time-to-interactive. We must assume something went
- // wrong beyond this point, and then fall-back to a full page transition to
- // show the user something of value.
- const MS_MAX_IDLE_DELAY = 3800;
- function withFuture(key, map, generator) {
- let entry = map.get(key);
- if (entry) {
- if ('future' in entry) {
- return entry.future;
- }
- return Promise.resolve(entry);
- }
- let resolver;
- const prom = new Promise((resolve)=>{
- resolver = resolve;
- });
- map.set(key, entry = {
- resolve: resolver,
- future: prom
- });
- return generator ? generator()// eslint-disable-next-line no-sequences
- .then((value)=>(resolver(value), value)).catch((err)=>{
- map.delete(key);
- throw err;
- }) : prom;
- }
- function hasPrefetch(link) {
- try {
- link = document.createElement('link');
- return(// detect IE11 since it supports prefetch but isn't detected
- // with relList.support
- (!!window.MSInputMethodContext && !!document.documentMode) || link.relList.supports('prefetch'));
- } catch (e) {
- return false;
- }
- }
- const canPrefetch = hasPrefetch();
- function prefetchViaDom(href, as, link) {
- return new Promise((res, rej)=>{
- const selector = `
- link[rel="prefetch"][href^="${href}"],
- link[rel="preload"][href^="${href}"],
- script[src^="${href}"]`;
- if (document.querySelector(selector)) {
- return res();
- }
- link = document.createElement('link');
- // The order of property assignment here is intentional:
- if (as) link.as = as;
- link.rel = `prefetch`;
- link.crossOrigin = process.env.__NEXT_CROSS_ORIGIN;
- link.onload = res;
- link.onerror = rej;
- // `href` should always be last:
- link.href = href;
- document.head.appendChild(link);
- });
- }
- const ASSET_LOAD_ERROR = Symbol('ASSET_LOAD_ERROR');
- function markAssetError(err) {
- return Object.defineProperty(err, ASSET_LOAD_ERROR, {});
- }
- function isAssetError(err) {
- return err && ASSET_LOAD_ERROR in err;
- }
- function appendScript(src, script) {
- return new Promise((resolve, reject)=>{
- script = document.createElement('script');
- // The order of property assignment here is intentional.
- // 1. Setup success/failure hooks in case the browser synchronously
- // executes when `src` is set.
- script.onload = resolve;
- script.onerror = ()=>reject(markAssetError(new Error(`Failed to load script: ${src}`)));
- // 2. Configure the cross-origin attribute before setting `src` in case the
- // browser begins to fetch.
- script.crossOrigin = process.env.__NEXT_CROSS_ORIGIN;
- // 3. Finally, set the source and inject into the DOM in case the child
- // must be appended for fetching to start.
- script.src = src;
- document.body.appendChild(script);
- });
- }
- // We wait for pages to be built in dev before we start the route transition
- // timeout to prevent an un-necessary hard navigation in development.
- let devBuildPromise;
- // Resolve a promise that times out after given amount of milliseconds.
- function resolvePromiseWithTimeout(p, ms, err) {
- return new Promise((resolve, reject)=>{
- let cancelled = false;
- p.then((r)=>{
- // Resolved, cancel the timeout
- cancelled = true;
- resolve(r);
- }).catch(reject);
- // We wrap these checks separately for better dead-code elimination in
- // production bundles.
- if (process.env.NODE_ENV === 'development') {
- (devBuildPromise || Promise.resolve()).then(()=>{
- (0, _requestIdleCallback).requestIdleCallback(()=>setTimeout(()=>{
- if (!cancelled) {
- reject(err);
- }
- }, ms));
- });
- }
- if (process.env.NODE_ENV !== 'development') {
- (0, _requestIdleCallback).requestIdleCallback(()=>setTimeout(()=>{
- if (!cancelled) {
- reject(err);
- }
- }, ms));
- }
- });
- }
- function getClientBuildManifest() {
- if (self.__BUILD_MANIFEST) {
- return Promise.resolve(self.__BUILD_MANIFEST);
- }
- const onBuildManifest = new Promise((resolve)=>{
- // Mandatory because this is not concurrent safe:
- const cb = self.__BUILD_MANIFEST_CB;
- self.__BUILD_MANIFEST_CB = ()=>{
- resolve(self.__BUILD_MANIFEST);
- cb && cb();
- };
- });
- return resolvePromiseWithTimeout(onBuildManifest, MS_MAX_IDLE_DELAY, markAssetError(new Error('Failed to load client build manifest')));
- }
- function getFilesForRoute(assetPrefix, route) {
- if (process.env.NODE_ENV === 'development') {
- const scriptUrl = assetPrefix + '/_next/static/chunks/pages' + encodeURI((0, _getAssetPathFromRoute).default(route, '.js'));
- return Promise.resolve({
- scripts: [
- (0, _trustedTypes).__unsafeCreateTrustedScriptURL(scriptUrl)
- ],
- // Styles are handled by `style-loader` in development:
- css: []
- });
- }
- return getClientBuildManifest().then((manifest)=>{
- if (!(route in manifest)) {
- throw markAssetError(new Error(`Failed to lookup route: ${route}`));
- }
- const allFiles = manifest[route].map((entry)=>assetPrefix + '/_next/' + encodeURI(entry));
- return {
- scripts: allFiles.filter((v)=>v.endsWith('.js')).map((v)=>(0, _trustedTypes).__unsafeCreateTrustedScriptURL(v)),
- css: allFiles.filter((v)=>v.endsWith('.css'))
- };
- });
- }
- function createRouteLoader(assetPrefix) {
- const entrypoints = new Map();
- const loadedScripts = new Map();
- const styleSheets = new Map();
- const routes = new Map();
- function maybeExecuteScript(src) {
- // With HMR we might need to "reload" scripts when they are
- // disposed and readded. Executing scripts twice has no functional
- // differences
- if (process.env.NODE_ENV !== 'development') {
- let prom = loadedScripts.get(src.toString());
- if (prom) {
- return prom;
- }
- // Skip executing script if it's already in the DOM:
- if (document.querySelector(`script[src^="${src}"]`)) {
- return Promise.resolve();
- }
- loadedScripts.set(src.toString(), prom = appendScript(src));
- return prom;
- } else {
- return appendScript(src);
- }
- }
- function fetchStyleSheet(href) {
- let prom = styleSheets.get(href);
- if (prom) {
- return prom;
- }
- styleSheets.set(href, prom = fetch(href).then((res)=>{
- if (!res.ok) {
- throw new Error(`Failed to load stylesheet: ${href}`);
- }
- return res.text().then((text)=>({
- href: href,
- content: text
- }));
- }).catch((err)=>{
- throw markAssetError(err);
- }));
- return prom;
- }
- return {
- whenEntrypoint (route) {
- return withFuture(route, entrypoints);
- },
- onEntrypoint (route, execute) {
- (execute ? Promise.resolve().then(()=>execute()).then((exports)=>({
- component: exports && exports.default || exports,
- exports: exports
- }), (err)=>({
- error: err
- })) : Promise.resolve(undefined)).then((input)=>{
- const old = entrypoints.get(route);
- if (old && 'resolve' in old) {
- if (input) {
- entrypoints.set(route, input);
- old.resolve(input);
- }
- } else {
- if (input) {
- entrypoints.set(route, input);
- } else {
- entrypoints.delete(route);
- }
- // when this entrypoint has been resolved before
- // the route is outdated and we want to invalidate
- // this cache entry
- routes.delete(route);
- }
- });
- },
- loadRoute (route, prefetch) {
- return withFuture(route, routes, ()=>{
- let devBuildPromiseResolve;
- if (process.env.NODE_ENV === 'development') {
- devBuildPromise = new Promise((resolve)=>{
- devBuildPromiseResolve = resolve;
- });
- }
- return resolvePromiseWithTimeout(getFilesForRoute(assetPrefix, route).then(({ scripts , css })=>{
- return Promise.all([
- entrypoints.has(route) ? [] : Promise.all(scripts.map(maybeExecuteScript)),
- Promise.all(css.map(fetchStyleSheet)),
- ]);
- }).then((res)=>{
- return this.whenEntrypoint(route).then((entrypoint)=>({
- entrypoint,
- styles: res[1]
- }));
- }), MS_MAX_IDLE_DELAY, markAssetError(new Error(`Route did not complete loading: ${route}`))).then(({ entrypoint , styles })=>{
- const res = Object.assign({
- styles: styles
- }, entrypoint);
- return 'error' in entrypoint ? entrypoint : res;
- }).catch((err)=>{
- if (prefetch) {
- // we don't want to cache errors during prefetch
- throw err;
- }
- return {
- error: err
- };
- }).finally(()=>{
- return devBuildPromiseResolve == null ? void 0 : devBuildPromiseResolve();
- });
- });
- },
- prefetch (route) {
- // https://github.com/GoogleChromeLabs/quicklink/blob/453a661fa1fa940e2d2e044452398e38c67a98fb/src/index.mjs#L115-L118
- // License: Apache 2.0
- let cn;
- if (cn = navigator.connection) {
- // Don't prefetch if using 2G or if Save-Data is enabled.
- if (cn.saveData || /2g/.test(cn.effectiveType)) return Promise.resolve();
- }
- return getFilesForRoute(assetPrefix, route).then((output)=>Promise.all(canPrefetch ? output.scripts.map((script)=>prefetchViaDom(script.toString(), 'script')) : [])).then(()=>{
- (0, _requestIdleCallback).requestIdleCallback(()=>this.loadRoute(route, true).catch(()=>{}));
- }).catch(// swallow prefetch errors
- ()=>{});
- }
- };
- }
- 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=route-loader.js.map
|