UNPKG

next-swr

Version:
442 lines (434 loc) 15.2 kB
Object.defineProperty(exports, '__esModule', { value: true }); var jsxRuntime = require('react/jsx-runtime'); var react = require('react'); var router = require('next/router'); function asyncGeneratorStep$2(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _async_to_generator$2(fn) { return function() { var self = this, args = arguments; return new Promise(function(resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep$2(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep$2(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } function useRefresh({ dedupingInterval, expires, revalidateIfStale, times }) { const { asPath, events, prefetch, replace } = router.useRouter(); const refreshTimerRef = react.useRef(); const isHardRefreshingRef = react.useRef(false); const isSoftRefreshingRef = react.useRef(false); const dedupingExpiration = react.useRef(0); const [revalidationAttempt, setRevalidationAttempt] = react.useState(0); react.useEffect(()=>{ setRevalidationAttempt(0); dedupingExpiration.current = 0; isHardRefreshingRef.current = false; isSoftRefreshingRef.current = false; function routeChangeStart(url) { clearTimeout(refreshTimerRef.current); if (url === asPath) return; isHardRefreshingRef.current = true; } function routeChangeError() { isHardRefreshingRef.current = false; } events.on('routeChangeStart', routeChangeStart); events.on('routeChangeError', routeChangeError); return ()=>{ clearTimeout(refreshTimerRef.current); events.off('routeChangeStart', routeChangeStart); events.off('routeChangeError', routeChangeError); }; }, [ asPath, events ]); function refresh({ delay = 0, skipRevalidateServerCache = false } = {}) { if (dedupingInterval === undefined) return; if (isHardRefreshingRef.current) return; if (isSoftRefreshingRef.current && skipRevalidateServerCache) return; if (revalidateIfStale && (!expires || expires + ((times == null ? void 0 : times.offset) || 60) > Date.now())) return; if (dedupingExpiration.current > Date.now()) return; dedupingExpiration.current = Date.now() + dedupingInterval; clearTimeout(refreshTimerRef.current); if (skipRevalidateServerCache) { isSoftRefreshingRef.current = true; } else { isHardRefreshingRef.current = true; isSoftRefreshingRef.current = false; } refreshTimerRef.current = setTimeout(/*#__PURE__*/ _async_to_generator$2(function*() { if (skipRevalidateServerCache) { yield prefetch(asPath, undefined, { unstable_skipClientCache: true }); yield replace(asPath, undefined, { scroll: false }); isSoftRefreshingRef.current = false; } else { if (!(times == null ? void 0 : times.firstLoad)) { yield replace(asPath, undefined, { unstable_skipClientCache: true, scroll: false }); } isHardRefreshingRef.current = false; } if (revalidationAttempt < 3) setRevalidationAttempt((attempt)=>attempt + 1); }), delay); } return { path: asPath, refresh, revalidationAttempt }; } function asyncGeneratorStep$1(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _async_to_generator$1(fn) { return function() { var self = this, args = arguments; return new Promise(function(resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep$1(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep$1(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } function _extends$2() { _extends$2 = Object.assign || function(target) { for(var i = 1; i < arguments.length; i++){ var source = arguments[i]; for(var key in source){ if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$2.apply(this, arguments); } function _object_without_properties_loose$1(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for(i = 0; i < sourceKeys.length; i++){ key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } const defaultTimes = { offset: 60, latency: 500, firstLoad: true }; const defaultSwrPath = '/swr'; const defaultCdnPropagationTimeInMs = 200; const defaultSwr = { revalidateIfStale: true, revalidateOnMount: true, revalidateOnFocus: true, refreshInterval: 0, dedupingInterval: undefined, swrPath: defaultSwrPath, expires: undefined, time: 0, times: defaultTimes }; const NextSwrContext = /*#__PURE__*/ react.createContext(defaultSwr); function RevalidateProvider(_param) { var { children, swr } = _param, props = _object_without_properties_loose$1(_param, [ "children", "swr" ]); const [times, setTimes] = react.useState(_extends$2({}, defaultTimes, swr == null ? void 0 : swr.times)); const cdnPropagationTimeRef = react.useRef(defaultCdnPropagationTimeInMs); const config = _extends$2({}, defaultSwr, swr, { times }); const { path, refresh, revalidationAttempt } = useRefresh(config); const { dedupingInterval, expires, refreshInterval, revalidateOnFocus, revalidateOnMount, swrPath } = config; react.useEffect(setCustomFetch, []); // set "times" on first load react.useEffect(()=>{ setTimeout(/*#__PURE__*/ _async_to_generator$1(function*() { let serverTime; const startTime = Date.now(); try { const response = yield fetch(swrPath); const body = yield response.json(); serverTime = body.timestamp || startTime; } catch (error) { serverTime = startTime; if (process.env.NODE_ENV === 'development') console.error(error); } const now = Date.now(); const offset = now - serverTime; const latency = now - startTime; setTimes({ offset, latency, firstLoad: false }); })); }, [ swrPath ]); // revalidate on focus react.useEffect(()=>{ if (!revalidateOnFocus) return; const onFocusListenerCallback = ()=>refresh(); window.addEventListener('focus', onFocusListenerCallback); return ()=>window.removeEventListener('focus', onFocusListenerCallback); }, [ refresh, revalidateOnFocus ]); // refresh interval react.useEffect(()=>{ if (!refreshInterval) return; let interval; if (typeof refreshInterval === 'number') { interval = setInterval(refresh, refreshInterval); } if (typeof refreshInterval === 'function') { interval = setInterval(refresh, refreshInterval(props)); } return ()=>clearInterval(interval); }, [ path, props, refresh, refreshInterval ]); // revalidate on mount react.useEffect(()=>{ if (!revalidateOnMount) return; if (process.env.NODE_ENV === 'development') return; if (dedupingInterval === undefined) return; if (revalidationAttempt > 2) return; if (times.firstLoad) return; if (revalidationAttempt === 0) { refresh(); return; } if (!expires) return; if (expires + times.offset > Date.now()) return; if (revalidationAttempt === 2 && cdnPropagationTimeRef.current < 16 * defaultCdnPropagationTimeInMs) cdnPropagationTimeRef.current *= 2; refresh({ delay: (0.5 + revalidationAttempt) * dedupingInterval + times.latency + cdnPropagationTimeRef.current, skipRevalidateServerCache: true }); }, [ dedupingInterval, expires, path, refresh, revalidateOnMount, revalidationAttempt, times ]); return /*#__PURE__*/ jsxRuntime.jsx(NextSwrContext.Provider, { value: config, children: children }); } function setCustomFetch() { if (process.env.NEXT_PUBLIC_HEAD_REQUESTS) return; if (![ 'production', 'preview' ].includes(process.env.NEXT_PUBLIC_VERCEL_ENV || '')) return; const originalFetch = globalThis.fetch; const customFetch = /*#__PURE__*/ _async_to_generator$1(function*(input, init) { var _init_headers; // HEAD requests are used by Next.js to revalidate the cache // But Vercel does not propagate revalidation data to CDN for HEAD requests // So this just causes a waste of lambda and database resources // Next.js ignores the HEAD response, so we can return a false response if ((init == null ? void 0 : init.method) === 'HEAD' && ((_init_headers = init.headers) == null ? void 0 : _init_headers['x-nextjs-data']) === '1') { return { ok: true, status: 304, statusText: 'Not Modified', json: /*#__PURE__*/ _async_to_generator$1(function*() { return null; }) }; } return yield originalFetch(input, init); }); globalThis.fetch = customFetch; return ()=>{ globalThis.fetch = originalFetch; }; } function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _async_to_generator(fn) { return function() { var self = this, args = arguments; return new Promise(function(resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } function _extends$1() { _extends$1 = Object.assign || function(target) { for(var i = 1; i < arguments.length; i++){ var source = arguments[i]; for(var key in source){ if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$1.apply(this, arguments); } function _object_without_properties_loose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for(i = 0; i < sourceKeys.length; i++){ key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } function getStaticPropsRevalidate(getStaticProps) { return /*#__PURE__*/ _async_to_generator(function*(ctx) { const startTime = Date.now(); const _ref = yield getStaticProps(ctx), { props, revalidate: _revalidate, swr } = _ref, rest = _object_without_properties_loose(_ref, [ "props", "revalidate", "swr" ]); const _ref1 = swr || {}, { revalidate_f, dedupingInterval = 0 } = _ref1, swrRest = _object_without_properties_loose(_ref1, [ "revalidate_f", "dedupingInterval" ]); const expires = typeof _revalidate === 'number' && _revalidate !== 0 ? (_revalidate * 1000 + Date.now()) * 2 - startTime : 0; const revalidationDuration = Date.now() - startTime; const revalidate = revalidate_f ? revalidate_f : typeof _revalidate === 'number' && _revalidate !== 0 ? Math.floor(revalidationDuration / 1000 + _revalidate) : false; return _extends$1({ props: _extends$1({ swr: _extends$1({ expires, dedupingInterval: revalidationDuration + dedupingInterval, time: startTime }, swrRest) }, props), revalidate }, rest); }); } function useRevalidate(props) { const [state, setNewState] = react.useState(props); const { time = 0, times } = react.useContext(NextSwrContext); const timeRef = react.useRef(time); react.useEffect(()=>{ if (time > timeRef.current || !time) { setNewState(props); timeRef.current = time; } }, [ props, time ]); function setState(newState) { timeRef.current = Date.now() - ((times == null ? void 0 : times.offset) || 0); setNewState(newState); } return [ state, setState ]; } function _extends() { _extends = Object.assign || function(target) { for(var i = 1; i < arguments.length; i++){ var source = arguments[i]; for(var key in source){ if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function useTimes() { const { time = 0, times } = react.useContext(NextSwrContext); return _extends({ time }, times); } exports.RevalidateProvider = RevalidateProvider; exports.getStaticPropsRevalidate = getStaticPropsRevalidate; exports.useRevalidate = useRevalidate; exports.useTimes = useTimes;