next
Version:
The React Framework
197 lines (196 loc) • 9.21 kB
JavaScript
import { parseLoaderTree } from '../../../shared/lib/router/utils/parse-loader-tree';
import { getLayoutOrPageModule } from '../../lib/app-dir-module';
/**
* The outcome of resolving a route's `ensureStatic` config.
* The ordering is intentional -- higher values indicate a
* more constrained route that forces more request kinds to be static.
*
* A constraint also implies all its predecessors:
* - `Prefetch` implies `Shell`, i.e. if prefetches are static, so are shells.
* - `Navigation` implies `Prefetch` and `Shell`, i.e. if the whole page is static,
* then so are its shells and prefetches.
*
* Note that this flattens `"auto"` and `false` into `None`.
* This is because rendering code does not need to distinguish them --
* both indicate that the route follows normal Partial Prefetching semantics
* and does not force anything to be static.
* However, it is only correct to collapse them into one *after* resolving the
* config for the entire route, because they have different interactions with
* configs from other segments on the same route.
*/ export var EnsureStaticLevel = /*#__PURE__*/ function(EnsureStaticLevel) {
/** The route follows standard Partial Prefetching semantics. */ EnsureStaticLevel[EnsureStaticLevel["None"] = 0] = "None";
/** The route requires that app shells must be statically prerendered. */ EnsureStaticLevel[EnsureStaticLevel["Shell"] = 1] = "Shell";
/** The route requires that app shells and prefetches must be statically prerendered. */ EnsureStaticLevel[EnsureStaticLevel["Prefetch"] = 2] = "Prefetch";
/** The route requires that app shells, prefetches, and the whole page must be statically prerendered. */ EnsureStaticLevel[EnsureStaticLevel["Navigation"] = 3] = "Navigation";
return EnsureStaticLevel;
}({});
function getEnsureStaticLevel(ensureStatic) {
switch(ensureStatic){
case 'auto':
case false:
{
return 0;
}
case 'shell':
{
return 1;
}
case 'prefetch':
{
return 2;
}
case 'navigation':
{
return 3;
}
}
}
export async function resolveEnsureStaticLevel(tree, partialPrefetching) {
return getEnsureStaticLevel(await resolveEnsureStaticConfig(tree, partialPrefetching));
}
export async function resolveEnsureStaticConfig(tree, partialPrefetching) {
let { config, filePath } = await resolveEnsureStaticConfigImpl(tree);
if (!partialPrefetching) {
// If Partial Prefetching is not enabled for this route, we only support
// a subset of `ensureStatic` values.
// (We error in `getAppPageStaticInfo` if `ensureStatic` is used without enabling
// Cache Components, so we don't have to assert that here)
switch(config){
case 'auto':
{
// Default to the equivalent of Cache Components prefetching behavior.
config = 'prefetch';
break;
}
case false:
case 'shell':
{
// Not meaningful in Cache Components without Partial Prefetching
const originalConfig = config;
config = 'prefetch';
console.warn(`${formatEnsureStaticExport(originalConfig)} has no effect unless the route is using Partial Prefetching.` + `\n (from: ${filePath})`);
break;
}
case 'prefetch':
break;
case 'navigation':
break;
default:
config;
}
}
if (config === false) {
// `false` and "auto" are equivalent once resolved.
return 'auto';
} else {
return config;
}
}
async function resolveEnsureStaticConfigImpl(tree) {
const { mod: layoutOrPageMod, filePath } = await getLayoutOrPageModule(tree);
const config = getEnsureStaticConfigForModule(layoutOrPageMod);
const parentResult = {
config,
filePath: filePath ?? null
};
// Walk the slots if any and validate that they don't have incompatible configs
// with each other. If compatible, pick the most constrained value from the slots.
let slotsResult = null;
let slotResultKey = null;
const { parallelRoutes } = parseLoaderTree(tree);
for(const parallelRouteKey in parallelRoutes){
const parallelRoute = parallelRoutes[parallelRouteKey];
const childResult = await resolveEnsureStaticConfigImpl(parallelRoute);
if (!slotsResult) {
slotsResult = childResult;
slotResultKey = parallelRouteKey;
} else {
// Check if the child is compatible with the current result for the slots.
switch(compareEnsureStatic(childResult.config, slotsResult.config)){
case 2:
{
// 'auto' is compatible with anything, but if the current result is 'auto' and the new one isn't,
// we want to use the more specific result.
if (slotsResult.config === 'auto' && childResult.config !== 'auto') {
slotsResult = childResult;
slotResultKey = parallelRouteKey;
}
break;
}
case 4:
case 1:
case 3:
{
throw new Error(`Parallel slots cannot have incompatible \`ensureStatic\`.` + `\n ${formatParallelSlot(slotResultKey)}: ` + `\n ${formatEnsureStaticExport(slotsResult.config)}` + `\n (from: ${slotsResult.filePath})` + `\n` + `\n ${formatParallelSlot(parallelRouteKey)}: ` + `\n ${formatEnsureStaticExport(childResult.config)}` + `\n (from: ${childResult.filePath})` + `\n` + `\n Possible fixes:` + `\n - Remove one of the \`ensureStatic\` exports` + `\n - Change one of the \`ensureStatic\` exports to match the other`);
}
}
}
}
// Child segments can override the config from the parent with a more constrained value,
// but they cannot have a less constrained value.
if (!slotsResult) {
return parentResult;
} else {
const comparison = compareEnsureStatic(slotsResult.config, parentResult.config);
switch(comparison){
case 2:
{
// 'auto' is compatible with anything, but if the parent result is 'auto' and the slots one isn't,
// we want to use the more constrained result.
if (parentResult.config === 'auto' && slotsResult.config !== 'auto') {
return slotsResult;
} else {
return parentResult;
}
}
case 3:
{
return slotsResult;
}
case 1:
case 4:
{
throw new Error((comparison === 1 ? `A child segment cannot override a parent segment with a less-constrained \`ensureStatic\`.` : `A child segment cannot override a parent segment with an incompatible \`ensureStatic\`.`) + `\n Parent has: ` + `\n ${formatEnsureStaticExport(parentResult.config)}` + `\n (from: ${parentResult.filePath})` + `\n Child has: ` + `\n ${formatEnsureStaticExport(slotsResult.config)}` + `\n (from: ${slotsResult.filePath})` + `\n` + `\n Possible fixes:` + `\n - Remove one of the \`ensureStatic\` exports` + `\n - Change one of the \`ensureStatic\` exports to match the other`);
}
}
}
}
function formatParallelSlot(slot) {
return slot === 'children' ? slot : `@${slot}`;
}
function formatEnsureStaticExport(config) {
return `export const ensureStatic = ${JSON.stringify(config)}`;
}
var Comparison = /*#__PURE__*/ function(Comparison) {
Comparison[Comparison["LessConstrained"] = 1] = "LessConstrained";
Comparison[Comparison["Compatible"] = 2] = "Compatible";
Comparison[Comparison["MoreConstrained"] = 3] = "MoreConstrained";
Comparison[Comparison["Incompatible"] = 4] = "Incompatible";
return Comparison;
}(Comparison || {});
const ENSURE_STATIC_ORDER = [
'shell',
'prefetch',
'navigation'
];
function compareEnsureStatic(left, right) {
// 'auto' is compatible with everything.
if (left === 'auto' || right === 'auto') {
return 2;
}
// `false` is compatible with false.
if (left === false && right === false) {
return 2;
}
// If only one of the values is `false` (we know it's not both), it's incompatible.
if (left === false || right === false) {
return 4;
}
const leftSort = ENSURE_STATIC_ORDER.indexOf(left);
const rightSort = ENSURE_STATIC_ORDER.indexOf(right);
return leftSort < rightSort ? 1 : leftSort === rightSort ? 2 : 3;
}
function getEnsureStaticConfigForModule(mod) {
return (mod ? mod.ensureStatic : undefined) ?? 'auto';
}
//# sourceMappingURL=ensure-static.js.map