diff --git a/packages/next/src/client/app-next-dev.ts b/packages/next/src/client/app-next-dev.ts index 9bd9a43c3e4b..27e7b6d7793c 100644 --- a/packages/next/src/client/app-next-dev.ts +++ b/packages/next/src/client/app-next-dev.ts @@ -3,12 +3,10 @@ import './app-webpack' import { appBootstrap } from './app-bootstrap' -import { initializeDevBuildIndicatorForAppRouter } from './dev/dev-build-indicator/initialize-for-app-router' const instrumentationHooks = require('../lib/require-instrumentation-client') appBootstrap(() => { const { hydrate } = require('./app-index') hydrate(instrumentationHooks) - initializeDevBuildIndicatorForAppRouter() }) diff --git a/packages/next/src/client/app-next-turbopack.ts b/packages/next/src/client/app-next-turbopack.ts index cced9a14d0fc..f7bbe4a30abd 100644 --- a/packages/next/src/client/app-next-turbopack.ts +++ b/packages/next/src/client/app-next-turbopack.ts @@ -10,10 +10,4 @@ const instrumentationHooks = require('../lib/require-instrumentation-client') appBootstrap(() => { const { hydrate } = require('./app-index') hydrate(instrumentationHooks) - - if (process.env.NODE_ENV !== 'production') { - const { initializeDevBuildIndicatorForAppRouter } = - require('./dev/dev-build-indicator/initialize-for-app-router') as typeof import('./dev/dev-build-indicator/initialize-for-app-router') - initializeDevBuildIndicatorForAppRouter() - } }) diff --git a/packages/next/src/client/components/react-dev-overlay/app/hot-reloader-client.tsx b/packages/next/src/client/components/react-dev-overlay/app/hot-reloader-client.tsx index ad2778cca125..d45b1a2e3d6b 100644 --- a/packages/next/src/client/components/react-dev-overlay/app/hot-reloader-client.tsx +++ b/packages/next/src/client/components/react-dev-overlay/app/hot-reloader-client.tsx @@ -7,8 +7,10 @@ import formatWebpackMessages from '../utils/format-webpack-messages' import { useRouter } from '../../navigation' import { ACTION_BEFORE_REFRESH, + ACTION_BUILDING_INDICATOR_HIDE, ACTION_BUILD_ERROR, ACTION_BUILD_OK, + ACTION_BUILDING_INDICATOR_SHOW, ACTION_DEBUG_INFO, ACTION_DEV_INDICATOR, ACTION_ERROR_OVERLAY_OPEN, @@ -40,7 +42,6 @@ import type { import { REACT_REFRESH_FULL_RELOAD_FROM_ERROR } from '../shared' import type { DebugInfo } from '../types' import { useUntrackedPathname } from '../../navigation-untracked' -import { handleDevBuildIndicatorHmrEvents } from '../../../dev/dev-build-indicator/internal/handle-dev-build-indicator-hmr-events' import type { GlobalErrorComponent } from '../../global-error' import type { DevIndicatorServerState } from '../../../../server/dev/dev-indicator-server-state' import reportHmrLatency from '../utils/report-hmr-latency' @@ -61,6 +62,8 @@ export interface Dispatcher { onUnhandledError(error: Error): void onUnhandledRejection(error: Error): void openErrorOverlay(): void + buildingIndicatorHide(): void + buildingIndicatorShow(): void } let mostRecentCompilationHash: any = null @@ -302,6 +305,8 @@ function processMessage( break } case HMR_ACTIONS_SENT_TO_BROWSER.BUILDING: { + dispatcher.buildingIndicatorShow() + if (process.env.TURBOPACK) { turbopackHmr!.onBuilding() } else { @@ -313,6 +318,8 @@ function processMessage( } case HMR_ACTIONS_SENT_TO_BROWSER.BUILT: case HMR_ACTIONS_SENT_TO_BROWSER.SYNC: { + dispatcher.buildingIndicatorHide() + if (obj.hash) { handleAvailableHash(obj.hash) } @@ -535,6 +542,12 @@ export default function HotReload({ openErrorOverlay() { dispatch({ type: ACTION_ERROR_OVERLAY_OPEN }) }, + buildingIndicatorHide() { + dispatch({ type: ACTION_BUILDING_INDICATOR_HIDE }) + }, + buildingIndicatorShow() { + dispatch({ type: ACTION_BUILDING_INDICATOR_SHOW }) + }, } }, [dispatch]) @@ -596,7 +609,6 @@ export default function HotReload({ const handler = (event: MessageEvent) => { try { const obj = JSON.parse(event.data) - handleDevBuildIndicatorHmrEvents(obj) processMessage( obj, sendMessage, diff --git a/packages/next/src/client/components/react-dev-overlay/pages/client.ts b/packages/next/src/client/components/react-dev-overlay/pages/client.ts index 5658791e57d0..76fe2e974489 100644 --- a/packages/next/src/client/components/react-dev-overlay/pages/client.ts +++ b/packages/next/src/client/components/react-dev-overlay/pages/client.ts @@ -5,8 +5,10 @@ import { } from './hydration-error-state' import { ACTION_BEFORE_REFRESH, + ACTION_BUILDING_INDICATOR_HIDE, ACTION_BUILD_ERROR, ACTION_BUILD_OK, + ACTION_BUILDING_INDICATOR_SHOW, ACTION_DEV_INDICATOR, ACTION_REFRESH, ACTION_STATIC_INDICATOR, @@ -115,5 +117,13 @@ export function onDevIndicator(devIndicatorsState: DevIndicatorServerState) { Bus.emit({ type: ACTION_DEV_INDICATOR, devIndicator: devIndicatorsState }) } +export function buildingIndicatorShow() { + Bus.emit({ type: ACTION_BUILDING_INDICATOR_SHOW }) +} + +export function buildingIndicatorHide() { + Bus.emit({ type: ACTION_BUILDING_INDICATOR_HIDE }) +} + export { getErrorByType } from '../utils/get-error-by-type' export { getServerError } from '../utils/node-stack-frames' diff --git a/packages/next/src/client/components/react-dev-overlay/pages/hot-reloader-client.ts b/packages/next/src/client/components/react-dev-overlay/pages/hot-reloader-client.ts index dea69706e237..bdd4d257edc4 100644 --- a/packages/next/src/client/components/react-dev-overlay/pages/hot-reloader-client.ts +++ b/packages/next/src/client/components/react-dev-overlay/pages/hot-reloader-client.ts @@ -41,6 +41,8 @@ import { onVersionInfo, onStaticIndicator, onDevIndicator, + buildingIndicatorHide, + buildingIndicatorShow, } from './client' import stripAnsi from 'next/dist/compiled/strip-ansi' import { addMessageListener, sendMessage } from './websocket' @@ -272,6 +274,8 @@ function processMessage(obj: HMR_ACTION_TYPES) { break } case HMR_ACTIONS_SENT_TO_BROWSER.BUILDING: { + buildingIndicatorShow() + if (process.env.TURBOPACK) { turbopackHmr!.onBuilding() } else { @@ -282,6 +286,8 @@ function processMessage(obj: HMR_ACTION_TYPES) { } case HMR_ACTIONS_SENT_TO_BROWSER.BUILT: case HMR_ACTIONS_SENT_TO_BROWSER.SYNC: { + buildingIndicatorHide() + if (obj.hash) handleAvailableHash(obj.hash) const { errors, warnings } = obj diff --git a/packages/next/src/client/components/react-dev-overlay/shared.ts b/packages/next/src/client/components/react-dev-overlay/shared.ts index f03ca92ffe06..486ea269b7db 100644 --- a/packages/next/src/client/components/react-dev-overlay/shared.ts +++ b/packages/next/src/client/components/react-dev-overlay/shared.ts @@ -22,6 +22,7 @@ export interface OverlayState { refreshState: FastRefreshState versionInfo: VersionInfo notFound: boolean + buildingIndicator: boolean staticIndicator: boolean showIndicator: boolean disableDevIndicator: boolean @@ -44,6 +45,8 @@ export const ACTION_DEV_INDICATOR = 'dev-indicator' export const ACTION_ERROR_OVERLAY_OPEN = 'error-overlay-open' export const ACTION_ERROR_OVERLAY_CLOSE = 'error-overlay-close' export const ACTION_ERROR_OVERLAY_TOGGLE = 'error-overlay-toggle' +export const ACTION_BUILDING_INDICATOR_SHOW = 'building-indicator-show' +export const ACTION_BUILDING_INDICATOR_HIDE = 'building-indicator-hide' export const STORAGE_KEY_THEME = '__nextjs-dev-tools-theme' export const STORAGE_KEY_POSITION = '__nextjs-dev-tools-position' @@ -102,6 +105,13 @@ export interface ErrorOverlayToggleAction { type: typeof ACTION_ERROR_OVERLAY_TOGGLE } +export interface BuildingIndicatorShowAction { + type: typeof ACTION_BUILDING_INDICATOR_SHOW +} +export interface BuildingIndicatorHideAction { + type: typeof ACTION_BUILDING_INDICATOR_HIDE +} + export type BusEvent = | BuildOkAction | BuildErrorAction @@ -116,6 +126,8 @@ export type BusEvent = | ErrorOverlayOpenAction | ErrorOverlayCloseAction | ErrorOverlayToggleAction + | BuildingIndicatorShowAction + | BuildingIndicatorHideAction const REACT_ERROR_STACK_BOTTOM_FRAME_REGEX = // 1st group: v8 @@ -149,6 +161,7 @@ export const INITIAL_OVERLAY_STATE: Omit< */ showIndicator: false, disableDevIndicator: false, + buildingIndicator: false, refreshState: { type: 'idle' }, versionInfo: { installed: '0.0.0', staleness: 'unknown' }, debugInfo: { devtoolsFrontendUrl: undefined }, @@ -296,6 +309,12 @@ export function useErrorOverlayReducer( case ACTION_ERROR_OVERLAY_TOGGLE: { return { ...state, isErrorOverlayOpen: !state.isErrorOverlayOpen } } + case ACTION_BUILDING_INDICATOR_SHOW: { + return { ...state, buildingIndicator: true } + } + case ACTION_BUILDING_INDICATOR_HIDE: { + return { ...state, buildingIndicator: false } + } default: { return state } diff --git a/packages/next/src/client/components/react-dev-overlay/ui/components/errors/dev-tools-indicator/dev-tools-indicator.stories.tsx b/packages/next/src/client/components/react-dev-overlay/ui/components/errors/dev-tools-indicator/dev-tools-indicator.stories.tsx index 68ed370f0b8e..2db3adb04dfd 100644 --- a/packages/next/src/client/components/react-dev-overlay/ui/components/errors/dev-tools-indicator/dev-tools-indicator.stories.tsx +++ b/packages/next/src/client/components/react-dev-overlay/ui/components/errors/dev-tools-indicator/dev-tools-indicator.stories.tsx @@ -54,6 +54,7 @@ const state: OverlayState = { showIndicator: true, versionInfo: mockVersionInfo, notFound: false, + buildingIndicator: false, staticIndicator: true, debugInfo: { devtoolsFrontendUrl: undefined }, isErrorOverlayOpen: false, diff --git a/packages/next/src/client/components/react-dev-overlay/ui/components/errors/dev-tools-indicator/dev-tools-indicator.tsx b/packages/next/src/client/components/react-dev-overlay/ui/components/errors/dev-tools-indicator/dev-tools-indicator.tsx index 13856c02a4c9..ab0a4d31868c 100644 --- a/packages/next/src/client/components/react-dev-overlay/ui/components/errors/dev-tools-indicator/dev-tools-indicator.tsx +++ b/packages/next/src/client/components/react-dev-overlay/ui/components/errors/dev-tools-indicator/dev-tools-indicator.tsx @@ -10,7 +10,6 @@ import { import { useState, useEffect, useRef, createContext, useContext } from 'react' import { Toast } from '../../toast' import { NextLogo } from './next-logo' -import { useIsDevBuilding } from '../../../../../../dev/dev-build-indicator/internal/initialize' import { useIsDevRendering } from '../../../../utils/dev-indicator/dev-render-indicator' import { useDelayedRender } from '../../../hooks/use-delayed-render' import { TurbopackInfo } from './dev-tools-info/turbopack-info' @@ -54,6 +53,7 @@ export function DevToolsIndicator({ routerType={state.routerType} semver={state.versionInfo.installed} issueCount={errorCount} + isDevBuilding={state.buildingIndicator} isStaticRoute={state.staticIndicator} hide={() => { setIsDevToolsIndicatorVisible(false) @@ -95,6 +95,7 @@ function DevToolsPopover({ routerType, disabled, issueCount, + isDevBuilding, isStaticRoute, isTurbopack, isBuildError, @@ -108,6 +109,7 @@ function DevToolsPopover({ issueCount: number isStaticRoute: boolean semver: string | undefined + isDevBuilding: boolean isTurbopack: boolean isBuildError: boolean hide: () => void @@ -294,7 +296,7 @@ function DevToolsPopover({ issueCount={issueCount} onTriggerClick={onTriggerClick} toggleErrorOverlay={toggleErrorOverlay} - isDevBuilding={useIsDevBuilding()} + isDevBuilding={isDevBuilding} isDevRendering={useIsDevRendering()} isBuildError={isBuildError} scale={scale} diff --git a/packages/next/src/client/components/react-dev-overlay/ui/dev-overlay.stories.tsx b/packages/next/src/client/components/react-dev-overlay/ui/dev-overlay.stories.tsx index ded4b633acf3..d338cfa76539 100644 --- a/packages/next/src/client/components/react-dev-overlay/ui/dev-overlay.stories.tsx +++ b/packages/next/src/client/components/react-dev-overlay/ui/dev-overlay.stories.tsx @@ -73,6 +73,7 @@ const initialState: OverlayState = { ], refreshState: { type: 'idle' }, notFound: false, + buildingIndicator: false, staticIndicator: false, debugInfo: { devtoolsFrontendUrl: undefined }, versionInfo: { diff --git a/packages/next/src/client/dev/dev-build-indicator/initialize-for-app-router.ts b/packages/next/src/client/dev/dev-build-indicator/initialize-for-app-router.ts deleted file mode 100644 index a37800b77db1..000000000000 --- a/packages/next/src/client/dev/dev-build-indicator/initialize-for-app-router.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { devBuildIndicator } from './internal/dev-build-indicator' - -/** Integrates the generic dev build indicator with the App Router. */ -export const initializeDevBuildIndicatorForAppRouter = () => { - if (!process.env.__NEXT_DEV_INDICATOR) { - return - } - - devBuildIndicator.initialize() -} diff --git a/packages/next/src/client/dev/dev-build-indicator/initialize-for-page-router.ts b/packages/next/src/client/dev/dev-build-indicator/initialize-for-page-router.ts deleted file mode 100644 index 2f681308531e..000000000000 --- a/packages/next/src/client/dev/dev-build-indicator/initialize-for-page-router.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { addMessageListener } from '../../components/react-dev-overlay/pages/websocket' -import { devBuildIndicator } from './internal/dev-build-indicator' -import { handleDevBuildIndicatorHmrEvents } from './internal/handle-dev-build-indicator-hmr-events' - -/** Integrates the generic dev build indicator with the Pages Router. */ -export const initializeDevBuildIndicatorForPageRouter = () => { - if (!process.env.__NEXT_DEV_INDICATOR) { - return - } - - devBuildIndicator.initialize() - - // Add message listener specifically for Pages Router to handle lifecycle events - // related to dev builds (building, built, sync) - addMessageListener(handleDevBuildIndicatorHmrEvents) -} diff --git a/packages/next/src/client/dev/dev-build-indicator/internal/dev-build-indicator.ts b/packages/next/src/client/dev/dev-build-indicator/internal/dev-build-indicator.ts deleted file mode 100644 index 2dd949fb48d5..000000000000 --- a/packages/next/src/client/dev/dev-build-indicator/internal/dev-build-indicator.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { initialize } from './initialize' - -const NOOP = () => {} - -export const devBuildIndicator = { - /** Shows build indicator when Next.js is compiling. Requires initialize() first. */ - show: NOOP, - /** Hides build indicator when Next.js finishes compiling. Requires initialize() first. */ - hide: NOOP, - /** Sets up the build indicator UI component. Call this before using show/hide. */ - initialize, -} diff --git a/packages/next/src/client/dev/dev-build-indicator/internal/handle-dev-build-indicator-hmr-events.ts b/packages/next/src/client/dev/dev-build-indicator/internal/handle-dev-build-indicator-hmr-events.ts deleted file mode 100644 index d4309cee4206..000000000000 --- a/packages/next/src/client/dev/dev-build-indicator/internal/handle-dev-build-indicator-hmr-events.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { - HMR_ACTIONS_SENT_TO_BROWSER, - type HMR_ACTION_TYPES, -} from '../../../../server/dev/hot-reloader-types' -import { devBuildIndicator } from './dev-build-indicator' - -/** - * Handles HMR events to control the dev build indicator visibility. - * Shows indicator when building and hides it when build completes or syncs. - */ -export const handleDevBuildIndicatorHmrEvents = (obj: HMR_ACTION_TYPES) => { - try { - if (!('action' in obj)) { - return - } - - // eslint-disable-next-line default-case - switch (obj.action) { - case HMR_ACTIONS_SENT_TO_BROWSER.BUILDING: - devBuildIndicator.show() - break - case HMR_ACTIONS_SENT_TO_BROWSER.BUILT: - case HMR_ACTIONS_SENT_TO_BROWSER.SYNC: - devBuildIndicator.hide() - break - } - } catch {} -} diff --git a/packages/next/src/client/dev/dev-build-indicator/internal/initialize.ts b/packages/next/src/client/dev/dev-build-indicator/internal/initialize.ts deleted file mode 100644 index 445d3f8fd0c7..000000000000 --- a/packages/next/src/client/dev/dev-build-indicator/internal/initialize.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Singleton store to track whether the app is currently being built - * Used by the dev tools indicator of the new overlay to show build status - */ - -import { devBuildIndicator } from './dev-build-indicator' -import { useSyncExternalStore } from 'react' - -let isVisible = false -let listeners: Array<() => void> = [] - -const subscribe = (listener: () => void) => { - listeners.push(listener) - return () => { - listeners = listeners.filter((l) => l !== listener) - } -} - -const getSnapshot = () => isVisible - -export function useIsDevBuilding() { - return useSyncExternalStore(subscribe, getSnapshot) -} - -export function initialize() { - devBuildIndicator.show = () => { - isVisible = true - listeners.forEach((listener) => listener()) - } - - devBuildIndicator.hide = () => { - isVisible = false - listeners.forEach((listener) => listener()) - } -} diff --git a/packages/next/src/client/page-bootstrap.ts b/packages/next/src/client/page-bootstrap.ts index e13a9ed34129..f9006ed9a638 100644 --- a/packages/next/src/client/page-bootstrap.ts +++ b/packages/next/src/client/page-bootstrap.ts @@ -1,7 +1,6 @@ import '../lib/require-instrumentation-client' import { hydrate, router } from './' import initOnDemandEntries from './dev/on-demand-entries-client' -import { devBuildIndicator } from './dev/dev-build-indicator/internal/dev-build-indicator' import { displayContent } from './dev/fouc' import { connectHMR, @@ -15,7 +14,10 @@ import { HMR_ACTIONS_SENT_TO_BROWSER } from '../server/dev/hot-reloader-types' import { RuntimeErrorHandler } from './components/errors/runtime-error-handler' import { REACT_REFRESH_FULL_RELOAD_FROM_ERROR } from './components/react-dev-overlay/shared' import { performFullReload } from './components/react-dev-overlay/pages/hot-reloader-client' -import { initializeDevBuildIndicatorForPageRouter } from './dev/dev-build-indicator/initialize-for-page-router' +import { + buildingIndicatorHide, + buildingIndicatorShow, +} from './components/react-dev-overlay/pages/client' export function pageBootstrap(assetPrefix: string) { connectHMR({ assetPrefix, path: '/_next/webpack-hmr' }) @@ -23,8 +25,6 @@ export function pageBootstrap(assetPrefix: string) { return hydrate({ beforeRender: displayContent }).then(() => { initOnDemandEntries() - initializeDevBuildIndicatorForPageRouter() - let reloading = false addMessageListener((payload) => { @@ -93,8 +93,8 @@ export function pageBootstrap(assetPrefix: string) { if (!router.clc && pages.includes(router.pathname)) { console.log('Refreshing page data due to server-side change') - devBuildIndicator.show() - const clearIndicator = () => devBuildIndicator.hide() + buildingIndicatorShow() + const clearIndicator = buildingIndicatorHide router .replace(