Skip to content

Commit f8f24aa

Browse files
authored
Plumbing for cache indicator (#84955)
1 parent 553f2b6 commit f8f24aa

File tree

24 files changed

+245
-14
lines changed

24 files changed

+245
-14
lines changed

packages/next/src/build/templates/app-page.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,7 @@ export async function handler(
503503
subresourceIntegrityManifest,
504504
serverActionsManifest,
505505
clientReferenceManifest,
506+
setCacheStatus: routerServerContext?.setCacheStatus,
506507
setIsrStatus: routerServerContext?.setIsrStatus,
507508
setReactDebugChannel: routerServerContext?.setReactDebugChannel,
508509

packages/next/src/client/app-next-dev.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ appBootstrap((assetPrefix) => {
1515
try {
1616
hydrate(instrumentationHooks, assetPrefix)
1717
} finally {
18-
renderAppDevOverlay(getOwnerStack, isRecoverableError)
18+
const enableCacheIndicator = process.env.__NEXT_CACHE_COMPONENTS
19+
renderAppDevOverlay(getOwnerStack, isRecoverableError, enableCacheIndicator)
1920
}
2021
})

packages/next/src/client/app-next-turbopack.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,16 @@ appBootstrap((assetPrefix) => {
1313
hydrate(instrumentationHooks, assetPrefix)
1414
} finally {
1515
if (process.env.NODE_ENV !== 'production') {
16+
const enableCacheIndicator = process.env.__NEXT_CACHE_COMPONENTS
1617
const { getOwnerStack } =
1718
require('../next-devtools/userspace/app/errors/stitched-error') as typeof import('../next-devtools/userspace/app/errors/stitched-error')
1819
const { renderAppDevOverlay } =
1920
require('next/dist/compiled/next-devtools') as typeof import('next/dist/compiled/next-devtools')
20-
renderAppDevOverlay(getOwnerStack, isRecoverableError)
21+
renderAppDevOverlay(
22+
getOwnerStack,
23+
isRecoverableError,
24+
enableCacheIndicator
25+
)
2126
}
2227
}
2328
})

packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,10 @@ export function processMessage(
500500
sendMessage(JSON.stringify(response))
501501
return
502502
}
503+
case HMR_MESSAGE_SENT_TO_BROWSER.CACHE_INDICATOR: {
504+
dispatcher.onCacheIndicator(message.state)
505+
return
506+
}
503507
case HMR_MESSAGE_SENT_TO_BROWSER.MIDDLEWARE_CHANGES:
504508
case HMR_MESSAGE_SENT_TO_BROWSER.CLIENT_CHANGES:
505509
case HMR_MESSAGE_SENT_TO_BROWSER.SERVER_ONLY_CHANGES:

packages/next/src/client/dev/hot-reloader/pages/hot-reloader-pages.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,7 @@ function processMessage(message: HmrMessageSentToBrowser) {
392392
case HMR_MESSAGE_SENT_TO_BROWSER.DEVTOOLS_CONFIG:
393393
dispatcher.onDevToolsConfig(message.data)
394394
break
395+
case HMR_MESSAGE_SENT_TO_BROWSER.CACHE_INDICATOR:
395396
case HMR_MESSAGE_SENT_TO_BROWSER.REACT_DEBUG_CHUNK:
396397
// Only relevant for app router.
397398
break

packages/next/src/client/page-bootstrap.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ export function pageBootstrap(assetPrefix: string) {
123123
case HMR_MESSAGE_SENT_TO_BROWSER.REACT_DEBUG_CHUNK:
124124
case HMR_MESSAGE_SENT_TO_BROWSER.REQUEST_CURRENT_ERROR_STATE:
125125
case HMR_MESSAGE_SENT_TO_BROWSER.REQUEST_PAGE_METADATA:
126+
case HMR_MESSAGE_SENT_TO_BROWSER.CACHE_INDICATOR:
126127
// Most of these action types are handled in
127128
// src/client/dev/hot-reloader/pages/hot-reloader-pages.ts and
128129
// src/client/dev/hot-reloader/app/hot-reloader-app.tsx

packages/next/src/next-devtools/dev-overlay.browser.tsx

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
ACTION_DEVTOOLS_CONFIG,
2222
type OverlayState,
2323
type DispatcherEvent,
24+
ACTION_CACHE_INDICATOR,
2425
} from './dev-overlay/shared'
2526

2627
import {
@@ -33,6 +34,7 @@ import {
3334
type ActionDispatch,
3435
} from 'react'
3536
import { createRoot } from 'react-dom/client'
37+
import type { CacheIndicatorState } from './dev-overlay/cache-indicator'
3638
import { FontStyles } from './dev-overlay/font/font-styles'
3739
import type { HydrationErrorState } from './shared/hydration-error'
3840
import type { DebugInfo } from './shared/types'
@@ -55,6 +57,7 @@ export interface Dispatcher {
5557
onDebugInfo(debugInfo: DebugInfo): void
5658
onBeforeRefresh(): void
5759
onRefresh(): void
60+
onCacheIndicator(status: CacheIndicatorState): void
5861
onStaticIndicator(status: 'pending' | 'static' | 'dynamic' | 'disabled'): void
5962
onDevIndicator(devIndicator: DevIndicatorServerState): void
6063
onDevToolsConfig(config: DevToolsConfig): void
@@ -147,6 +150,11 @@ export const dispatcher: Dispatcher = {
147150
dispatch({ type: ACTION_VERSION_INFO, versionInfo })
148151
}
149152
),
153+
onCacheIndicator: createQueuable(
154+
(dispatch: Dispatch, status: CacheIndicatorState) => {
155+
dispatch({ type: ACTION_CACHE_INDICATOR, cacheIndicator: status })
156+
}
157+
),
150158
onStaticIndicator: createQueuable(
151159
(
152160
dispatch: Dispatch,
@@ -230,12 +238,14 @@ function replayQueuedEvents(dispatch: NonNullable<typeof maybeDispatch>) {
230238
}
231239

232240
function DevOverlayRoot({
241+
enableCacheIndicator,
233242
getOwnerStack,
234243
getSquashedHydrationErrorDetails,
235244
isRecoverableError,
236245
routerType,
237246
shadowRoot,
238247
}: {
248+
enableCacheIndicator: boolean
239249
getOwnerStack: (error: Error) => string | null | undefined
240250
getSquashedHydrationErrorDetails: (error: Error) => HydrationErrorState | null
241251
isRecoverableError: (error: Error) => boolean
@@ -245,7 +255,8 @@ function DevOverlayRoot({
245255
const [state, dispatch] = useErrorOverlayReducer(
246256
routerType,
247257
getOwnerStack,
248-
isRecoverableError
258+
isRecoverableError,
259+
enableCacheIndicator
249260
)
250261

251262
useEffect(() => {
@@ -319,7 +330,8 @@ function getSquashedHydrationErrorDetailsApp() {
319330

320331
export function renderAppDevOverlay(
321332
getOwnerStack: (error: Error) => string | null | undefined,
322-
isRecoverableError: (error: Error) => boolean
333+
isRecoverableError: (error: Error) => boolean,
334+
enableCacheIndicator: boolean
323335
): void {
324336
if (isPagesMounted) {
325337
// Switching between App and Pages Router is always a hard navigation
@@ -361,6 +373,7 @@ export function renderAppDevOverlay(
361373
// At least it won't unmount any user code if it errors.
362374
root.render(
363375
<DevOverlayRoot
376+
enableCacheIndicator={enableCacheIndicator}
364377
getOwnerStack={getOwnerStack}
365378
getSquashedHydrationErrorDetails={getSquashedHydrationErrorDetailsApp}
366379
isRecoverableError={isRecoverableError}
@@ -426,6 +439,8 @@ export function renderPagesDevOverlay(
426439
// At least it won't unmount any user code if it errors.
427440
root.render(
428441
<DevOverlayRoot
442+
// Pages Router does not support Cache Components
443+
enableCacheIndicator={false}
429444
getOwnerStack={getOwnerStack}
430445
getSquashedHydrationErrorDetails={getSquashedHydrationErrorDetails}
431446
isRecoverableError={isRecoverableError}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export type ServerCacheStatus = 'filling' | 'filled'
2+
export type CacheIndicatorState = 'disabled' | 'ready' | ServerCacheStatus

packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/devtools-indicator.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ export function DevToolsIndicator() {
3131
<Toast
3232
id="devtools-indicator"
3333
data-nextjs-toast
34+
// TODO: Remove once we have actual UI for this.
35+
data-nextjs-cache-indicator={
36+
state.cacheIndicator === 'disabled' ? undefined : state.cacheIndicator
37+
}
3438
style={
3539
{
3640
'--animate-out-duration-ms': `${MENU_DURATION_MS}ms`,

packages/next/src/next-devtools/dev-overlay/shared.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { DebugInfo } from '../shared/types'
66
import type { DevIndicatorServerState } from '../../server/dev/dev-indicator-server-state'
77
import { parseStack } from '../../server/lib/parse-stack'
88
import { isConsoleError } from '../shared/console-error'
9+
import type { CacheIndicatorState } from './cache-indicator'
910

1011
export type DevToolsConfig = {
1112
theme?: 'light' | 'dark' | 'system'
@@ -49,6 +50,7 @@ export interface OverlayState {
4950
readonly notFound: boolean
5051
readonly buildingIndicator: boolean
5152
readonly renderingIndicator: boolean
53+
readonly cacheIndicator: CacheIndicatorState
5254
readonly staticIndicator: 'pending' | 'static' | 'dynamic' | 'disabled'
5355
readonly showIndicator: boolean
5456
readonly disableDevIndicator: boolean
@@ -72,6 +74,7 @@ export interface OverlayState {
7274
type DevtoolsPanelName = string
7375
export type OverlayDispatch = React.Dispatch<DispatcherEvent>
7476

77+
export const ACTION_CACHE_INDICATOR = 'cache-indicator'
7578
export const ACTION_STATIC_INDICATOR = 'static-indicator'
7679
export const ACTION_BUILD_OK = 'build-ok'
7780
export const ACTION_BUILD_ERROR = 'build-error'
@@ -110,6 +113,11 @@ export const STORE_KEY_SHARED_PANEL_LOCATION =
110113
export const ACTION_DEVTOOL_UPDATE_ROUTE_STATE =
111114
'segment-explorer-update-route-state'
112115

116+
interface CacheIndicatorAction {
117+
type: typeof ACTION_CACHE_INDICATOR
118+
cacheIndicator: CacheIndicatorState
119+
}
120+
113121
interface StaticIndicatorAction {
114122
type: typeof ACTION_STATIC_INDICATOR
115123
staticIndicator: 'pending' | 'static' | 'dynamic' | 'disabled'
@@ -216,6 +224,7 @@ export type DispatcherEvent =
216224
| UnhandledErrorAction
217225
| UnhandledRejectionAction
218226
| VersionInfoAction
227+
| CacheIndicatorAction
219228
| StaticIndicatorAction
220229
| DebugInfoAction
221230
| DevIndicatorAction
@@ -263,6 +272,7 @@ export const INITIAL_OVERLAY_STATE: Omit<
263272
errors: [],
264273
notFound: false,
265274
renderingIndicator: false,
275+
cacheIndicator: 'disabled',
266276
staticIndicator: 'disabled',
267277
/*
268278
This is set to `true` when we can reliably know
@@ -287,7 +297,8 @@ export const INITIAL_OVERLAY_STATE: Omit<
287297
}
288298

289299
function getInitialState(
290-
routerType: 'pages' | 'app'
300+
routerType: 'pages' | 'app',
301+
enableCacheIndicator: boolean
291302
): OverlayState & { routerType: 'pages' | 'app' } {
292303
return {
293304
...INITIAL_OVERLAY_STATE,
@@ -296,13 +307,15 @@ function getInitialState(
296307
// TODO: Should be the same default as App Router once we surface console.error in Pages Router.
297308
isErrorOverlayOpen: routerType === 'pages',
298309
routerType,
310+
cacheIndicator: enableCacheIndicator ? 'ready' : 'disabled',
299311
}
300312
}
301313

302314
export function useErrorOverlayReducer(
303315
routerType: 'pages' | 'app',
304316
getOwnerStack: (error: Error) => string | null | undefined,
305-
isRecoverableError: (error: Error) => boolean
317+
isRecoverableError: (error: Error) => boolean,
318+
enableCacheIndicator: boolean
306319
) {
307320
function pushErrorFilterDuplicates(
308321
events: readonly SupportedErrorEvent[],
@@ -349,6 +362,9 @@ export function useErrorOverlayReducer(
349362
case ACTION_DEBUG_INFO: {
350363
return { ...state, debugInfo: action.debugInfo }
351364
}
365+
case ACTION_CACHE_INDICATOR: {
366+
return { ...state, cacheIndicator: action.cacheIndicator }
367+
}
352368
case ACTION_STATIC_INDICATOR: {
353369
return { ...state, staticIndicator: action.staticIndicator }
354370
}
@@ -496,6 +512,6 @@ export function useErrorOverlayReducer(
496512
}
497513
}
498514
},
499-
getInitialState(routerType)
515+
getInitialState(routerType, enableCacheIndicator)
500516
)
501517
}

0 commit comments

Comments
 (0)