2018-12-14 11:25:59 +00:00
/* global location, WebSocket */
2017-02-26 19:45:16 +00:00
2018-11-28 14:03:02 +00:00
import Router from 'next/router'
2017-02-26 19:45:16 +00:00
import fetch from 'unfetch'
2019-01-19 12:39:09 +00:00
const { hostname , protocol } = location
const wsProtocol = protocol . includes ( 'https' ) ? 'wss' : 'ws'
2018-12-14 11:25:59 +00:00
const retryTime = 5000
let ws = null
let lastHref = null
2019-02-15 21:22:21 +00:00
let wsConnectTries = 0
let showedWarning = false
2018-12-14 11:25:59 +00:00
export default async ( { assetPrefix } ) => {
2017-04-18 04:18:43 +00:00
Router . ready ( ( ) => {
2018-07-31 19:04:14 +00:00
Router . events . on ( 'routeChangeComplete' , ping )
2017-04-18 04:18:43 +00:00
} )
2017-03-12 03:51:49 +00:00
2019-02-15 21:22:21 +00:00
const setup = async ( ) => {
2018-12-14 11:25:59 +00:00
if ( ws && ws . readyState === ws . OPEN ) {
return Promise . resolve ( )
2019-02-15 21:22:21 +00:00
} else if ( wsConnectTries > 1 ) {
return
2018-12-14 11:25:59 +00:00
}
2019-02-15 21:22:21 +00:00
wsConnectTries ++
2018-12-14 11:25:59 +00:00
return new Promise ( resolve => {
2019-02-15 16:49:40 +00:00
ws = new WebSocket ( ` ${ wsProtocol } :// ${ hostname } : ${ process . env . _ _NEXT _WS _PORT } ${ process . env . _ _NEXT _WS _PROXY _PATH } ` )
2019-02-15 21:22:21 +00:00
ws . onopen = ( ) => {
wsConnectTries = 0
resolve ( )
}
2018-12-14 11:25:59 +00:00
ws . onclose = ( ) => {
setTimeout ( async ( ) => {
await fetch ( ` ${ assetPrefix } /_next/on-demand-entries-ping ` )
2019-02-15 21:22:21 +00:00
. then ( res => {
// Only reload if next was restarted and we have a new WebSocket port
if ( res . status === 200 && res . headers . get ( 'port' ) !== process . env . _ _NEXT _WS _PORT + '' ) {
location . reload ( )
}
} )
2018-12-14 11:25:59 +00:00
. catch ( ( ) => { } )
await setup ( true )
resolve ( )
} , retryTime )
}
ws . onmessage = async ( { data } ) => {
const payload = JSON . parse ( data )
if ( payload . invalid && lastHref !== location . href ) {
// Payload can be invalid even if the page does not exist.
// So, we need to make sure it exists before reloading.
const pageRes = await fetch ( location . href , {
credentials : 'omit'
} )
if ( pageRes . status === 200 ) {
location . reload ( )
} else {
lastHref = location . href
}
2017-07-02 07:29:08 +00:00
}
2017-04-18 04:18:43 +00:00
}
2018-12-14 11:25:59 +00:00
} )
}
2019-02-15 21:22:21 +00:00
setup ( )
2018-12-14 11:25:59 +00:00
async function ping ( ) {
2019-02-15 21:22:21 +00:00
// Use WebSocket if available
if ( ws && ws . readyState === ws . OPEN ) {
return ws . send ( Router . pathname )
}
if ( ! showedWarning ) {
console . warn ( 'onDemandEntries WebSocket failed to connect, falling back to fetch based pinging. https://err.sh/zeit/next.js/on-demand-entries-websocket-unavailable' )
showedWarning = true
}
// If not, fallback to fetch based pinging
try {
const url = ` ${ assetPrefix || '' } /_next/on-demand-entries-ping?page= ${ Router . pathname } `
const res = await fetch ( url , {
credentials : 'same-origin'
} )
const payload = await res . json ( )
if ( payload . invalid ) {
// Payload can be invalid even if the page does not exist.
// So, we need to make sure it exists before reloading.
const pageRes = await fetch ( location . href , {
credentials : 'same-origin'
} )
if ( pageRes . status === 200 ) {
location . reload ( )
}
}
} catch ( err ) {
console . error ( ` Error with on-demand-entries-ping: ${ err . message } ` )
2017-02-26 19:45:16 +00:00
}
}
2017-09-08 21:26:13 +00:00
let pingerTimeout
2017-04-18 04:18:43 +00:00
async function runPinger ( ) {
2017-09-08 21:26:13 +00:00
// Will restart on the visibilitychange API below. For older browsers, this
// will always be true and will always run, but support is fairly prevalent
// at this point.
while ( ! document . hidden ) {
2017-04-18 04:18:43 +00:00
await ping ( )
2018-12-14 11:25:59 +00:00
await new Promise ( resolve => {
2017-09-08 21:26:13 +00:00
pingerTimeout = setTimeout ( resolve , 5000 )
} )
2017-04-18 04:18:43 +00:00
}
2017-02-26 19:45:16 +00:00
}
2018-12-14 11:25:59 +00:00
document . addEventListener (
'visibilitychange' ,
( ) => {
if ( ! document . hidden ) {
runPinger ( )
} else {
clearTimeout ( pingerTimeout )
}
} ,
false
)
2017-09-08 21:26:13 +00:00
setTimeout ( ( ) => {
2018-12-14 11:25:59 +00:00
runPinger ( ) . catch ( err => {
console . error ( err )
} )
2017-09-08 21:26:13 +00:00
} , 10000 )
2017-04-18 04:18:43 +00:00
}