@@ -4,6 +4,7 @@ import { join } from 'path'
44import { parse } from 'url'
55import resolvePath from './resolve'
66import touch from 'touch'
7+ import { MATCH_ROUTE_NAME , IS_BUNDLED_PAGE } from './utils'
78
89const ADDED = Symbol ( 'added' )
910const BUILDING = Symbol ( 'building' )
@@ -12,13 +13,17 @@ const BUILT = Symbol('built')
1213export default function onDemandEntryHandler ( devMiddleware , compiler , {
1314 dir,
1415 dev,
16+ reload,
1517 maxInactiveAge = 1000 * 25
1618} ) {
17- const entries = { }
18- const lastAccessPages = [ '' ]
19- const doneCallbacks = new EventEmitter ( )
19+ let entries = { }
20+ let lastAccessPages = [ '' ]
21+ let doneCallbacks = new EventEmitter ( )
2022 const invalidator = new Invalidator ( devMiddleware )
2123 let touchedAPage = false
24+ let reloading = false
25+ let stopped = false
26+ let reloadCallbacks = new EventEmitter ( )
2227
2328 compiler . plugin ( 'make' , function ( compilation , done ) {
2429 invalidator . startBuilding ( )
@@ -35,6 +40,27 @@ export default function onDemandEntryHandler (devMiddleware, compiler, {
3540 } )
3641
3742 compiler . plugin ( 'done' , function ( stats ) {
43+ const { compilation } = stats
44+ const hardFailedPages = compilation . errors
45+ . filter ( e => {
46+ // Make sure to only pick errors which marked with missing modules
47+ const hasNoModuleFoundError = / E N O E N T / . test ( e . message ) || / M o d u l e n o t f o u n d / . test ( e . message )
48+ if ( ! hasNoModuleFoundError ) return false
49+
50+ // The page itself is missing. So this is a failed page.
51+ if ( IS_BUNDLED_PAGE . test ( e . module . name ) ) return true
52+
53+ // No dependencies means this is a top level page.
54+ // So this is a failed page.
55+ return e . module . dependencies . length === 0
56+ } )
57+ . map ( e => e . module . chunks )
58+ . reduce ( ( a , b ) => [ ...a , ...b ] , [ ] )
59+ . map ( c => {
60+ const pageName = MATCH_ROUTE_NAME . exec ( c . name ) [ 1 ]
61+ return normalizePage ( `/${ pageName } ` )
62+ } )
63+
3864 // Call all the doneCallbacks
3965 Object . keys ( entries ) . forEach ( ( page ) => {
4066 const entryInfo = entries [ page ]
@@ -57,14 +83,48 @@ export default function onDemandEntryHandler (devMiddleware, compiler, {
5783 } )
5884
5985 invalidator . doneBuilding ( )
86+
87+ if ( hardFailedPages . length > 0 && ! reloading ) {
88+ console . log ( `> Reloading webpack due to inconsistant state of pages(s): ${ hardFailedPages . join ( ', ' ) } ` )
89+ reloading = true
90+ reload ( )
91+ . then ( ( ) => {
92+ console . log ( '> Webpack reloaded.' )
93+ reloadCallbacks . emit ( 'done' )
94+ stop ( )
95+ } )
96+ . catch ( err => {
97+ console . error ( `> Webpack reloading failed: ${ err . message } ` )
98+ console . error ( err . stack )
99+ process . exit ( 1 )
100+ } )
101+ }
60102 } )
61103
62- setInterval ( function ( ) {
104+ const disposeHandler = setInterval ( function ( ) {
105+ if ( stopped ) return
63106 disposeInactiveEntries ( devMiddleware , entries , lastAccessPages , maxInactiveAge )
64107 } , 5000 )
65108
109+ function stop ( ) {
110+ clearInterval ( disposeHandler )
111+ stopped = true
112+ doneCallbacks = null
113+ reloadCallbacks = null
114+ }
115+
66116 return {
117+ waitUntilReloaded ( ) {
118+ if ( ! reloading ) return Promise . resolve ( true )
119+ return new Promise ( ( resolve ) => {
120+ reloadCallbacks . once ( 'done' , function ( ) {
121+ resolve ( )
122+ } )
123+ } )
124+ } ,
125+
67126 async ensurePage ( page ) {
127+ await this . waitUntilReloaded ( )
68128 page = normalizePage ( page )
69129
70130 const pagePath = join ( dir , 'pages' , page )
@@ -103,31 +163,49 @@ export default function onDemandEntryHandler (devMiddleware, compiler, {
103163 } ,
104164
105165 middleware ( ) {
106- return function ( req , res , next ) {
107- if ( ! / ^ \/ _ n e x t \/ o n - d e m a n d - e n t r i e s - p i n g / . test ( req . url ) ) return next ( )
108-
109- const { query } = parse ( req . url , true )
110- const page = normalizePage ( query . page )
111- const entryInfo = entries [ page ]
112-
113- // If there's no entry.
114- // Then it seems like an weird issue.
115- if ( ! entryInfo ) {
116- const message = `Client pings, but there's no entry for page: ${ page } `
117- console . error ( message )
118- sendJson ( res , { invalid : true } )
119- return
120- }
166+ return ( req , res , next ) => {
167+ if ( stopped ) {
168+ // If this handler is stopped, we need to reload the user's browser.
169+ // So the user could connect to the actually running handler.
170+ res . statusCode = 302
171+ res . setHeader ( 'Location' , req . url )
172+ res . end ( '302' )
173+ } else if ( reloading ) {
174+ // Webpack config is reloading. So, we need to wait until it's done and
175+ // reload user's browser.
176+ // So the user could connect to the new handler and webpack setup.
177+ this . waitUntilReloaded ( )
178+ . then ( ( ) => {
179+ res . statusCode = 302
180+ res . setHeader ( 'Location' , req . url )
181+ res . end ( '302' )
182+ } )
183+ } else {
184+ if ( ! / ^ \/ _ n e x t \/ o n - d e m a n d - e n t r i e s - p i n g / . test ( req . url ) ) return next ( )
185+
186+ const { query } = parse ( req . url , true )
187+ const page = normalizePage ( query . page )
188+ const entryInfo = entries [ page ]
189+
190+ // If there's no entry.
191+ // Then it seems like an weird issue.
192+ if ( ! entryInfo ) {
193+ const message = `Client pings, but there's no entry for page: ${ page } `
194+ console . error ( message )
195+ sendJson ( res , { invalid : true } )
196+ return
197+ }
121198
122- sendJson ( res , { success : true } )
199+ sendJson ( res , { success : true } )
123200
124- // We don't need to maintain active state of anything other than BUILT entries
125- if ( entryInfo . status !== BUILT ) return
201+ // We don't need to maintain active state of anything other than BUILT entries
202+ if ( entryInfo . status !== BUILT ) return
126203
127- // If there's an entryInfo
128- lastAccessPages . pop ( )
129- lastAccessPages . unshift ( page )
130- entryInfo . lastActiveTime = Date . now ( )
204+ // If there's an entryInfo
205+ lastAccessPages . pop ( )
206+ lastAccessPages . unshift ( page )
207+ entryInfo . lastActiveTime = Date . now ( )
208+ }
131209 }
132210 }
133211 }
0 commit comments