Skip to content

Commit

Permalink
Update error when internal href and external as are used (vercel#20658)
Browse files Browse the repository at this point in the history
* Update error when internal href and external as are used

* Update to handle error outside invariant

* Update err.sh components
  • Loading branch information
ijjk authored Jan 1, 2021
1 parent fad3256 commit 8b803d7
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 17 deletions.
32 changes: 18 additions & 14 deletions errors/incompatible-href-as.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,31 @@ Note: this error will only show when the `next/link` component is clicked not wh
```jsx
import Link from 'next/link'

export default () => (
<>
<Link href="/[post]" as="/post-1/comments">
<a>Invalid link</a>
</Link>
</>
)
export default function Page(props) {
return (
<>
<Link href="/[post]" as="/post-1/comments">
<a>Invalid link</a>
</Link>
</>
)
}
```

**Compatible `href` and `as`**

```jsx
import Link from 'next/link'

export default () => (
<>
<Link href="/[post]" as="/post-1">
<a>Valid link</a>
</Link>
</>
)
export default function Page(props) {
return (
<>
<Link href="/[post]" as="/post-1">
<a>Valid link</a>
</Link>
</>
)
}
```

#### Possible Ways to Fix It
Expand Down
47 changes: 47 additions & 0 deletions errors/invalid-relative-url-external-as.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Invalid relative `href` and external `as` values

#### Why This Error Occurred

Somewhere you are utilizing the `next/link` component, `Router#push`, or `Router#replace` with a relative route in your `href` that has an external `as` value. The `as` value must be relative also or only `href` should be used with an external URL.

Note: this error will only show when the `next/link` component is clicked not when only rendered.

**Incompatible `href` and `as`**

```jsx
import Link from 'next/link'

export default function Page(props) {
return (
<>
<Link href="/invalid" as="mailto:[email protected]">
<a>Invalid link</a>
</Link>
</>
)
}
```

**Compatible `href` and `as`**

```jsx
import Link from 'next/link'

export default function Page(props) {
return (
<>
<Link href="mailto:[email protected]">
<a>Invalid link</a>
</Link>
</>
)
}
```

#### Possible Ways to Fix It

Look for any usage of the `next/link` component, `Router#push`, or `Router#replace` and make sure that the provided `href` and `as` values are compatible

### Useful Links

- [Routing section in Documentation](https://nextjs.org/docs/routing/introduction)
15 changes: 14 additions & 1 deletion packages/next/next-server/lib/router/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -904,7 +904,7 @@ export default class Router implements BaseRouter {
// pages to allow building the data URL correctly
let resolvedAs = as

if (process.env.__NEXT_HAS_REWRITES) {
if (process.env.__NEXT_HAS_REWRITES && as.startsWith('/')) {
resolvedAs = resolveRewrites(
addBasePath(
addLocale(delBasePath(parseRelativeUrl(as).pathname), this.locale)
Expand Down Expand Up @@ -940,6 +940,19 @@ export default class Router implements BaseRouter {
}
}
}

if (!isLocalURL(as)) {
if (process.env.NODE_ENV !== 'production') {
throw new Error(
`Invalid href: "${url}" and as: "${as}", received relative href and external as` +
`\nSee more info: https://err.sh/next.js/invalid-relative-url-external-as`
)
}

window.location.href = as
return false
}

resolvedAs = delLocale(delBasePath(resolvedAs), this.locale)

if (isDynamicRoute(route)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function parseRelativeUrl(url: string, base?: string) {
resolvedBase
)
if (origin !== globalBase.origin) {
throw new Error('invariant: invalid relative URL')
throw new Error(`invariant: invalid relative URL, router received ${url}`)
}
return {
pathname,
Expand Down
2 changes: 1 addition & 1 deletion test/integration/build-output/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ describe('Build Output', () => {
expect(indexSize.endsWith('B')).toBe(true)

// should be no bigger than 62.2 kb
expect(parseFloat(indexFirstLoad)).toBeCloseTo(62.2, 1)
expect(parseFloat(indexFirstLoad)).toBeCloseTo(62.3, 1)
expect(indexFirstLoad.endsWith('kB')).toBe(true)

expect(parseFloat(err404Size) - 3.7).toBeLessThanOrEqual(0)
Expand Down
9 changes: 9 additions & 0 deletions test/integration/invalid-href/pages/invalid-relative.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Link from 'next/link'

export default function Page() {
return (
<Link href="/second" as="mailto:[email protected]">
<a id="click-me">email</a>
</Link>
)
}
12 changes: 12 additions & 0 deletions test/integration/invalid-href/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ describe('Invalid hrefs', () => {
await noError('/second')
})

it('does not show error when internal href is used with external as', async () => {
await noError('/invalid-relative', true)
})

it('shows error when dynamic route mismatch is used on Link', async () => {
const browser = await webdriver(appPort, '/dynamic-route-mismatch')
try {
Expand Down Expand Up @@ -166,6 +170,14 @@ describe('Invalid hrefs', () => {
)
})

it('shows error when internal href is used with external as', async () => {
await showsError(
'/invalid-relative',
/Invalid href: "\/second" and as: "mailto:hello@example\.com", received relative href and external as/,
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

0 comments on commit 8b803d7

Please sign in to comment.