195 lines
9.5 KiB
JavaScript
195 lines
9.5 KiB
JavaScript
/**
|
|
* The functions provided by this module are used to communicate certain properties
|
|
* about the currently running code so that Next.js can make decisions on how to handle
|
|
* the current execution in different rendering modes such as pre-rendering, resuming, and SSR.
|
|
*
|
|
* Today Next.js treats all code as potentially static. Certain APIs may only make sense when dynamically rendering.
|
|
* Traditionally this meant deopting the entire render to dynamic however with PPR we can now deopt parts
|
|
* of a React tree as dynamic while still keeping other parts static. There are really two different kinds of
|
|
* Dynamic indications.
|
|
*
|
|
* The first is simply an intention to be dynamic. unstable_noStore is an example of this where
|
|
* the currently executing code simply declares that the current scope is dynamic but if you use it
|
|
* inside unstable_cache it can still be cached. This type of indication can be removed if we ever
|
|
* make the default dynamic to begin with because the only way you would ever be static is inside
|
|
* a cache scope which this indication does not affect.
|
|
*
|
|
* The second is an indication that a dynamic data source was read. This is a stronger form of dynamic
|
|
* because it means that it is inappropriate to cache this at all. using a dynamic data source inside
|
|
* unstable_cache should error. If you want to use some dynamic data inside unstable_cache you should
|
|
* read that data outside the cache and pass it in as an argument to the cached function.
|
|
*/ // Once postpone is in stable we should switch to importing the postpone export directly
|
|
"use strict";
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
Postpone: null,
|
|
createPostponedAbortSignal: null,
|
|
createPrerenderState: null,
|
|
formatDynamicAPIAccesses: null,
|
|
markCurrentScopeAsDynamic: null,
|
|
trackDynamicDataAccessed: null,
|
|
trackDynamicFetch: null,
|
|
usedDynamicAPIs: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
Postpone: function() {
|
|
return Postpone;
|
|
},
|
|
createPostponedAbortSignal: function() {
|
|
return createPostponedAbortSignal;
|
|
},
|
|
createPrerenderState: function() {
|
|
return createPrerenderState;
|
|
},
|
|
formatDynamicAPIAccesses: function() {
|
|
return formatDynamicAPIAccesses;
|
|
},
|
|
markCurrentScopeAsDynamic: function() {
|
|
return markCurrentScopeAsDynamic;
|
|
},
|
|
trackDynamicDataAccessed: function() {
|
|
return trackDynamicDataAccessed;
|
|
},
|
|
trackDynamicFetch: function() {
|
|
return trackDynamicFetch;
|
|
},
|
|
usedDynamicAPIs: function() {
|
|
return usedDynamicAPIs;
|
|
}
|
|
});
|
|
const _react = /*#__PURE__*/ _interop_require_default(require("react"));
|
|
const _hooksservercontext = require("../../client/components/hooks-server-context");
|
|
const _staticgenerationbailout = require("../../client/components/static-generation-bailout");
|
|
const _url = require("../../lib/url");
|
|
function _interop_require_default(obj) {
|
|
return obj && obj.__esModule ? obj : {
|
|
default: obj
|
|
};
|
|
}
|
|
const hasPostpone = typeof _react.default.unstable_postpone === "function";
|
|
function createPrerenderState(isDebugSkeleton) {
|
|
return {
|
|
isDebugSkeleton,
|
|
dynamicAccesses: []
|
|
};
|
|
}
|
|
function markCurrentScopeAsDynamic(store, expression) {
|
|
const pathname = (0, _url.getPathname)(store.urlPathname);
|
|
if (store.isUnstableCacheCallback) {
|
|
// inside cache scopes marking a scope as dynamic has no effect because the outer cache scope
|
|
// creates a cache boundary. This is subtly different from reading a dynamic data source which is
|
|
// forbidden inside a cache scope.
|
|
return;
|
|
} else if (store.dynamicShouldError) {
|
|
throw new _staticgenerationbailout.StaticGenBailoutError(`Route ${pathname} with \`dynamic = "error"\` couldn't be rendered statically because it used \`${expression}\`. See more info here: https://nextjs.org/docs/app/building-your-application/rendering/static-and-dynamic#dynamic-rendering`);
|
|
} else if (// We are in a prerender (PPR enabled, during build)
|
|
store.prerenderState) {
|
|
// We track that we had a dynamic scope that postponed.
|
|
// This will be used by the renderer to decide whether
|
|
// the prerender requires a resume
|
|
postponeWithTracking(store.prerenderState, expression, pathname);
|
|
} else {
|
|
store.revalidate = 0;
|
|
if (store.isStaticGeneration) {
|
|
// We aren't prerendering but we are generating a static page. We need to bail out of static generation
|
|
const err = new _hooksservercontext.DynamicServerError(`Route ${pathname} couldn't be rendered statically because it used ${expression}. See more info here: https://nextjs.org/docs/messages/dynamic-server-error`);
|
|
store.dynamicUsageDescription = expression;
|
|
store.dynamicUsageStack = err.stack;
|
|
throw err;
|
|
}
|
|
}
|
|
}
|
|
function trackDynamicDataAccessed(store, expression) {
|
|
const pathname = (0, _url.getPathname)(store.urlPathname);
|
|
if (store.isUnstableCacheCallback) {
|
|
throw new Error(`Route ${pathname} used "${expression}" inside a function cached with "unstable_cache(...)". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use "${expression}" outside of the cached function and pass the required dynamic data in as an argument. See more info here: https://nextjs.org/docs/app/api-reference/functions/unstable_cache`);
|
|
} else if (store.dynamicShouldError) {
|
|
throw new _staticgenerationbailout.StaticGenBailoutError(`Route ${pathname} with \`dynamic = "error"\` couldn't be rendered statically because it used \`${expression}\`. See more info here: https://nextjs.org/docs/app/building-your-application/rendering/static-and-dynamic#dynamic-rendering`);
|
|
} else if (// We are in a prerender (PPR enabled, during build)
|
|
store.prerenderState) {
|
|
// We track that we had a dynamic scope that postponed.
|
|
// This will be used by the renderer to decide whether
|
|
// the prerender requires a resume
|
|
postponeWithTracking(store.prerenderState, expression, pathname);
|
|
} else {
|
|
store.revalidate = 0;
|
|
if (store.isStaticGeneration) {
|
|
// We aren't prerendering but we are generating a static page. We need to bail out of static generation
|
|
const err = new _hooksservercontext.DynamicServerError(`Route ${pathname} couldn't be rendered statically because it used \`${expression}\`. See more info here: https://nextjs.org/docs/messages/dynamic-server-error`);
|
|
store.dynamicUsageDescription = expression;
|
|
store.dynamicUsageStack = err.stack;
|
|
throw err;
|
|
}
|
|
}
|
|
}
|
|
function Postpone({ reason, prerenderState, pathname }) {
|
|
postponeWithTracking(prerenderState, reason, pathname);
|
|
}
|
|
function trackDynamicFetch(store, expression) {
|
|
if (store.prerenderState) {
|
|
postponeWithTracking(store.prerenderState, expression, store.urlPathname);
|
|
}
|
|
}
|
|
function postponeWithTracking(prerenderState, expression, pathname) {
|
|
assertPostpone();
|
|
const reason = `Route ${pathname} needs to bail out of prerendering at this point because it used ${expression}. ` + `React throws this special object to indicate where. It should not be caught by ` + `your own try/catch. Learn more: https://nextjs.org/docs/messages/ppr-caught-error`;
|
|
prerenderState.dynamicAccesses.push({
|
|
// When we aren't debugging, we don't need to create another error for the
|
|
// stack trace.
|
|
stack: prerenderState.isDebugSkeleton ? new Error().stack : undefined,
|
|
expression
|
|
});
|
|
_react.default.unstable_postpone(reason);
|
|
}
|
|
function usedDynamicAPIs(prerenderState) {
|
|
return prerenderState.dynamicAccesses.length > 0;
|
|
}
|
|
function formatDynamicAPIAccesses(prerenderState) {
|
|
return prerenderState.dynamicAccesses.filter((access)=>typeof access.stack === "string" && access.stack.length > 0).map(({ expression, stack })=>{
|
|
stack = stack.split("\n")// Remove the "Error: " prefix from the first line of the stack trace as
|
|
// well as the first 4 lines of the stack trace which is the distance
|
|
// from the user code and the `new Error().stack` call.
|
|
.slice(4).filter((line)=>{
|
|
// Exclude Next.js internals from the stack trace.
|
|
if (line.includes("node_modules/next/")) {
|
|
return false;
|
|
}
|
|
// Exclude anonymous functions from the stack trace.
|
|
if (line.includes(" (<anonymous>)")) {
|
|
return false;
|
|
}
|
|
// Exclude Node.js internals from the stack trace.
|
|
if (line.includes(" (node:")) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}).join("\n");
|
|
return `Dynamic API Usage Debug - ${expression}:\n${stack}`;
|
|
});
|
|
}
|
|
function assertPostpone() {
|
|
if (!hasPostpone) {
|
|
throw new Error(`Invariant: React.unstable_postpone is not defined. This suggests the wrong version of React was loaded. This is a bug in Next.js`);
|
|
}
|
|
}
|
|
function createPostponedAbortSignal(reason) {
|
|
assertPostpone();
|
|
const controller = new AbortController();
|
|
// We get our hands on a postpone instance by calling postpone and catching the throw
|
|
try {
|
|
_react.default.unstable_postpone(reason);
|
|
} catch (x) {
|
|
controller.abort(x);
|
|
}
|
|
return controller.signal;
|
|
}
|
|
|
|
//# sourceMappingURL=dynamic-rendering.js.map
|