UNPKG

next

Version:

The React Framework

156 lines (155 loc) 6.79 kB
/** * Based on https://github.com/facebook/react/blob/d4e78c42a94be027b4dc7ed2659a5fddfbf9bd4e/packages/react/src/ReactFetch.js */ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "createDedupeFetch", { enumerable: true, get: function() { return createDedupeFetch; } }); const _react = /*#__PURE__*/ _interop_require_wildcard(require("react")); const _cloneresponse = require("./clone-response"); const _invarianterror = require("../../shared/lib/invariant-error"); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interop_require_wildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = { __proto__: null }; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for(var key in obj){ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } const simpleCacheKey = '["GET",[],null,"follow",null,null,null,null]' // generateCacheKey(new Request('https://blank')); ; function generateCacheKey(request) { // We pick the fields that goes into the key used to dedupe requests. // We don't include the `cache` field, because we end up using whatever // caching resulted from the first request. // Notably we currently don't consider non-standard (or future) options. // This might not be safe. TODO: warn for non-standard extensions differing. // IF YOU CHANGE THIS UPDATE THE simpleCacheKey ABOVE. return JSON.stringify([ request.method, Array.from(request.headers.entries()), request.mode, request.redirect, request.credentials, request.referrer, request.referrerPolicy, request.integrity ]); } function createDedupeFetch(originalFetch) { const getCacheEntries = _react.cache(// eslint-disable-next-line @typescript-eslint/no-unused-vars -- url is the cache key (url)=>[]); return function dedupeFetch(resource, options) { if (options && options.signal) { // If we're passed a signal, then we assume that // someone else controls the lifetime of this object and opts out of // caching. It's effectively the opt-out mechanism. // Ideally we should be able to check this on the Request but // it always gets initialized with its own signal so we don't // know if it's supposed to override - unless we also override the // Request constructor. return originalFetch(resource, options); } // Normalize the Request let url; let cacheKey; if (typeof resource === 'string' && !options) { // Fast path. cacheKey = simpleCacheKey; url = resource; } else { // Normalize the request. // if resource is not a string or a URL (its an instance of Request) // then do not instantiate a new Request but instead // reuse the request as to not disturb the body in the event it's a ReadableStream. const request = typeof resource === 'string' || resource instanceof URL ? new Request(resource, options) : resource; if (request.method !== 'GET' && request.method !== 'HEAD' || request.keepalive) { // We currently don't dedupe requests that might have side-effects. Those // have to be explicitly cached. We assume that the request doesn't have a // body if it's GET or HEAD. // keepalive gets treated the same as if you passed a custom cache signal. return originalFetch(resource, options); } cacheKey = generateCacheKey(request); url = request.url; } const cacheEntries = getCacheEntries(url); for(let i = 0, j = cacheEntries.length; i < j; i += 1){ const [key, promise] = cacheEntries[i]; if (key === cacheKey) { return promise.then(()=>{ const response = cacheEntries[i][2]; if (!response) throw Object.defineProperty(new _invarianterror.InvariantError('No cached response'), "__NEXT_ERROR_CODE", { value: "E579", enumerable: false, configurable: true }); // We're cloning the response using this utility because there exists // a bug in the undici library around response cloning. See the // following pull request for more details: // https://github.com/vercel/next.js/pull/73274 const [cloned1, cloned2] = (0, _cloneresponse.cloneResponse)(response); cacheEntries[i][2] = cloned2; return cloned1; }); } } // We pass the original arguments here in case normalizing the Request // doesn't include all the options in this environment. const promise = originalFetch(resource, options); const entry = [ cacheKey, promise, null ]; cacheEntries.push(entry); return promise.then((response)=>{ // We're cloning the response using this utility because there exists // a bug in the undici library around response cloning. See the // following pull request for more details: // https://github.com/vercel/next.js/pull/73274 const [cloned1, cloned2] = (0, _cloneresponse.cloneResponse)(response); entry[2] = cloned2; return cloned1; }); }; } //# sourceMappingURL=dedupe-fetch.js.map