Skip to content

Commit

Permalink
make router.prefetch throw when given invalid URL, but don't throw in…
Browse files Browse the repository at this point in the history
… Link
  • Loading branch information
lubieowoce committed Jun 11, 2024
1 parent 8e75fd3 commit 933f6c9
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 17 deletions.
5 changes: 2 additions & 3 deletions packages/next/src/client/components/app-router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -369,11 +369,10 @@ function Router({
let url: URL
try {
url = new URL(addBasePath(href), window.location.href)
} catch (err) {
console.error(
} catch (_) {
throw new Error(
`Cannot prefetch '${href}' because it cannot be converted to a URL.`
)
return
}

// Don't prefetch during development (improves compilation performance)
Expand Down
14 changes: 10 additions & 4 deletions packages/next/src/client/link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,15 +161,21 @@ function prefetch(
prefetched.add(prefetchedKey)
}

const prefetchPromise = isAppRouter
? (router as AppRouterInstance).prefetch(href, appOptions)
: (router as NextRouter).prefetch(href, as, options)
const doPrefetch = async () => {
if (isAppRouter) {
// note that `appRouter.prefetch()` is currently sync,
// so we have to wrap this call in an async function to be able to catch() errors below.
return (router as AppRouterInstance).prefetch(href, appOptions)
} else {
return (router as NextRouter).prefetch(href, as, options)
}
}

// Prefetch the JSON page if asked (only in the client)
// We need to handle a prefetch error here since we may be
// loading with priority which can reject but we don't
// want to force navigation since this is only a prefetch
Promise.resolve(prefetchPromise).catch((err) => {
doPrefetch().catch((err) => {
if (process.env.NODE_ENV !== 'production') {
// rethrow to show invalid URL errors
throw err
Expand Down
4 changes: 4 additions & 0 deletions test/e2e/app-dir/app-prefetch/app/invalid-url/error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
'use client'
export default function Error() {
return <h1>A prefetch threw an error</h1>
}
22 changes: 12 additions & 10 deletions test/e2e/app-dir/app-prefetch/prefetching.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,19 +330,21 @@ describe('app dir - prefetching', () => {
})
})

describe('should not crash for invalid URLs', () => {
it.each([
{ title: 'passed to Link', path: '/invalid-url/from-link' },
{
title: 'passed to router.prefetch',
path: '/invalid-url/from-router-prefetch',
},
])('$title', async ({ path }) => {
const browser = await next.browser(path)
describe('invalid URLs', () => {
it('should not throw when an invalid URL is passed to Link', async () => {
const browser = await next.browser('/invalid-url/from-link')

await check(() => browser.hasElementByCssSelector('h1'), true)

expect(await browser.elementByCss('h1').text()).toEqual('Hello, world!')
})

it('should throw when an invalid URL is passed to router.prefetch', async () => {
const browser = await next.browser('/invalid-url/from-router-prefetch')

await check(() => browser.hasElementByCssSelector('h1'), true)
expect(await browser.elementByCss('h1').text()).toEqual(
'A prefetch threw an error'
)
})
})
})

0 comments on commit 933f6c9

Please sign in to comment.