Skip to content

Commit

Permalink
Also update next/client/components/react-dev-overlay
Browse files Browse the repository at this point in the history
  • Loading branch information
alexkirsz committed Nov 3, 2022
1 parent ef6c9b4 commit 1b8e3e8
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 19 deletions.
Expand Up @@ -14,6 +14,7 @@ import { errorOverlayReducer } from './internal/error-overlay-reducer'
import {
ACTION_BUILD_OK,
ACTION_BUILD_ERROR,
ACTION_BEFORE_REFRESH,
ACTION_REFRESH,
ACTION_UNHANDLED_ERROR,
ACTION_UNHANDLED_REJECTION,
Expand All @@ -30,6 +31,7 @@ import {
interface Dispatcher {
onBuildOk(): void
onBuildError(message: string): void
onBeforeRefresh(): void
onRefresh(): void
}

Expand All @@ -42,6 +44,12 @@ let hadRuntimeError = false

// let startLatency = undefined

function onBeforeFastRefresh(dispatcher: Dispatcher, hasUpdates: boolean) {
if (hasUpdates) {
dispatcher.onBeforeRefresh()
}
}

function onFastRefresh(dispatcher: Dispatcher, hasUpdates: boolean) {
dispatcher.onBuildOk()
if (hasUpdates) {
Expand Down Expand Up @@ -104,6 +112,7 @@ function performFullReload(err: any, sendMessage: any) {

// Attempt to update code on the fly, fall back to a hard reload.
function tryApplyUpdates(
onBeforeUpdate: (hasUpdates: boolean) => void,
onHotUpdateSuccess: (hasUpdates: boolean) => void,
sendMessage: any,
dispatcher: Dispatcher
Expand Down Expand Up @@ -142,6 +151,7 @@ function tryApplyUpdates(
if (isUpdateAvailable()) {
// While we were updating, there was a new update! Do it again.
tryApplyUpdates(
hasUpdates ? () => {} : onBeforeUpdate,
hasUpdates ? () => dispatcher.onBuildOk() : onHotUpdateSuccess,
sendMessage,
dispatcher
Expand All @@ -161,14 +171,25 @@ function tryApplyUpdates(

// https://webpack.js.org/api/hot-module-replacement/#check
// @ts-expect-error module.hot exists
module.hot.check(/* autoApply */ true).then(
(updatedModules: any) => {
handleApplyUpdates(null, updatedModules)
},
(err: any) => {
handleApplyUpdates(err, null)
}
)
module.hot
.check(/* autoApply */ false)
.then((updatedModules: any) => {
const hasUpdates = Boolean(updatedModules.length)
if (typeof onBeforeUpdate === 'function') {
onBeforeUpdate(hasUpdates)
}
// https://webpack.js.org/api/hot-module-replacement/#apply
// @ts-expect-error module.hot exists
return module.hot.apply()
})
.then(
(updatedModules: any) => {
handleApplyUpdates(null, updatedModules)
},
(err: any) => {
handleApplyUpdates(err, null)
}
)
}

function processMessage(
Expand Down Expand Up @@ -260,6 +281,9 @@ function processMessage(
// Attempt to apply hot updates or reload.
if (isHotUpdate) {
tryApplyUpdates(
function onBeforeHotUpdate(hasUpdates: boolean) {
onBeforeFastRefresh(dispatcher, hasUpdates)
},
function onSuccessfulHotUpdate(hasUpdates: any) {
// Only dismiss it when we're sure it's a hot update.
// Otherwise it would flicker right before the reload.
Expand Down Expand Up @@ -287,6 +311,9 @@ function processMessage(
// Attempt to apply hot updates or reload.
if (isHotUpdate) {
tryApplyUpdates(
function onBeforeHotUpdate(hasUpdates: boolean) {
onBeforeFastRefresh(dispatcher, hasUpdates)
},
function onSuccessfulHotUpdate(hasUpdates: any) {
// Only dismiss it when we're sure it's a hot update.
// Otherwise it would flicker right before the reload.
Expand Down Expand Up @@ -361,6 +388,7 @@ export default function HotReload({
nextId: 1,
buildError: null,
errors: [],
refreshState: { type: 'idle' },
})
const dispatcher = useMemo((): Dispatcher => {
return {
Expand All @@ -370,6 +398,9 @@ export default function HotReload({
onBuildError(message: string): void {
dispatch({ type: ACTION_BUILD_ERROR, message })
},
onBeforeRefresh(): void {
dispatch({ type: ACTION_BEFORE_REFRESH })
},
onRefresh(): void {
dispatch({ type: ACTION_REFRESH })
},
Expand Down
Expand Up @@ -3,6 +3,7 @@ import { SupportedErrorEvent } from './container/Errors'

export const ACTION_BUILD_OK = 'build-ok'
export const ACTION_BUILD_ERROR = 'build-error'
export const ACTION_BEFORE_REFRESH = 'before-fast-refresh'
export const ACTION_REFRESH = 'fast-refresh'
export const ACTION_UNHANDLED_ERROR = 'unhandled-error'
export const ACTION_UNHANDLED_REJECTION = 'unhandled-rejection'
Expand All @@ -14,6 +15,9 @@ interface BuildErrorAction {
type: typeof ACTION_BUILD_ERROR
message: string
}
interface BeforeFastRefreshAction {
type: typeof ACTION_BEFORE_REFRESH
}
interface FastRefreshAction {
type: typeof ACTION_REFRESH
}
Expand All @@ -28,20 +32,44 @@ export interface UnhandledRejectionAction {
frames: StackFrame[]
}

export type FastRefreshState =
| {
type: 'idle'
}
| {
type: 'pending'
errors: SupportedErrorEvent[]
}

export interface OverlayState {
nextId: number
buildError: string | null
errors: SupportedErrorEvent[]
rootLayoutMissingTagsError?: {
missingTags: string[]
}
refreshState: FastRefreshState
}

function pushErrorFilterDuplicates(
errors: SupportedErrorEvent[],
err: SupportedErrorEvent
): SupportedErrorEvent[] {
return [
...errors.filter((e) => {
// Filter out duplicate errors
return e.event.reason !== err.event.reason
}),
err,
]
}

export function errorOverlayReducer(
state: Readonly<OverlayState>,
action: Readonly<
| BuildOkAction
| BuildErrorAction
| BeforeFastRefreshAction
| FastRefreshAction
| UnhandledErrorAction
| UnhandledRejectionAction
Expand All @@ -54,21 +82,56 @@ export function errorOverlayReducer(
case ACTION_BUILD_ERROR: {
return { ...state, buildError: action.message }
}
case ACTION_BEFORE_REFRESH: {
return { ...state, refreshState: { type: 'pending', errors: [] } }
}
case ACTION_REFRESH: {
return { ...state, buildError: null, errors: [] }
return {
...state,
buildError: null,
errors:
// Errors can come in during updates. In this case, UNHANDLED_ERROR
// and UNHANDLED_REJECTION events might be dispatched between the
// BEFORE_REFRESH and the REFRESH event. We want to keep those errors
// around until the next refresh. Otherwise we run into a race
// condition where those errors would be cleared on refresh completion
// before they can be displayed.
state.refreshState.type === 'pending'
? state.refreshState.errors
: [],
refreshState: { type: 'idle' },
}
}
case ACTION_UNHANDLED_ERROR:
case ACTION_UNHANDLED_REJECTION: {
return {
...state,
nextId: state.nextId + 1,
errors: [
...state.errors.filter((err) => {
// Filter out duplicate errors
return err.event.reason !== action.reason
}),
{ id: state.nextId, event: action },
],
switch (state.refreshState.type) {
case 'idle': {
return {
...state,
nextId: state.nextId + 1,
errors: pushErrorFilterDuplicates(state.errors, {
id: state.nextId,
event: action,
}),
}
}
case 'pending': {
return {
...state,
nextId: state.nextId + 1,
refreshState: {
...state.refreshState,
errors: pushErrorFilterDuplicates(state.refreshState.errors, {
id: state.nextId,
event: action,
}),
},
}
}
default:
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const _: never = state.refreshState
return state
}
}
default: {
Expand Down

0 comments on commit 1b8e3e8

Please sign in to comment.