@@ -1248,110 +1248,14 @@ export function cache(
12481248 if ( cachedEntry !== undefined ) {
12491249 const existingEntry = await cachedEntry
12501250 if ( workUnitStore !== undefined && existingEntry !== undefined ) {
1251- if (
1252- existingEntry . revalidate === 0 ||
1253- existingEntry . expire < DYNAMIC_EXPIRE
1254- ) {
1255- switch ( workUnitStore . type ) {
1256- case 'prerender' :
1257- // In a Dynamic I/O prerender, if the cache entry has
1258- // revalidate: 0 or if the expire time is under 5 minutes,
1259- // then we consider this cache entry dynamic as it's not worth
1260- // generating static pages for such data. It's better to leave
1261- // a dynamic hole that can be filled in during the resume with
1262- // a potentially cached entry.
1263- if ( cacheSignal ) {
1264- cacheSignal . endRead ( )
1265- }
1266- return makeHangingPromise (
1267- workUnitStore . renderSignal ,
1268- workStore . route ,
1269- 'dynamic "use cache"'
1270- )
1271- case 'prerender-runtime' : {
1272- // In the final phase of a runtime prerender, we have to make
1273- // sure that APIs that would hang during a static prerender
1274- // are resolved with a delay, in the runtime stage.
1275- if ( workUnitStore . runtimeStagePromise ) {
1276- await workUnitStore . runtimeStagePromise
1277- }
1278- break
1279- }
1280- case 'request' : {
1281- if ( process . env . NODE_ENV === 'development' ) {
1282- // We delay the cache here so that it doesn't resolve in the static task --
1283- // in a regular static prerender, it'd be a hanging promise, and we need to reflect that,
1284- // so it has to resolve later.
1285- // TODO(restart-on-cache-miss): This can fallthrough to the `RUNTIME_PREFETCH_DYNAMIC_STALE`
1286- // check below, and try to delay again. This is not incorrect, but it's unnecessary.
1287- // refactor this to avoid it.
1288- const hang = await delayOrHangStartedCacheReadInDev (
1289- RenderStage . Runtime ,
1290- workUnitStore ,
1291- cacheSignal ,
1292- workStore . route ,
1293- 'dynamic "use cache"'
1294- )
1295- if ( hang ) {
1296- return hang . hangingPromise
1297- }
1298- }
1299- break
1300- }
1301- case 'prerender-ppr' :
1302- case 'prerender-legacy' :
1303- case 'cache' :
1304- case 'private-cache' :
1305- case 'unstable-cache' :
1306- break
1307- default :
1308- workUnitStore satisfies never
1309- }
1310- }
1311-
1312- if ( existingEntry . stale < RUNTIME_PREFETCH_DYNAMIC_STALE ) {
1313- switch ( workUnitStore . type ) {
1314- case 'prerender-runtime' :
1315- // In a runtime prerender, if the cache entry will become
1316- // stale in less then 30 seconds, we consider this cache entry
1317- // dynamic as it's not worth prefetching. It's better to leave
1318- // a dynamic hole that can be filled during the navigation.
1319- if ( cacheSignal ) {
1320- cacheSignal . endRead ( )
1321- }
1322- return makeHangingPromise (
1323- workUnitStore . renderSignal ,
1324- workStore . route ,
1325- 'dynamic "use cache"'
1326- )
1327- case 'request' : {
1328- if ( process . env . NODE_ENV === 'development' ) {
1329- // We delay the cache here so that it doesn't resolve in the runtime phase --
1330- // in a regular runtime prerender, it'd be a hanging promise, and we need to reflect that,
1331- // so it has to resolve later.
1332- const hang = await delayOrHangStartedCacheReadInDev (
1333- RenderStage . Dynamic ,
1334- workUnitStore ,
1335- cacheSignal ,
1336- workStore . route ,
1337- 'dynamic "use cache"'
1338- )
1339- if ( hang ) {
1340- return hang . hangingPromise
1341- }
1342- }
1343- break
1344- }
1345- case 'prerender' :
1346- case 'prerender-ppr' :
1347- case 'prerender-legacy' :
1348- case 'cache' :
1349- case 'private-cache' :
1350- case 'unstable-cache' :
1351- break
1352- default :
1353- workUnitStore satisfies never
1354- }
1251+ const hang = await delayOrHangStartedCacheReadIfTooDynamic (
1252+ workStore ,
1253+ workUnitStore ,
1254+ existingEntry ,
1255+ cacheSignal
1256+ )
1257+ if ( hang ) {
1258+ return hang . hangingPromise
13551259 }
13561260 }
13571261
@@ -1479,54 +1383,15 @@ export function cache(
14791383 }
14801384
14811385 const currentTime = performance . timeOrigin + performance . now ( )
1482- if (
1483- workUnitStore !== undefined &&
1484- entry !== undefined &&
1485- ( entry . revalidate === 0 || entry . expire < DYNAMIC_EXPIRE )
1486- ) {
1487- switch ( workUnitStore . type ) {
1488- case 'prerender' :
1489- // In a Dynamic I/O prerender, if the cache entry has revalidate:
1490- // 0 or if the expire time is under 5 minutes, then we consider
1491- // this cache entry dynamic as it's not worth generating static
1492- // pages for such data. It's better to leave a dynamic hole that
1493- // can be filled in during the resume with a potentially cached
1494- // entry.
1495- if ( cacheSignal ) {
1496- cacheSignal . endRead ( )
1497- }
1498- return makeHangingPromise (
1499- workUnitStore . renderSignal ,
1500- workStore . route ,
1501- 'dynamic "use cache"'
1502- )
1503- case 'request' : {
1504- if ( process . env . NODE_ENV === 'development' ) {
1505- // We delay the cache here so that it doesn't resolve in the static task --
1506- // in a regular static prerender, it'd be a hanging promise, and we need to reflect that,
1507- // so it has to resolve later.
1508- const hang = await delayOrHangStartedCacheReadInDev (
1509- RenderStage . Dynamic ,
1510- workUnitStore ,
1511- cacheSignal ,
1512- workStore . route ,
1513- 'dynamic "use cache"'
1514- )
1515- if ( hang ) {
1516- return hang . hangingPromise
1517- }
1518- }
1519- break
1520- }
1521- case 'prerender-runtime' :
1522- case 'prerender-ppr' :
1523- case 'prerender-legacy' :
1524- case 'cache' :
1525- case 'private-cache' :
1526- case 'unstable-cache' :
1527- break
1528- default :
1529- workUnitStore satisfies never
1386+ if ( workUnitStore !== undefined && entry !== undefined ) {
1387+ const hang = await delayOrHangStartedCacheReadIfTooDynamic (
1388+ workStore ,
1389+ workUnitStore ,
1390+ entry ,
1391+ cacheSignal
1392+ )
1393+ if ( hang ) {
1394+ return hang . hangingPromise
15301395 }
15311396 }
15321397
@@ -1717,6 +1582,127 @@ export function cache(
17171582 return React . cache ( cachedFn )
17181583}
17191584
1585+ /**
1586+ * If we're in a prerender-like environment, caches that would expire
1587+ * or go stale too quickly should be omitted from the rendered result.
1588+ * In multi-stage renders, they should be delayed to the appropriate stage.
1589+ */
1590+ async function delayOrHangStartedCacheReadIfTooDynamic (
1591+ workStore : WorkStore ,
1592+ workUnitStore : WorkUnitStore ,
1593+ entry : CacheEntry ,
1594+ cacheSignal : CacheSignal | null
1595+ ) : Promise < { hangingPromise : Promise < never > } | null > {
1596+ if ( entry . revalidate === 0 || entry . expire < DYNAMIC_EXPIRE ) {
1597+ switch ( workUnitStore . type ) {
1598+ case 'prerender' :
1599+ // In a Dynamic I/O prerender, if the cache entry has
1600+ // revalidate: 0 or if the expire time is under 5 minutes,
1601+ // then we consider this cache entry dynamic as it's not worth
1602+ // generating static pages for such data. It's better to leave
1603+ // a dynamic hole that can be filled in during the resume with
1604+ // a potentially cached entry.
1605+ if ( cacheSignal ) {
1606+ cacheSignal . endRead ( )
1607+ }
1608+ const hangingPromise = makeHangingPromise < never > (
1609+ workUnitStore . renderSignal ,
1610+ workStore . route ,
1611+ 'dynamic "use cache"'
1612+ )
1613+ return { hangingPromise }
1614+ case 'prerender-runtime' : {
1615+ // In the final phase of a runtime prerender, we have to make
1616+ // sure that APIs that would hang during a static prerender
1617+ // are resolved with a delay, in the runtime stage.
1618+ if ( workUnitStore . runtimeStagePromise ) {
1619+ await workUnitStore . runtimeStagePromise
1620+ }
1621+ break
1622+ }
1623+ case 'request' : {
1624+ if ( process . env . NODE_ENV === 'development' ) {
1625+ // We delay the cache here so that it doesn't resolve in the static task --
1626+ // in a regular static prerender, it'd be a hanging promise, and we need to reflect that,
1627+ // so it has to resolve later.
1628+ // TODO(restart-on-cache-miss): This can fallthrough to the `RUNTIME_PREFETCH_DYNAMIC_STALE`
1629+ // check below, and try to delay again. This is not incorrect, but it's unnecessary.
1630+ // refactor this to avoid it.
1631+ const hang = await delayOrHangStartedCacheReadInDev (
1632+ RenderStage . Runtime ,
1633+ workUnitStore ,
1634+ cacheSignal ,
1635+ workStore . route ,
1636+ 'dynamic "use cache"'
1637+ )
1638+ if ( hang ) {
1639+ return hang
1640+ }
1641+ }
1642+ break
1643+ }
1644+ case 'prerender-client' :
1645+ case 'prerender-ppr' :
1646+ case 'prerender-legacy' :
1647+ case 'cache' :
1648+ case 'private-cache' :
1649+ case 'unstable-cache' :
1650+ break
1651+ default :
1652+ workUnitStore satisfies never
1653+ }
1654+ }
1655+
1656+ if ( entry . stale < RUNTIME_PREFETCH_DYNAMIC_STALE ) {
1657+ switch ( workUnitStore . type ) {
1658+ case 'prerender-runtime' :
1659+ // In a runtime prerender, if the cache entry will become
1660+ // stale in less then 30 seconds, we consider this cache entry
1661+ // dynamic as it's not worth prefetching. It's better to leave
1662+ // a dynamic hole that can be filled during the navigation.
1663+ if ( cacheSignal ) {
1664+ cacheSignal . endRead ( )
1665+ }
1666+ const hangingPromise = makeHangingPromise < never > (
1667+ workUnitStore . renderSignal ,
1668+ workStore . route ,
1669+ 'dynamic "use cache"'
1670+ )
1671+ return { hangingPromise }
1672+ case 'request' : {
1673+ if ( process . env . NODE_ENV === 'development' ) {
1674+ // We delay the cache here so that it doesn't resolve in the runtime phase --
1675+ // in a regular runtime prerender, it'd be a hanging promise, and we need to reflect that,
1676+ // so it has to resolve later.
1677+ const hang = await delayOrHangStartedCacheReadInDev (
1678+ RenderStage . Dynamic ,
1679+ workUnitStore ,
1680+ cacheSignal ,
1681+ workStore . route ,
1682+ 'dynamic "use cache"'
1683+ )
1684+ if ( hang ) {
1685+ return hang
1686+ }
1687+ }
1688+ break
1689+ }
1690+ case 'prerender' :
1691+ case 'prerender-client' :
1692+ case 'prerender-ppr' :
1693+ case 'prerender-legacy' :
1694+ case 'cache' :
1695+ case 'private-cache' :
1696+ case 'unstable-cache' :
1697+ break
1698+ default :
1699+ workUnitStore satisfies never
1700+ }
1701+ }
1702+
1703+ return null
1704+ }
1705+
17201706/**
17211707 * Returns `true` if the `'use cache'` function is the page component itself,
17221708 * or `generateMetadata`/`generateViewport` in a page file.
0 commit comments