rsnext/packages/next/client/next-dev.js
JJ Kasper 122899bd37
Add hard navigation guard and fix middleware rewrite cases (#37815)
This adds a guard for whenever we do a hard navigation over a client-navigation to ensure we aren't redirecting to the same URL that we are currently on as this can cause infinite redirecting. This also fixes some cases with middleware rewrites without i18n enabled and expands our middleware suite to test both with i18n and without. 

This also fixes a race condition with the query updating where a user could attempt a route transition and it then gets overridden by the query updating and prevents firing router events during the query updating as these can be false signals of a transition.

## Bug

- [x] Related issues linked using `fixes #number`
- [x] Integration tests added
- [ ] Errors have helpful link attached, see `contributing.md`

Fixes: https://github.com/vercel/next.js/issues/37804
2022-06-20 11:31:19 +00:00

113 lines
3.6 KiB
JavaScript

import { initialize, hydrate, version, router, emitter } from './'
import initOnDemandEntries from './dev/on-demand-entries-client'
import initWebpackHMR from './dev/webpack-hot-middleware-client'
import initializeBuildWatcher from './dev/dev-build-watcher'
import { displayContent } from './dev/fouc'
import { connectHMR, addMessageListener } from './dev/error-overlay/websocket'
import {
assign,
urlQueryToSearchParams,
} from '../shared/lib/router/utils/querystring'
if (!window._nextSetupHydrationWarning) {
const origConsoleError = window.console.error
window.console.error = (...args) => {
const isHydrateError = args.some(
(arg) =>
typeof arg === 'string' &&
arg.match(/(hydration|content does not match|did not match)/i)
)
if (isHydrateError) {
args = [
...args,
`\n\nSee more info here: https://nextjs.org/docs/messages/react-hydration-error`,
]
}
origConsoleError.apply(window.console, args)
}
window._nextSetupHydrationWarning = true
}
window.next = {
version,
// router is initialized later so it has to be live-binded
get router() {
return router
},
emitter,
}
const webpackHMR = initWebpackHMR()
initialize({ webpackHMR })
.then(({ assetPrefix }) => {
connectHMR({ assetPrefix, path: '/_next/webpack-hmr' })
return hydrate({ beforeRender: displayContent }).then(() => {
initOnDemandEntries()
let buildIndicatorHandler = () => {}
function devPagesManifestListener(event) {
if (event.data.indexOf('devPagesManifest') !== -1) {
fetch(
`${assetPrefix}/_next/static/development/_devPagesManifest.json`
)
.then((res) => res.json())
.then((manifest) => {
window.__DEV_PAGES_MANIFEST = manifest
})
.catch((err) => {
console.log(`Failed to fetch devPagesManifest`, err)
})
} else if (event.data.indexOf('middlewareChanges') !== -1) {
return window.location.reload()
} else if (event.data.indexOf('serverOnlyChanges') !== -1) {
const { pages } = JSON.parse(event.data)
// Make sure to reload when the dev-overlay is showing for an
// API route
if (pages.includes(router.query.__NEXT_PAGE)) {
return window.location.reload()
}
if (!router.clc && pages.includes(router.pathname)) {
console.log('Refreshing page data due to server-side change')
buildIndicatorHandler('building')
const clearIndicator = () => buildIndicatorHandler('built')
router
.replace(
router.pathname +
'?' +
String(
assign(
urlQueryToSearchParams(router.query),
new URLSearchParams(location.search)
)
),
router.asPath,
{ scroll: false }
)
.catch(() => {
// trigger hard reload when failing to refresh data
// to show error overlay properly
location.reload()
})
.finally(clearIndicator)
}
}
}
addMessageListener(devPagesManifestListener)
if (process.env.__NEXT_BUILD_INDICATOR) {
initializeBuildWatcher((handler) => {
buildIndicatorHandler = handler
}, process.env.__NEXT_BUILD_INDICATOR_POSITION)
}
})
})
.catch((err) => {
console.error('Error was not caught', err)
})