Spaces:
Running
Running
| ; | |
| Object.defineProperty(exports, "__esModule", { | |
| value: true | |
| }); | |
| 0 && (module.exports = { | |
| IDLE_LINK_STATUS: null, | |
| PENDING_LINK_STATUS: null, | |
| mountFormInstance: null, | |
| mountLinkInstance: null, | |
| onLinkVisibilityChanged: null, | |
| onNavigationIntent: null, | |
| pingVisibleLinks: null, | |
| setLinkForCurrentNavigation: null, | |
| unmountLinkForCurrentNavigation: null, | |
| unmountPrefetchableInstance: null | |
| }); | |
| function _export(target, all) { | |
| for(var name in all)Object.defineProperty(target, name, { | |
| enumerable: true, | |
| get: all[name] | |
| }); | |
| } | |
| _export(exports, { | |
| IDLE_LINK_STATUS: function() { | |
| return IDLE_LINK_STATUS; | |
| }, | |
| PENDING_LINK_STATUS: function() { | |
| return PENDING_LINK_STATUS; | |
| }, | |
| mountFormInstance: function() { | |
| return mountFormInstance; | |
| }, | |
| mountLinkInstance: function() { | |
| return mountLinkInstance; | |
| }, | |
| onLinkVisibilityChanged: function() { | |
| return onLinkVisibilityChanged; | |
| }, | |
| onNavigationIntent: function() { | |
| return onNavigationIntent; | |
| }, | |
| pingVisibleLinks: function() { | |
| return pingVisibleLinks; | |
| }, | |
| setLinkForCurrentNavigation: function() { | |
| return setLinkForCurrentNavigation; | |
| }, | |
| unmountLinkForCurrentNavigation: function() { | |
| return unmountLinkForCurrentNavigation; | |
| }, | |
| unmountPrefetchableInstance: function() { | |
| return unmountPrefetchableInstance; | |
| } | |
| }); | |
| const _types = require("./segment-cache/types"); | |
| const _cachekey = require("./segment-cache/cache-key"); | |
| const _scheduler = require("./segment-cache/scheduler"); | |
| const _react = require("react"); | |
| // Tracks the most recently navigated link instance. When null, indicates | |
| // the current navigation was not initiated by a link click. | |
| let linkForMostRecentNavigation = null; | |
| const PENDING_LINK_STATUS = { | |
| pending: true | |
| }; | |
| const IDLE_LINK_STATUS = { | |
| pending: false | |
| }; | |
| function setLinkForCurrentNavigation(link) { | |
| (0, _react.startTransition)(()=>{ | |
| linkForMostRecentNavigation?.setOptimisticLinkStatus(IDLE_LINK_STATUS); | |
| link?.setOptimisticLinkStatus(PENDING_LINK_STATUS); | |
| linkForMostRecentNavigation = link; | |
| }); | |
| } | |
| function unmountLinkForCurrentNavigation(link) { | |
| if (linkForMostRecentNavigation === link) { | |
| linkForMostRecentNavigation = null; | |
| } | |
| } | |
| // Use a WeakMap to associate a Link instance with its DOM element. This is | |
| // used by the IntersectionObserver to track the link's visibility. | |
| const prefetchable = typeof WeakMap === 'function' ? new WeakMap() : new Map(); | |
| // A Set of the currently visible links. We re-prefetch visible links after a | |
| // cache invalidation, or when the current URL changes. It's a separate data | |
| // structure from the WeakMap above because only the visible links need to | |
| // be enumerated. | |
| const prefetchableAndVisible = new Set(); | |
| // A single IntersectionObserver instance shared by all <Link> components. | |
| const observer = typeof IntersectionObserver === 'function' ? new IntersectionObserver(handleIntersect, { | |
| rootMargin: '200px' | |
| }) : null; | |
| function observeVisibility(element, instance) { | |
| const existingInstance = prefetchable.get(element); | |
| if (existingInstance !== undefined) { | |
| // This shouldn't happen because each <Link> component should have its own | |
| // anchor tag instance, but it's defensive coding to avoid a memory leak in | |
| // case there's a logical error somewhere else. | |
| unmountPrefetchableInstance(element); | |
| } | |
| // Only track prefetchable links that have a valid prefetch URL | |
| prefetchable.set(element, instance); | |
| if (observer !== null) { | |
| observer.observe(element); | |
| } | |
| } | |
| function coercePrefetchableUrl(href) { | |
| if (typeof window !== 'undefined') { | |
| const { createPrefetchURL } = require('./app-router-utils'); | |
| try { | |
| return createPrefetchURL(href); | |
| } catch { | |
| // createPrefetchURL sometimes throws an error if an invalid URL is | |
| // provided, though I'm not sure if it's actually necessary. | |
| // TODO: Consider removing the throw from the inner function, or change it | |
| // to reportError. Or maybe the error isn't even necessary for automatic | |
| // prefetches, just navigations. | |
| const reportErrorFn = typeof reportError === 'function' ? reportError : console.error; | |
| reportErrorFn(`Cannot prefetch '${href}' because it cannot be converted to a URL.`); | |
| return null; | |
| } | |
| } else { | |
| return null; | |
| } | |
| } | |
| function mountLinkInstance(element, href, router, fetchStrategy, prefetchEnabled, setOptimisticLinkStatus) { | |
| if (prefetchEnabled) { | |
| const prefetchURL = coercePrefetchableUrl(href); | |
| if (prefetchURL !== null) { | |
| const instance = { | |
| router, | |
| fetchStrategy, | |
| isVisible: false, | |
| prefetchTask: null, | |
| prefetchHref: prefetchURL.href, | |
| setOptimisticLinkStatus | |
| }; | |
| // We only observe the link's visibility if it's prefetchable. For | |
| // example, this excludes links to external URLs. | |
| observeVisibility(element, instance); | |
| return instance; | |
| } | |
| } | |
| // If the link is not prefetchable, we still create an instance so we can | |
| // track its optimistic state (i.e. useLinkStatus). | |
| const instance = { | |
| router, | |
| fetchStrategy, | |
| isVisible: false, | |
| prefetchTask: null, | |
| prefetchHref: null, | |
| setOptimisticLinkStatus | |
| }; | |
| return instance; | |
| } | |
| function mountFormInstance(element, href, router, fetchStrategy) { | |
| const prefetchURL = coercePrefetchableUrl(href); | |
| if (prefetchURL === null) { | |
| // This href is not prefetchable, so we don't track it. | |
| // TODO: We currently observe/unobserve a form every time its href changes. | |
| // For Links, this isn't a big deal because the href doesn't usually change, | |
| // but for forms it's extremely common. We should optimize this. | |
| return; | |
| } | |
| const instance = { | |
| router, | |
| fetchStrategy, | |
| isVisible: false, | |
| prefetchTask: null, | |
| prefetchHref: prefetchURL.href, | |
| setOptimisticLinkStatus: null | |
| }; | |
| observeVisibility(element, instance); | |
| } | |
| function unmountPrefetchableInstance(element) { | |
| const instance = prefetchable.get(element); | |
| if (instance !== undefined) { | |
| prefetchable.delete(element); | |
| prefetchableAndVisible.delete(instance); | |
| const prefetchTask = instance.prefetchTask; | |
| if (prefetchTask !== null) { | |
| (0, _scheduler.cancelPrefetchTask)(prefetchTask); | |
| } | |
| } | |
| if (observer !== null) { | |
| observer.unobserve(element); | |
| } | |
| } | |
| function handleIntersect(entries) { | |
| for (const entry of entries){ | |
| // Some extremely old browsers or polyfills don't reliably support | |
| // isIntersecting so we check intersectionRatio instead. (Do we care? Not | |
| // really. But whatever this is fine.) | |
| const isVisible = entry.intersectionRatio > 0; | |
| onLinkVisibilityChanged(entry.target, isVisible); | |
| } | |
| } | |
| function onLinkVisibilityChanged(element, isVisible) { | |
| if (process.env.NODE_ENV !== 'production') { | |
| // Prefetching on viewport is disabled in development for performance | |
| // reasons, because it requires compiling the target page. | |
| // TODO: Investigate re-enabling this. | |
| return; | |
| } | |
| const instance = prefetchable.get(element); | |
| if (instance === undefined) { | |
| return; | |
| } | |
| instance.isVisible = isVisible; | |
| if (isVisible) { | |
| prefetchableAndVisible.add(instance); | |
| } else { | |
| prefetchableAndVisible.delete(instance); | |
| } | |
| rescheduleLinkPrefetch(instance, _types.PrefetchPriority.Default); | |
| } | |
| function onNavigationIntent(element, unstable_upgradeToDynamicPrefetch) { | |
| const instance = prefetchable.get(element); | |
| if (instance === undefined) { | |
| return; | |
| } | |
| // Prefetch the link on hover/touchstart. | |
| if (instance !== undefined) { | |
| if (process.env.__NEXT_DYNAMIC_ON_HOVER && unstable_upgradeToDynamicPrefetch) { | |
| // Switch to a full prefetch | |
| instance.fetchStrategy = _types.FetchStrategy.Full; | |
| } | |
| rescheduleLinkPrefetch(instance, _types.PrefetchPriority.Intent); | |
| } | |
| } | |
| function rescheduleLinkPrefetch(instance, priority) { | |
| // Ensures that app-router-instance is not compiled in the server bundle | |
| if (typeof window !== 'undefined') { | |
| const existingPrefetchTask = instance.prefetchTask; | |
| if (!instance.isVisible) { | |
| // Cancel any in-progress prefetch task. (If it already finished then this | |
| // is a no-op.) | |
| if (existingPrefetchTask !== null) { | |
| (0, _scheduler.cancelPrefetchTask)(existingPrefetchTask); | |
| } | |
| // We don't need to reset the prefetchTask to null upon cancellation; an | |
| // old task object can be rescheduled with reschedulePrefetchTask. This is a | |
| // micro-optimization but also makes the code simpler (don't need to | |
| // worry about whether an old task object is stale). | |
| return; | |
| } | |
| const { getCurrentAppRouterState } = require('./app-router-instance'); | |
| const appRouterState = getCurrentAppRouterState(); | |
| if (appRouterState !== null) { | |
| const treeAtTimeOfPrefetch = appRouterState.tree; | |
| if (existingPrefetchTask === null) { | |
| // Initiate a prefetch task. | |
| const nextUrl = appRouterState.nextUrl; | |
| const cacheKey = (0, _cachekey.createCacheKey)(instance.prefetchHref, nextUrl); | |
| instance.prefetchTask = (0, _scheduler.schedulePrefetchTask)(cacheKey, treeAtTimeOfPrefetch, instance.fetchStrategy, priority, null); | |
| } else { | |
| // We already have an old task object that we can reschedule. This is | |
| // effectively the same as canceling the old task and creating a new one. | |
| (0, _scheduler.reschedulePrefetchTask)(existingPrefetchTask, treeAtTimeOfPrefetch, instance.fetchStrategy, priority); | |
| } | |
| } | |
| } | |
| } | |
| function pingVisibleLinks(nextUrl, tree) { | |
| // For each currently visible link, cancel the existing prefetch task (if it | |
| // exists) and schedule a new one. This is effectively the same as if all the | |
| // visible links left and then re-entered the viewport. | |
| // | |
| // This is called when the Next-Url or the base tree changes, since those | |
| // may affect the result of a prefetch task. It's also called after a | |
| // cache invalidation. | |
| for (const instance of prefetchableAndVisible){ | |
| const task = instance.prefetchTask; | |
| if (task !== null && !(0, _scheduler.isPrefetchTaskDirty)(task, nextUrl, tree)) { | |
| continue; | |
| } | |
| // Something changed. Cancel the existing prefetch task and schedule a | |
| // new one. | |
| if (task !== null) { | |
| (0, _scheduler.cancelPrefetchTask)(task); | |
| } | |
| const cacheKey = (0, _cachekey.createCacheKey)(instance.prefetchHref, nextUrl); | |
| instance.prefetchTask = (0, _scheduler.schedulePrefetchTask)(cacheKey, tree, instance.fetchStrategy, _types.PrefetchPriority.Default, null); | |
| } | |
| } | |
| 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=links.js.map |