Skip to content

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
gaojude committed Jan 7, 2025
1 parent e4c0794 commit 4a8fb40
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 63 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { HMR_ACTIONS_SENT_TO_BROWSER } from '../../../server/dev/hot-reloader-types'
import { addMessageListener } from '../../components/react-dev-overlay/pages/websocket'
import { devBuildIndicator } from './internal/dev-build-indicator'

/** Integrates the generic dev build indicator with the Pages Router. */
export const initializeDevBuildIndicatorForPageRouter = () => {
if (!process.env.__NEXT_BUILD_INDICATOR) {
return
}

devBuildIndicator.initialize(process.env.__NEXT_BUILD_INDICATOR_POSITION)

// Add message listener specifically for Pages Router to handle lifecycle events
// related to dev builds (building, built, sync)
addMessageListener((obj) => {
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 {}
})
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
/* eslint-disable @typescript-eslint/no-use-before-define */
import { HMR_ACTIONS_SENT_TO_BROWSER } from '../../server/dev/hot-reloader-types'
import type { HMR_ACTION_TYPES } from '../../server/dev/hot-reloader-types'
import { addMessageListener } from '../components/react-dev-overlay/pages/websocket'

type VerticalPosition = 'top' | 'bottom'
type HorizonalPosition = 'left' | 'right'

export interface ShowHideHandler {
show: () => void
hide: () => void
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,
}

export default function initializeBuildWatcher(
toggleCallback: (handlers: ShowHideHandler) => void,
position = 'bottom-right'
) {
/**
* Creates and initializes the build indicator UI component.
* Configures where the indicator appears in the browser window.
* Must be called before using showDevBuildIndicator or hideDevBuildIndicator.
* This is a singleton initialization function that sets up the indicator's DOM and behavior.
*/
function initialize(position = 'bottom-right') {
const shadowHost = document.createElement('div')
const [verticalProperty, horizontalProperty] = position.split('-', 2) as [
VerticalPosition,
HorizonalPosition,
]
shadowHost.id = '__next-build-watcher'
shadowHost.id = '__next-build-indicator'
// Make sure container is fixed and on a high zIndex so it shows
shadowHost.style.position = 'fixed'
// Ensure container's position to be top or bottom (default)
Expand All @@ -42,7 +46,7 @@ export default function initializeBuildWatcher(
// the Shadow DOM, we need to prefix all the names so there
// will be no conflicts
shadowRoot = shadowHost
prefix = '__next-build-watcher-'
prefix = '__next-build-indicator-'
}

// Container
Expand All @@ -58,22 +62,14 @@ export default function initializeBuildWatcher(
let isBuilding = false
let timeoutId: null | ReturnType<typeof setTimeout> = null

// Handle events

addMessageListener((obj) => {
try {
handleMessage(obj)
} catch {}
})

function show() {
devBuildIndicator.show = () => {
timeoutId && clearTimeout(timeoutId)
isVisible = true
isBuilding = true
updateContainer()
}

function hide() {
devBuildIndicator.hide = () => {
isBuilding = false
// Wait for the fade out transition to complete
timeoutId = setTimeout(() => {
Expand All @@ -83,28 +79,6 @@ export default function initializeBuildWatcher(
updateContainer()
}

function handleMessage(obj: HMR_ACTION_TYPES) {
if (!('action' in obj)) {
return
}

// eslint-disable-next-line default-case
switch (obj.action) {
case HMR_ACTIONS_SENT_TO_BROWSER.BUILDING:
show()
break
case HMR_ACTIONS_SENT_TO_BROWSER.BUILT:
case HMR_ACTIONS_SENT_TO_BROWSER.SYNC:
hide()
break
}
}

toggleCallback({
show,
hide,
})

function updateContainer() {
if (isBuilding) {
container.classList.add(`${prefix}building`)
Expand Down
18 changes: 5 additions & 13 deletions packages/next/src/client/page-bootstrap.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { hydrate, router } from './'
import initOnDemandEntries from './dev/on-demand-entries-client'
import initializeBuildWatcher from './dev/dev-build-watcher'
import type { ShowHideHandler } from './dev/dev-build-watcher'
import { devBuildIndicator } from './dev/dev-build-indicator/internal/dev-build-indicator'
import { displayContent } from './dev/fouc'
import {
connectHMR,
Expand All @@ -15,20 +14,15 @@ import { HMR_ACTIONS_SENT_TO_BROWSER } from '../server/dev/hot-reloader-types'
import { RuntimeErrorHandler } from './components/react-dev-overlay/internal/helpers/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'

export function pageBootstrap(assetPrefix: string) {
connectHMR({ assetPrefix, path: '/_next/webpack-hmr' })

return hydrate({ beforeRender: displayContent }).then(() => {
initOnDemandEntries()

let buildIndicatorHandler: ShowHideHandler | undefined

if (process.env.__NEXT_BUILD_INDICATOR) {
initializeBuildWatcher((handler) => {
buildIndicatorHandler = handler
}, process.env.__NEXT_BUILD_INDICATOR_POSITION)
}
initializeDevBuildIndicatorForPageRouter()

let reloading = false

Expand Down Expand Up @@ -98,10 +92,8 @@ export function pageBootstrap(assetPrefix: string) {

if (!router.clc && pages.includes(router.pathname)) {
console.log('Refreshing page data due to server-side change')

buildIndicatorHandler?.show()

const clearIndicator = () => buildIndicatorHandler?.hide()
devBuildIndicator.show()
const clearIndicator = () => devBuildIndicator.hide()

router
.replace(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { NextInstance } from 'e2e-utils'
const installCheckVisible = (browser) => {
return browser.eval(`(function() {
window.checkInterval = setInterval(function() {
let watcherDiv = document.querySelector('#__next-build-watcher')
let watcherDiv = document.querySelector('#__next-build-indicator')
watcherDiv = watcherDiv.shadowRoot || watcherDiv
window.showedBuilder = window.showedBuilder || (
watcherDiv.querySelector('div').className.indexOf('visible') > -1
Expand Down
6 changes: 3 additions & 3 deletions test/integration/build-indicator/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ let app
const installCheckVisible = (browser) => {
return browser.eval(`(function() {
window.checkInterval = setInterval(function() {
let watcherDiv = document.querySelector('#__next-build-watcher')
let watcherDiv = document.querySelector('#__next-build-indicator')
watcherDiv = watcherDiv.shadowRoot || watcherDiv
window.showedBuilder = window.showedBuilder || (
watcherDiv.querySelector('div').className.indexOf('visible') > -1
Expand Down Expand Up @@ -70,7 +70,7 @@ describe('Build Activity Indicator', () => {
it('Adds the build indicator container', async () => {
const browser = await webdriver(appPort, '/')
const html = await browser.eval('document.body.innerHTML')
expect(html).toMatch(/__next-build-watcher/)
expect(html).toMatch(/__next-build-indicator/)
await browser.close()
})
;(process.env.TURBOPACK ? describe.skip : describe)('webpack only', () => {
Expand Down Expand Up @@ -120,7 +120,7 @@ describe('Build Activity Indicator', () => {
it('Does not add the build indicator container', async () => {
const browser = await webdriver(appPort, '/')
const html = await browser.eval('document.body.innerHTML')
expect(html).not.toMatch(/__next-build-watcher/)
expect(html).not.toMatch(/__next-build-indicator/)
await browser.close()
})
})
Expand Down

0 comments on commit 4a8fb40

Please sign in to comment.