UNPKG

next

Version:

The React Framework

226 lines (225 loc) • 11 kB
import { InvariantError } from '../../shared/lib/invariant-error'; import { normalizeAppPath } from '../../shared/lib/router/utils/app-paths'; import { pathHasPrefix } from '../../shared/lib/router/utils/path-has-prefix'; import { removePathPrefix } from '../../shared/lib/router/utils/remove-path-prefix'; import { workAsyncStorage } from './work-async-storage.external'; // This is a global singleton that is, among other things, also used to // encode/decode bound args of server function closures. This can't be using a // AsyncLocalStorage as it might happen at the module level. const MANIFESTS_SINGLETON = Symbol.for('next.server.manifests'); const globalThisWithManifests = globalThis; function createProxiedClientReferenceManifest(clientReferenceManifestsPerRoute) { const createMappingProxy = (prop)=>{ return new Proxy({}, { get (_, id) { const workStore = workAsyncStorage.getStore(); if (workStore) { const currentManifest = clientReferenceManifestsPerRoute.get(workStore.route); if (currentManifest == null ? void 0 : currentManifest[prop][id]) { return currentManifest[prop][id]; } // In development, we also check all other manifests to see if the // module exists there. This is to support a scenario where React's // I/O tracking (dev-only) creates a connection from one page to // another through an emitted async I/O node that references client // components from the other page, e.g. in owner props. // TODO: Maybe we need to add a `debugBundlerConfig` option to React // to avoid this workaround. The current workaround has the // disadvantage that one might accidentally or intentionally share // client references across pages (e.g. by storing them in a global // variable), which would then only be caught in production. if (process.env.NODE_ENV !== 'production') { for (const [route, manifest] of clientReferenceManifestsPerRoute){ if (route === workStore.route) { continue; } const entry = manifest[prop][id]; if (entry !== undefined) { return entry; } } } } else { // If there's no work store defined, we can assume that a client // reference manifest is needed during module evaluation, e.g. to // create a server function using a higher-order function. This // might also use client components which need to be serialized by // Flight, and therefore client references need to be resolvable. In // that case we search all page manifests to find the module. for (const manifest of clientReferenceManifestsPerRoute.values()){ const entry = manifest[prop][id]; if (entry !== undefined) { return entry; } } } return undefined; } }); }; const mappingProxies = new Map(); return new Proxy({}, { get (_, prop) { const workStore = workAsyncStorage.getStore(); switch(prop){ case 'moduleLoading': case 'entryCSSFiles': case 'entryJSFiles': { if (!workStore) { throw Object.defineProperty(new InvariantError(`Cannot access "${prop}" without a work store.`), "__NEXT_ERROR_CODE", { value: "E952", enumerable: false, configurable: true }); } const currentManifest = clientReferenceManifestsPerRoute.get(workStore.route); if (!currentManifest) { throw Object.defineProperty(new InvariantError(`The client reference manifest for route "${workStore.route}" does not exist.`), "__NEXT_ERROR_CODE", { value: "E951", enumerable: false, configurable: true }); } return currentManifest[prop]; } case 'clientModules': case 'rscModuleMapping': case 'edgeRscModuleMapping': case 'ssrModuleMapping': case 'edgeSSRModuleMapping': { let proxy = mappingProxies.get(prop); if (!proxy) { proxy = createMappingProxy(prop); mappingProxies.set(prop, proxy); } return proxy; } default: { throw Object.defineProperty(new InvariantError(`This is a proxied client reference manifest. The property "${String(prop)}" is not handled.`), "__NEXT_ERROR_CODE", { value: "E953", enumerable: false, configurable: true }); } } } }); } /** * This function creates a Flight-acceptable server module map proxy from our * Server Reference Manifest similar to our client module map. This is because * our manifest contains a lot of internal Next.js data that are relevant to the * runtime, workers, etc. that React doesn't need to know. */ function createServerModuleMap() { return new Proxy({}, { get: (_, id)=>{ var _getServerActionsManifest__id, _getServerActionsManifest_; const workers = (_getServerActionsManifest_ = getServerActionsManifest()[process.env.NEXT_RUNTIME === 'edge' ? 'edge' : 'node']) == null ? void 0 : (_getServerActionsManifest__id = _getServerActionsManifest_[id]) == null ? void 0 : _getServerActionsManifest__id.workers; if (!workers) { return undefined; } const workStore = workAsyncStorage.getStore(); let workerEntry; if (workStore) { workerEntry = workers[normalizeWorkerPageName(workStore.page)]; } else { // If there's no work store defined, we can assume that a server // module map is needed during module evaluation, e.g. to create a // server action using a higher-order function. Therefore it should be // safe to return any entry from the manifest that matches the action // ID. They all refer to the same module ID, which must also exist in // the current page bundle. TODO: This is currently not guaranteed in // Turbopack, and needs to be fixed. workerEntry = Object.values(workers).at(0); } if (!workerEntry) { return undefined; } const { moduleId, async } = workerEntry; return { id: moduleId, name: id, chunks: [], async }; } }); } /** * The flight entry loader keys actions by bundlePath. bundlePath corresponds * with the relative path (including 'app') to the page entrypoint. */ function normalizeWorkerPageName(pageName) { if (pathHasPrefix(pageName, 'app')) { return pageName; } return 'app' + pageName; } /** * Converts a bundlePath (relative path to the entrypoint) to a routable page * name. */ function denormalizeWorkerPageName(bundlePath) { return normalizeAppPath(removePathPrefix(bundlePath, 'app')); } /** * Checks if the requested action has a worker for the current page. * If not, it returns the first worker that has a handler for the action. */ export function selectWorkerForForwarding(actionId, pageName) { var _serverActionsManifest__actionId; const serverActionsManifest = getServerActionsManifest(); const workers = (_serverActionsManifest__actionId = serverActionsManifest[process.env.NEXT_RUNTIME === 'edge' ? 'edge' : 'node'][actionId]) == null ? void 0 : _serverActionsManifest__actionId.workers; // There are no workers to handle this action, nothing to forward to. if (!workers) { return; } // If there is an entry for the current page, we don't need to forward. if (workers[normalizeWorkerPageName(pageName)]) { return; } // Otherwise, grab the first worker that has a handler for this action id. return denormalizeWorkerPageName(Object.keys(workers)[0]); } export function setManifestsSingleton({ page, clientReferenceManifest, serverActionsManifest }) { const existingSingleton = globalThisWithManifests[MANIFESTS_SINGLETON]; if (existingSingleton) { existingSingleton.clientReferenceManifestsPerRoute.set(normalizeAppPath(page), clientReferenceManifest); existingSingleton.serverActionsManifest = serverActionsManifest; } else { const clientReferenceManifestsPerRoute = new Map([ [ normalizeAppPath(page), clientReferenceManifest ] ]); const proxiedClientReferenceManifest = createProxiedClientReferenceManifest(clientReferenceManifestsPerRoute); globalThisWithManifests[MANIFESTS_SINGLETON] = { clientReferenceManifestsPerRoute, proxiedClientReferenceManifest, serverActionsManifest, serverModuleMap: createServerModuleMap() }; } } function getManifestsSingleton() { const manifestSingleton = globalThisWithManifests[MANIFESTS_SINGLETON]; if (!manifestSingleton) { throw Object.defineProperty(new InvariantError('The manifests singleton was not initialized.'), "__NEXT_ERROR_CODE", { value: "E950", enumerable: false, configurable: true }); } return manifestSingleton; } export function getClientReferenceManifest() { return getManifestsSingleton().proxiedClientReferenceManifest; } export function getServerActionsManifest() { return getManifestsSingleton().serverActionsManifest; } export function getServerModuleMap() { return getManifestsSingleton().serverModuleMap; } //# sourceMappingURL=manifests-singleton.js.map