Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow mismatching href and as when manually provided #9837

Merged
merged 11 commits into from Jan 20, 2020
34 changes: 22 additions & 12 deletions packages/next/next-server/lib/router/router.ts
Expand Up @@ -316,22 +316,32 @@ 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)
console.warn(
ijjk marked this conversation as resolved.
Show resolved Hide resolved
ijjk marked this conversation as resolved.
Show resolved Hide resolved
`The provided \`as\` value (${asPathname}) is incompatible with the \`href\` value (${route}). ` +
`Read more: https://err.sh/zeit/next.js/incompatible-href-as`
)

const missingParams = Object.keys(routeRegex.groups).filter(
param => !query[param]
)

if (missingParams.length > 0) {
ijjk marked this conversation as resolved.
Show resolved Hide resolved
throw new Error(
`Mismatching \`as\` and \`href\` failed to manually provide ` +
`the params: ${missingParams.join(
', '
)} in the \`href\`'s \`query\``
)
}
}
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>
)
39 changes: 36 additions & 3 deletions test/integration/invalid-href/test/index.test.js
Expand Up @@ -20,13 +20,36 @@ 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) => {
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)
}
await browser.close()
}

Expand Down Expand Up @@ -87,8 +110,18 @@ describe('Invalid hrefs', () => {
await showsError(
'/dynamic-route-mismatch',
/The provided `as` value \(\/blog\/post-1\) is incompatible with the `href` value \(\/\[post\]\)/,
true,
true
)
await showsError(
'/dynamic-route-mismatch',
/Mismatching `as` and `href` failed to manually provide the params: post in the `href`'s `query`/,
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