Skip to content

Commit

Permalink
Allow mismatching href and as when manually provided (#9837)
Browse files Browse the repository at this point in the history
* Allow mismatch href and as when manually provided

* Swap warning and error and throw error in production also

* Add test for mismatch error in production

* Update to only show warning in development
  • Loading branch information
ijjk committed Jan 20, 2020
1 parent 57aae47 commit fcfb595
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 16 deletions.
38 changes: 25 additions & 13 deletions packages/next/next-server/lib/router/router.ts
Expand Up @@ -331,22 +331,34 @@ export default class Router implements BaseRouter {

if (isDynamicRoute(route)) {
const { pathname: asPathname } = parse(as)
const routeMatch = getRouteMatcher(getRouteRegex(route))(asPathname)
const routeRegex = getRouteRegex(route)
const routeMatch = getRouteMatcher(routeRegex)(asPathname)
if (!routeMatch) {
const error =
`The provided \`as\` value (${asPathname}) is incompatible with the \`href\` value (${route}). ` +
`Read more: https://err.sh/zeit/next.js/incompatible-href-as`

if (process.env.NODE_ENV !== 'production') {
throw new Error(error)
} else {
console.error(error)
const missingParams = Object.keys(routeRegex.groups).filter(
param => !query[param]
)

if (missingParams.length > 0) {
if (process.env.NODE_ENV !== 'production') {
console.warn(
`Mismatching \`as\` and \`href\` failed to manually provide ` +
`the params: ${missingParams.join(
', '
)} in the \`href\`'s \`query\``
)
}

return reject(
new Error(
`The provided \`as\` value (${asPathname}) is incompatible with the \`href\` value (${route}). ` +
`Read more: https://err.sh/zeit/next.js/incompatible-href-as`
)
)
}
return resolve(false)
} else {
// Merge params into `query`, overwriting any specified in search
Object.assign(query, routeMatch)
}

// Merge params into `query`, overwriting any specified in search
Object.assign(query, routeMatch)
}

Router.events.emit('routeChangeStart', as)
Expand Down
1 change: 1 addition & 0 deletions test/integration/invalid-href/pages/[post].js
@@ -0,0 +1 @@
export default () => 'hi from post'
@@ -0,0 +1,7 @@
import Link from 'next/link'

export default () => (
<Link href="/[post]?post=post-1" as="/blog/post-1">
<a>Click me</a>
</Link>
)
68 changes: 65 additions & 3 deletions test/integration/invalid-href/test/index.test.js
Expand Up @@ -20,13 +20,45 @@ const appDir = join(__dirname, '..')
const firstErrorRegex = /Invalid href passed to router: mailto:idk@idk.com.*invalid-href-passed/
const secondErrorRegex = /Invalid href passed to router: .*google\.com.*invalid-href-passed/

const showsError = async (pathname, regex, click = false) => {
const showsError = async (
pathname,
regex,
click = false,
isWarn = false,
cb
) => {
const browser = await webdriver(appPort, pathname)
if (isWarn) {
await browser.eval(`(function() {
window.warnLogs = []
var origWarn = window.console.warn
window.console.warn = function() {
var warnStr = ''
for (var i = 0; i < arguments.length; i++) {
if (i > 0) warnStr += ' ';
warnStr += arguments[i]
}
window.warnLogs.push(warnStr)
origWarn.apply(undefined, arguments)
}
})()`)
}

if (click) {
await browser.elementByCss('a').click()
}
const errorContent = await getReactErrorOverlayContent(browser)
expect(errorContent).toMatch(regex)
if (isWarn) {
await waitFor(2000)
const warnLogs = await browser.eval('window.warnLogs')
console.log(warnLogs)
expect(warnLogs.some(log => log.match(regex))).toBe(true)
} else {
const errorContent = await getReactErrorOverlayContent(browser)
expect(errorContent).toMatch(regex)
}

if (cb) await cb(browser)

await browser.close()
}

Expand Down Expand Up @@ -89,6 +121,16 @@ describe('Invalid hrefs', () => {
/The provided `as` value \(\/blog\/post-1\) is incompatible with the `href` value \(\/\[post\]\)/,
true
)
await showsError(
'/dynamic-route-mismatch',
/Mismatching `as` and `href` failed to manually provide the params: post in the `href`'s `query`/,
true,
true
)
})

it('does not throw error when dynamic route mismatch is used on Link and params are manually provided', async () => {
await noError('/dynamic-route-mismatch-manual', true)
})
})

Expand Down Expand Up @@ -123,5 +165,25 @@ describe('Invalid hrefs', () => {
it('does not show error in production when https://google.com is used as href on router.replace', async () => {
await noError('/second?method=replace', true)
})

it('shows error when dynamic route mismatch is used on Link', async () => {
const browser = await webdriver(appPort, '/dynamic-route-mismatch')
await browser.eval(`(function() {
window.caughtErrors = []
window.addEventListener('unhandledrejection', (error) => {
window.caughtErrors.push(error.reason.message)
})
})()`)
await browser.elementByCss('a').click()
await waitFor(500)
const errors = await browser.eval('window.caughtErrors')
expect(
errors.find(err =>
err.includes(
'The provided `as` value (/blog/post-1) is incompatible with the `href` value (/[post]). Read more: https://err.sh/zeit/next.js/incompatible-href-as'
)
)
).toBeTruthy()
})
})
})

0 comments on commit fcfb595

Please sign in to comment.