From 7f2a41d8763337bc9d9aba15a358da3a28fdd6a9 Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Fri, 14 Oct 2022 20:53:16 +0200 Subject: [PATCH 1/5] Don't bubble up NEXT_REDIRECT and NEXT_NOT_FOUND to error overlay --- .../client/components/react-dev-overlay/hot-reloader.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/next/client/components/react-dev-overlay/hot-reloader.tsx b/packages/next/client/components/react-dev-overlay/hot-reloader.tsx index e5228b89ee7d8..543659a01b365 100644 --- a/packages/next/client/components/react-dev-overlay/hot-reloader.tsx +++ b/packages/next/client/components/react-dev-overlay/hot-reloader.tsx @@ -432,6 +432,13 @@ export default function HotReload({ }) const handleOnUnhandledError = useCallback((ev) => { + if ( + ev?.error?.digest.startsWith('NEXT_REDIRECT') || + ev?.error?.digest === 'NEXT_NOT_FOUND' + ) { + ev.preventDefault() + return + } hadRuntimeError = true onUnhandledError(dispatch, ev) }, []) From 57c9935f25e2f571bf37af9b7c7c41fe39c56ed2 Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Sat, 15 Oct 2022 14:21:12 +0200 Subject: [PATCH 2/5] Add test for meta tag --- test/e2e/app-dir/index.test.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/e2e/app-dir/index.test.ts b/test/e2e/app-dir/index.test.ts index 9d98f0400fec7..c47219d842bdf 100644 --- a/test/e2e/app-dir/index.test.ts +++ b/test/e2e/app-dir/index.test.ts @@ -1693,6 +1693,11 @@ describe('app dir', () => { expect( await browser.waitForElementByCss('#not-found-component').text() ).toBe('Not Found!') + expect( + await browser + .waitForElementByCss('meta[name="robots"]') + .getAttribute('content') + ).toBe('noindex') }) it.skip('should trigger not-found in a client component', async () => { @@ -1700,6 +1705,11 @@ describe('app dir', () => { expect( await browser.waitForElementByCss('#not-found-component').text() ).toBe('Not Found!') + expect( + await browser + .waitForElementByCss('meta[name="robots"]') + .getAttribute('content') + ).toBe('noindex') }) ;(isDev ? it.skip : it)( 'should trigger not-found client-side', @@ -1712,6 +1722,11 @@ describe('app dir', () => { expect( await browser.elementByCss('#not-found-component').text() ).toBe('Not Found!') + expect( + await browser + .waitForElementByCss('meta[name="robots"]') + .getAttribute('content') + ).toBe('noindex') } ) }) From 70b76f0d780dee6bd03fd782845481ee2e16a891 Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Sat, 15 Oct 2022 14:34:10 +0200 Subject: [PATCH 3/5] Add meta robots --- packages/next/client/components/layout-router.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/next/client/components/layout-router.tsx b/packages/next/client/components/layout-router.tsx index f19e762eebf63..64c7bfc40821d 100644 --- a/packages/next/client/components/layout-router.tsx +++ b/packages/next/client/components/layout-router.tsx @@ -348,7 +348,12 @@ class NotFoundErrorBoundary extends React.Component< render() { if (this.state.notFoundTriggered) { - return this.props.notFound + return ( + <> + + {this.props.notFound} + + ) } return this.props.children From ffaee68783ac5c5f82993364586bfe1090ec7345 Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Sat, 15 Oct 2022 14:49:20 +0200 Subject: [PATCH 4/5] Run redirect in useEffect of error boundary --- .../next/client/components/layout-router.tsx | 20 +++++----- .../clientcomponent/{a.js => page.js} | 0 test/e2e/app-dir/index.test.ts | 40 +++++++++---------- 3 files changed, 28 insertions(+), 32 deletions(-) rename test/e2e/app-dir/app/app/not-found/clientcomponent/{a.js => page.js} (100%) diff --git a/packages/next/client/components/layout-router.tsx b/packages/next/client/components/layout-router.tsx index 64c7bfc40821d..698b2e7ef8c42 100644 --- a/packages/next/client/components/layout-router.tsx +++ b/packages/next/client/components/layout-router.tsx @@ -278,9 +278,13 @@ interface RedirectBoundaryProps { children: React.ReactNode } -function InfinitePromiseComponent() { - use(createInfinitePromise()) - return <> +function HandleRedirect({ redirect }: { redirect: string }) { + const router = useContext(AppRouterContext) + + useEffect(() => { + router.replace(redirect, {}) + }, [redirect, router]) + return null } class RedirectErrorBoundary extends React.Component< @@ -297,20 +301,14 @@ class RedirectErrorBoundary extends React.Component< const url = error.digest.split(';')[1] return { redirect: url } } - // Re-throw if error is not for 404 + // Re-throw if error is not for redirect throw error } render() { const redirect = this.state.redirect if (redirect !== null) { - setTimeout(() => { - // @ts-ignore startTransition exists - React.startTransition(() => { - this.props.router.replace(redirect, {}) - }) - }) - return + return } return this.props.children diff --git a/test/e2e/app-dir/app/app/not-found/clientcomponent/a.js b/test/e2e/app-dir/app/app/not-found/clientcomponent/page.js similarity index 100% rename from test/e2e/app-dir/app/app/not-found/clientcomponent/a.js rename to test/e2e/app-dir/app/app/not-found/clientcomponent/page.js diff --git a/test/e2e/app-dir/index.test.ts b/test/e2e/app-dir/index.test.ts index c47219d842bdf..134dab910a10d 100644 --- a/test/e2e/app-dir/index.test.ts +++ b/test/e2e/app-dir/index.test.ts @@ -1700,7 +1700,7 @@ describe('app dir', () => { ).toBe('noindex') }) - it.skip('should trigger not-found in a client component', async () => { + it('should trigger not-found in a client component', async () => { const browser = await webdriver(next.url, '/not-found/clientcomponent') expect( await browser.waitForElementByCss('#not-found-component').text() @@ -1711,29 +1711,26 @@ describe('app dir', () => { .getAttribute('content') ).toBe('noindex') }) - ;(isDev ? it.skip : it)( - 'should trigger not-found client-side', - async () => { - const browser = await webdriver(next.url, '/not-found/client-side') + it('should trigger not-found client-side', async () => { + const browser = await webdriver(next.url, '/not-found/client-side') + await browser + .elementByCss('button') + .click() + .waitForElementByCss('#not-found-component') + expect(await browser.elementByCss('#not-found-component').text()).toBe( + 'Not Found!' + ) + expect( await browser - .elementByCss('button') - .click() - .waitForElementByCss('#not-found-component') - expect( - await browser.elementByCss('#not-found-component').text() - ).toBe('Not Found!') - expect( - await browser - .waitForElementByCss('meta[name="robots"]') - .getAttribute('content') - ).toBe('noindex') - } - ) + .waitForElementByCss('meta[name="robots"]') + .getAttribute('content') + ).toBe('noindex') + }) }) describe('redirect', () => { describe('components', () => { - it.skip('should redirect in a server component', async () => { + it('should redirect in a server component', async () => { const browser = await webdriver(next.url, '/redirect/servercomponent') await browser.waitForElementByCss('#result-page') expect(await browser.elementByCss('#result-page').text()).toBe( @@ -1741,7 +1738,7 @@ describe('app dir', () => { ) }) - it.skip('should redirect in a client component', async () => { + it('should redirect in a client component', async () => { const browser = await webdriver(next.url, '/redirect/clientcomponent') await browser.waitForElementByCss('#result-page') expect(await browser.elementByCss('#result-page').text()).toBe( @@ -1750,12 +1747,13 @@ describe('app dir', () => { }) // TODO-APP: Enable in development - ;(isDev ? it.skip : it)('should redirect client-side', async () => { + it('should redirect client-side', async () => { const browser = await webdriver(next.url, '/redirect/client-side') await browser .elementByCss('button') .click() .waitForElementByCss('#result-page') + // eslint-disable-next-line jest/no-standalone-expect expect(await browser.elementByCss('#result-page').text()).toBe( 'Result Page' ) From c0b352aec44291adaa870240873af3551b02ba42 Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Sat, 15 Oct 2022 17:52:35 +0200 Subject: [PATCH 5/5] Fix check for error.digest --- .../client/components/react-dev-overlay/hot-reloader.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/next/client/components/react-dev-overlay/hot-reloader.tsx b/packages/next/client/components/react-dev-overlay/hot-reloader.tsx index 654dc10785991..e7e90806662e8 100644 --- a/packages/next/client/components/react-dev-overlay/hot-reloader.tsx +++ b/packages/next/client/components/react-dev-overlay/hot-reloader.tsx @@ -446,12 +446,15 @@ export default function HotReload({ const handleOnUnhandledError = useCallback((ev) => { if ( - ev?.error?.digest.startsWith('NEXT_REDIRECT') || - ev?.error?.digest === 'NEXT_NOT_FOUND' + ev.error && + ev.error.digest && + (ev.error.digest.startsWith('NEXT_REDIRECT') || + ev.error.digest === 'NEXT_NOT_FOUND') ) { ev.preventDefault() return } + hadRuntimeError = true onUnhandledError(dispatch, ev) }, [])