@@ -14,13 +14,17 @@ import { errorOverlayReducer } from './internal/error-overlay-reducer'
14
14
import {
15
15
ACTION_BUILD_OK ,
16
16
ACTION_BUILD_ERROR ,
17
+ ACTION_BEFORE_REFRESH ,
17
18
ACTION_REFRESH ,
18
19
ACTION_UNHANDLED_ERROR ,
19
20
ACTION_UNHANDLED_REJECTION ,
20
21
} from './internal/error-overlay-reducer'
21
22
import { parseStack } from './internal/helpers/parseStack'
22
23
import ReactDevOverlay from './internal/ReactDevOverlay'
23
- import { useErrorHandler } from './internal/helpers/use-error-handler'
24
+ import {
25
+ RuntimeErrorHandler ,
26
+ useErrorHandler ,
27
+ } from './internal/helpers/use-error-handler'
24
28
import {
25
29
useSendMessage ,
26
30
useWebsocket ,
@@ -30,6 +34,7 @@ import {
30
34
interface Dispatcher {
31
35
onBuildOk ( ) : void
32
36
onBuildError ( message : string ) : void
37
+ onBeforeRefresh ( ) : void
33
38
onRefresh ( ) : void
34
39
}
35
40
@@ -38,10 +43,15 @@ type PongEvent = any
38
43
39
44
let mostRecentCompilationHash : any = null
40
45
let __nextDevClientId = Math . round ( Math . random ( ) * 100 + Date . now ( ) )
41
- let hadRuntimeError = false
42
46
43
47
// let startLatency = undefined
44
48
49
+ function onBeforeFastRefresh ( dispatcher : Dispatcher , hasUpdates : boolean ) {
50
+ if ( hasUpdates ) {
51
+ dispatcher . onBeforeRefresh ( )
52
+ }
53
+ }
54
+
45
55
function onFastRefresh ( dispatcher : Dispatcher , hasUpdates : boolean ) {
46
56
dispatcher . onBuildOk ( )
47
57
if ( hasUpdates ) {
@@ -104,6 +114,7 @@ function performFullReload(err: any, sendMessage: any) {
104
114
105
115
// Attempt to update code on the fly, fall back to a hard reload.
106
116
function tryApplyUpdates (
117
+ onBeforeUpdate : ( hasUpdates : boolean ) => void ,
107
118
onHotUpdateSuccess : ( hasUpdates : boolean ) => void ,
108
119
sendMessage : any ,
109
120
dispatcher : Dispatcher
@@ -114,7 +125,7 @@ function tryApplyUpdates(
114
125
}
115
126
116
127
function handleApplyUpdates ( err : any , updatedModules : any ) {
117
- if ( err || hadRuntimeError || ! updatedModules ) {
128
+ if ( err || RuntimeErrorHandler . hadRuntimeError || ! updatedModules ) {
118
129
if ( err ) {
119
130
console . warn (
120
131
'[Fast Refresh] performing full reload\n\n' +
@@ -124,7 +135,7 @@ function tryApplyUpdates(
124
135
'It is also possible the parent component of the component you edited is a class component, which disables Fast Refresh.\n' +
125
136
'Fast Refresh requires at least one parent function component in your React tree.'
126
137
)
127
- } else if ( hadRuntimeError ) {
138
+ } else if ( RuntimeErrorHandler . hadRuntimeError ) {
128
139
console . warn (
129
140
'[Fast Refresh] performing full reload because your application had an unrecoverable error'
130
141
)
@@ -142,6 +153,7 @@ function tryApplyUpdates(
142
153
if ( isUpdateAvailable ( ) ) {
143
154
// While we were updating, there was a new update! Do it again.
144
155
tryApplyUpdates (
156
+ hasUpdates ? ( ) => { } : onBeforeUpdate ,
145
157
hasUpdates ? ( ) => dispatcher . onBuildOk ( ) : onHotUpdateSuccess ,
146
158
sendMessage ,
147
159
dispatcher
@@ -161,14 +173,25 @@ function tryApplyUpdates(
161
173
162
174
// https://webpack.js.org/api/hot-module-replacement/#check
163
175
// @ts -expect-error module.hot exists
164
- module . hot . check ( /* autoApply */ true ) . then (
165
- ( updatedModules : any ) => {
166
- handleApplyUpdates ( null , updatedModules )
167
- } ,
168
- ( err : any ) => {
169
- handleApplyUpdates ( err , null )
170
- }
171
- )
176
+ module . hot
177
+ . check ( /* autoApply */ false )
178
+ . then ( ( updatedModules : any ) => {
179
+ const hasUpdates = Boolean ( updatedModules . length )
180
+ if ( typeof onBeforeUpdate === 'function' ) {
181
+ onBeforeUpdate ( hasUpdates )
182
+ }
183
+ // https://webpack.js.org/api/hot-module-replacement/#apply
184
+ // @ts -expect-error module.hot exists
185
+ return module . hot . apply ( )
186
+ } )
187
+ . then (
188
+ ( updatedModules : any ) => {
189
+ handleApplyUpdates ( null , updatedModules )
190
+ } ,
191
+ ( err : any ) => {
192
+ handleApplyUpdates ( err , null )
193
+ }
194
+ )
172
195
}
173
196
174
197
function processMessage (
@@ -260,6 +283,9 @@ function processMessage(
260
283
// Attempt to apply hot updates or reload.
261
284
if ( isHotUpdate ) {
262
285
tryApplyUpdates (
286
+ function onBeforeHotUpdate ( hasUpdates : boolean ) {
287
+ onBeforeFastRefresh ( dispatcher , hasUpdates )
288
+ } ,
263
289
function onSuccessfulHotUpdate ( hasUpdates : any ) {
264
290
// Only dismiss it when we're sure it's a hot update.
265
291
// Otherwise it would flicker right before the reload.
@@ -287,6 +313,9 @@ function processMessage(
287
313
// Attempt to apply hot updates or reload.
288
314
if ( isHotUpdate ) {
289
315
tryApplyUpdates (
316
+ function onBeforeHotUpdate ( hasUpdates : boolean ) {
317
+ onBeforeFastRefresh ( dispatcher , hasUpdates )
318
+ } ,
290
319
function onSuccessfulHotUpdate ( hasUpdates : any ) {
291
320
// Only dismiss it when we're sure it's a hot update.
292
321
// Otherwise it would flicker right before the reload.
@@ -306,7 +335,7 @@ function processMessage(
306
335
clientId : __nextDevClientId ,
307
336
} )
308
337
)
309
- if ( hadRuntimeError ) {
338
+ if ( RuntimeErrorHandler . hadRuntimeError ) {
310
339
return window . location . reload ( )
311
340
}
312
341
startTransition ( ( ) => {
@@ -361,6 +390,7 @@ export default function HotReload({
361
390
nextId : 1 ,
362
391
buildError : null ,
363
392
errors : [ ] ,
393
+ refreshState : { type : 'idle' } ,
364
394
} )
365
395
const dispatcher = useMemo ( ( ) : Dispatcher => {
366
396
return {
@@ -370,72 +400,29 @@ export default function HotReload({
370
400
onBuildError ( message : string ) : void {
371
401
dispatch ( { type : ACTION_BUILD_ERROR , message } )
372
402
} ,
403
+ onBeforeRefresh ( ) : void {
404
+ dispatch ( { type : ACTION_BEFORE_REFRESH } )
405
+ } ,
373
406
onRefresh ( ) : void {
374
407
dispatch ( { type : ACTION_REFRESH } )
375
408
} ,
376
409
}
377
410
} , [ dispatch ] )
378
411
379
- const handleOnUnhandledError = useCallback (
380
- ( ev : WindowEventMap [ 'error' ] ) : void => {
381
- if (
382
- ev . error &&
383
- ev . error . digest &&
384
- ( ev . error . digest . startsWith ( 'NEXT_REDIRECT' ) ||
385
- ev . error . digest === 'NEXT_NOT_FOUND' )
386
- ) {
387
- ev . preventDefault ( )
388
- return
389
- }
390
-
391
- hadRuntimeError = true
392
- const error = ev ?. error
393
- if (
394
- ! error ||
395
- ! ( error instanceof Error ) ||
396
- typeof error . stack !== 'string'
397
- ) {
398
- // A non-error was thrown, we don't have anything to show. :-(
399
- return
400
- }
401
-
402
- if (
403
- error . message . match ( / ( h y d r a t i o n | c o n t e n t d o e s n o t m a t c h | d i d n o t m a t c h ) / i)
404
- ) {
405
- error . message += `\n\nSee more info here: https://nextjs.org/docs/messages/react-hydration-error`
406
- }
407
-
408
- const e = error
409
- dispatch ( {
410
- type : ACTION_UNHANDLED_ERROR ,
411
- reason : error ,
412
- frames : parseStack ( e . stack ! ) ,
413
- } )
414
- } ,
415
- [ ]
416
- )
417
- const handleOnUnhandledRejection = useCallback (
418
- ( ev : WindowEventMap [ 'unhandledrejection' ] ) : void => {
419
- hadRuntimeError = true
420
- const reason = ev ?. reason
421
- if (
422
- ! reason ||
423
- ! ( reason instanceof Error ) ||
424
- typeof reason . stack !== 'string'
425
- ) {
426
- // A non-error was thrown, we don't have anything to show. :-(
427
- return
428
- }
429
-
430
- const e = reason
431
- dispatch ( {
432
- type : ACTION_UNHANDLED_REJECTION ,
433
- reason : reason ,
434
- frames : parseStack ( e . stack ! ) ,
435
- } )
436
- } ,
437
- [ ]
438
- )
412
+ const handleOnUnhandledError = useCallback ( ( error : Error ) : void => {
413
+ dispatch ( {
414
+ type : ACTION_UNHANDLED_ERROR ,
415
+ reason : error ,
416
+ frames : parseStack ( error . stack ! ) ,
417
+ } )
418
+ } , [ ] )
419
+ const handleOnUnhandledRejection = useCallback ( ( reason : Error ) : void => {
420
+ dispatch ( {
421
+ type : ACTION_UNHANDLED_REJECTION ,
422
+ reason : reason ,
423
+ frames : parseStack ( reason . stack ! ) ,
424
+ } )
425
+ } , [ ] )
439
426
useErrorHandler ( handleOnUnhandledError , handleOnUnhandledRejection )
440
427
441
428
const webSocketRef = useWebsocket ( assetPrefix )
0 commit comments