From a760d3de5d624ecc1ca0bc51af8027f843df4d55 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Fri, 22 Sep 2023 23:14:13 +0200 Subject: [PATCH 1/5] Loose types of app routes return value --- .../plugins/next-types-plugin/index.ts | 4 +- .../api-route-errors/app/pages/api/error.js | 3 - .../app/pages/api/uncaught-exception.js | 6 -- .../app/pages/api/unhandled-rejection.js | 4 -- .../api-route-errors/index.test.ts | 67 ------------------- .../app/lowercase/delete/route.js | 1 + .../app/lowercase/get/route.js | 1 + .../app/lowercase/head/route.js | 1 + .../app/lowercase/options/route.js | 1 + .../app/lowercase/patch/route.js | 1 + .../app/lowercase/post/route.js | 1 + .../app/lowercase/put/route.js | 1 + .../app-dir/app-routes-error/hello.js | 15 +++++ .../app-dir/app-routes-error/index.test.ts | 40 +++++++++++ .../app-routes/app-custom-routes.test.ts | 52 +++++--------- .../app-dir/app-routes/app/default/route.ts | 1 - .../app-routes/app/lowercase/delete/route.ts | 1 - .../app-routes/app/lowercase/get/route.ts | 1 - .../app-routes/app/lowercase/head/route.ts | 1 - .../app-routes/app/lowercase/options/route.ts | 1 - .../app-routes/app/lowercase/patch/route.ts | 1 - .../app-routes/app/lowercase/post/route.ts | 1 - .../app-routes/app/lowercase/put/route.ts | 1 - .../app-routes/app/mixed-response/route.ts | 12 ++++ test/e2e/app-dir/app-routes/next.config.js | 6 +- 25 files changed, 93 insertions(+), 131 deletions(-) delete mode 100644 test/development/api-route-errors/app/pages/api/error.js delete mode 100644 test/development/api-route-errors/app/pages/api/uncaught-exception.js delete mode 100644 test/development/api-route-errors/app/pages/api/unhandled-rejection.js delete mode 100644 test/development/api-route-errors/index.test.ts create mode 100644 test/development/app-dir/app-routes-error/app/lowercase/delete/route.js create mode 100644 test/development/app-dir/app-routes-error/app/lowercase/get/route.js create mode 100644 test/development/app-dir/app-routes-error/app/lowercase/head/route.js create mode 100644 test/development/app-dir/app-routes-error/app/lowercase/options/route.js create mode 100644 test/development/app-dir/app-routes-error/app/lowercase/patch/route.js create mode 100644 test/development/app-dir/app-routes-error/app/lowercase/post/route.js create mode 100644 test/development/app-dir/app-routes-error/app/lowercase/put/route.js create mode 100644 test/development/app-dir/app-routes-error/hello.js create mode 100644 test/development/app-dir/app-routes-error/index.test.ts delete mode 100644 test/e2e/app-dir/app-routes/app/default/route.ts delete mode 100644 test/e2e/app-dir/app-routes/app/lowercase/delete/route.ts delete mode 100644 test/e2e/app-dir/app-routes/app/lowercase/get/route.ts delete mode 100644 test/e2e/app-dir/app-routes/app/lowercase/head/route.ts delete mode 100644 test/e2e/app-dir/app-routes/app/lowercase/options/route.ts delete mode 100644 test/e2e/app-dir/app-routes/app/lowercase/patch/route.ts delete mode 100644 test/e2e/app-dir/app-routes/app/lowercase/post/route.ts delete mode 100644 test/e2e/app-dir/app-routes/app/lowercase/put/route.ts create mode 100644 test/e2e/app-dir/app-routes/app/mixed-response/route.ts diff --git a/packages/next/src/build/webpack/plugins/next-types-plugin/index.ts b/packages/next/src/build/webpack/plugins/next-types-plugin/index.ts index 3d98e30cf6501..9196175fb202e 100644 --- a/packages/next/src/build/webpack/plugins/next-types-plugin/index.ts +++ b/packages/next/src/build/webpack/plugins/next-types-plugin/index.ts @@ -111,7 +111,7 @@ if ('${method}' in entry) { Diff< { __tag__: '${method}', - __return_type__: Response | Promise + __return_type__: Response | void | never | Promise }, { __tag__: '${method}', @@ -428,7 +428,7 @@ declare module 'next/link' { import type { LinkProps as OriginalLinkProps } from 'next/dist/client/link.js' import type { AnchorHTMLAttributes, DetailedHTMLProps } from 'react' import type { UrlObject } from 'url' - + type LinkRestProps = Omit< Omit< DetailedHTMLProps< diff --git a/test/development/api-route-errors/app/pages/api/error.js b/test/development/api-route-errors/app/pages/api/error.js deleted file mode 100644 index 62270eec6b0b7..0000000000000 --- a/test/development/api-route-errors/app/pages/api/error.js +++ /dev/null @@ -1,3 +0,0 @@ -export default function error(req, res) { - throw new Error() -} diff --git a/test/development/api-route-errors/app/pages/api/uncaught-exception.js b/test/development/api-route-errors/app/pages/api/uncaught-exception.js deleted file mode 100644 index 847bc854279f5..0000000000000 --- a/test/development/api-route-errors/app/pages/api/uncaught-exception.js +++ /dev/null @@ -1,6 +0,0 @@ -export default function uncaughtException(req, res) { - setTimeout(() => { - throw new Error('uncaught exception') - }, 0) - res.send('hello') -} diff --git a/test/development/api-route-errors/app/pages/api/unhandled-rejection.js b/test/development/api-route-errors/app/pages/api/unhandled-rejection.js deleted file mode 100644 index 3a17a62880aec..0000000000000 --- a/test/development/api-route-errors/app/pages/api/unhandled-rejection.js +++ /dev/null @@ -1,4 +0,0 @@ -export default function unhandledRejection(req, res) { - Promise.reject(new Error('unhandled rejection')) - res.send('hello') -} diff --git a/test/development/api-route-errors/index.test.ts b/test/development/api-route-errors/index.test.ts deleted file mode 100644 index a02e76893c833..0000000000000 --- a/test/development/api-route-errors/index.test.ts +++ /dev/null @@ -1,67 +0,0 @@ -import stripAnsi from 'next/dist/compiled/strip-ansi' -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'test/lib/next-modes/base' -import { check, renderViaHTTP } from 'next-test-utils' -import { join } from 'path' - -describe('api-route-errors cli output', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(join(__dirname, 'app/pages')), - }, - dependencies: {}, - }) - }) - afterAll(() => next.destroy()) - - test('error', async () => { - const outputIndex = next.cliOutput.length - await renderViaHTTP(next.url, '/api/error') - await check(() => next.cliOutput.slice(outputIndex), /pages\/api/) - - const output = stripAnsi(next.cliOutput.slice(outputIndex)) - // Location - expect(output).toContain('pages/api/error.js (2:8) @ error') - // Stack - expect(output).toContain('pages/api/error.js:6:11') - // Source code - expect(output).toContain('1 | export default function error(req, res) {') - }) - - test('uncaught exception', async () => { - const outputIndex = next.cliOutput.length - await renderViaHTTP(next.url, '/api/uncaught-exception') - await check(() => next.cliOutput.slice(outputIndex), /pages\/api/) - - const output = stripAnsi(next.cliOutput.slice(outputIndex)) - // Location - expect(output).toContain('pages/api/uncaught-exception.js (3:10) @ Timeout') - // Stack - expect(output).toContain('pages/api/uncaught-exception.js:7:15') - // Source code - expect(output).toContain( - '1 | export default function uncaughtException(req, res) {' - ) - }) - - test('unhandled rejection', async () => { - const outputIndex = next.cliOutput.length - await renderViaHTTP(next.url, '/api/unhandled-rejection') - await check(() => next.cliOutput.slice(outputIndex), /pages\/api/) - - const output = stripAnsi(next.cliOutput.slice(outputIndex)) - // Location - expect(output).toContain( - 'pages/api/unhandled-rejection.js (2:17) @ unhandledRejection' - ) - // Stack - expect(output).toContain('pages/api/unhandled-rejection.js:6:20') - // Source code - expect(output).toContain( - '1 | export default function unhandledRejection(req, res) ' - ) - }) -}) diff --git a/test/development/app-dir/app-routes-error/app/lowercase/delete/route.js b/test/development/app-dir/app-routes-error/app/lowercase/delete/route.js new file mode 100644 index 0000000000000..91a5652eeaa18 --- /dev/null +++ b/test/development/app-dir/app-routes-error/app/lowercase/delete/route.js @@ -0,0 +1 @@ +export { DELETE as delete } from '../../../hello' diff --git a/test/development/app-dir/app-routes-error/app/lowercase/get/route.js b/test/development/app-dir/app-routes-error/app/lowercase/get/route.js new file mode 100644 index 0000000000000..1cea4e8ff38fa --- /dev/null +++ b/test/development/app-dir/app-routes-error/app/lowercase/get/route.js @@ -0,0 +1 @@ +export { GET as get } from '../../../hello' diff --git a/test/development/app-dir/app-routes-error/app/lowercase/head/route.js b/test/development/app-dir/app-routes-error/app/lowercase/head/route.js new file mode 100644 index 0000000000000..07fbba4929097 --- /dev/null +++ b/test/development/app-dir/app-routes-error/app/lowercase/head/route.js @@ -0,0 +1 @@ +export { HEAD as head } from '../../../hello' diff --git a/test/development/app-dir/app-routes-error/app/lowercase/options/route.js b/test/development/app-dir/app-routes-error/app/lowercase/options/route.js new file mode 100644 index 0000000000000..d96dc0220013c --- /dev/null +++ b/test/development/app-dir/app-routes-error/app/lowercase/options/route.js @@ -0,0 +1 @@ +export { OPTIONS as options } from '../../../hello' diff --git a/test/development/app-dir/app-routes-error/app/lowercase/patch/route.js b/test/development/app-dir/app-routes-error/app/lowercase/patch/route.js new file mode 100644 index 0000000000000..3127323743bf9 --- /dev/null +++ b/test/development/app-dir/app-routes-error/app/lowercase/patch/route.js @@ -0,0 +1 @@ +export { PATCH as patch } from '../../../hello' diff --git a/test/development/app-dir/app-routes-error/app/lowercase/post/route.js b/test/development/app-dir/app-routes-error/app/lowercase/post/route.js new file mode 100644 index 0000000000000..10e243a309189 --- /dev/null +++ b/test/development/app-dir/app-routes-error/app/lowercase/post/route.js @@ -0,0 +1 @@ +export { POST as post } from '../../../hello' diff --git a/test/development/app-dir/app-routes-error/app/lowercase/put/route.js b/test/development/app-dir/app-routes-error/app/lowercase/put/route.js new file mode 100644 index 0000000000000..0faf51fe7d1dc --- /dev/null +++ b/test/development/app-dir/app-routes-error/app/lowercase/put/route.js @@ -0,0 +1 @@ +export { PUT as put } from '../../../hello' diff --git a/test/development/app-dir/app-routes-error/hello.js b/test/development/app-dir/app-routes-error/hello.js new file mode 100644 index 0000000000000..12c1f6e39474b --- /dev/null +++ b/test/development/app-dir/app-routes-error/hello.js @@ -0,0 +1,15 @@ +const helloHandler = async () => { + if (typeof WebSocket === 'undefined') { + throw new Error('missing WebSocket constructor!!') + } + + return new Response('hello, world') +} + +export const GET = helloHandler +export const HEAD = helloHandler +export const OPTIONS = helloHandler +export const POST = helloHandler +export const PUT = helloHandler +export const DELETE = helloHandler +export const PATCH = helloHandler diff --git a/test/development/app-dir/app-routes-error/index.test.ts b/test/development/app-dir/app-routes-error/index.test.ts new file mode 100644 index 0000000000000..30cb5e670bcab --- /dev/null +++ b/test/development/app-dir/app-routes-error/index.test.ts @@ -0,0 +1,40 @@ +import { createNextDescribe } from 'e2e-utils' +import { check } from 'next-test-utils' + +createNextDescribe( + 'app-dir - app routes errors', + { + files: __dirname, + }, + ({ next }) => { + describe('bad lowercase exports', () => { + it.each([ + ['get'], + ['head'], + ['options'], + ['post'], + ['put'], + ['delete'], + ['patch'], + ])( + 'should print an error when using lowercase %p in dev', + async (method: string) => { + await next.fetch('/lowercase/' + method) + + await check(() => { + expect(next.cliOutput).toContain( + `Detected lowercase method '${method}' in` + ) + expect(next.cliOutput).toContain( + `Export the uppercase '${method.toUpperCase()}' method name to fix this error.` + ) + expect(next.cliOutput).toMatch( + /Detected lowercase method '.+' in '.+\/route\.ts'\. Export the uppercase '.+' method name to fix this error\./ + ) + return 'yes' + }, 'yes') + } + ) + }) + } +) diff --git a/test/e2e/app-dir/app-routes/app-custom-routes.test.ts b/test/e2e/app-dir/app-routes/app-custom-routes.test.ts index 83596d1bcc04a..cc151af7feea0 100644 --- a/test/e2e/app-dir/app-routes/app-custom-routes.test.ts +++ b/test/e2e/app-dir/app-routes/app-custom-routes.test.ts @@ -589,45 +589,25 @@ createNextDescribe( }) if (isNextDev) { - describe('lowercase exports', () => { - it.each([ - ['get'], - ['head'], - ['options'], - ['post'], - ['put'], - ['delete'], - ['patch'], - ])( - 'should print an error when using lowercase %p in dev', - async (method: string) => { - await next.fetch(basePath + '/lowercase/' + method) - - await check(() => { - expect(next.cliOutput).toContain( - `Detected lowercase method '${method}' in` - ) - expect(next.cliOutput).toContain( - `Export the uppercase '${method.toUpperCase()}' method name to fix this error.` - ) - expect(next.cliOutput).toMatch( - /Detected lowercase method '.+' in '.+\/route\.ts'\. Export the uppercase '.+' method name to fix this error\./ - ) - return 'yes' - }, 'yes') - } - ) - }) - describe('invalid exports', () => { + beforeAll(async () => { + await next.patchFile( + 'app/default/route.ts', + `\ + export { GET as default } from '../../handlers/hello' + ` + ) + }) + afterAll(async () => { + await next.deleteFile('app/default/route.ts') + }) it('should print an error when exporting a default handler in dev', async () => { - const res = await next.fetch(basePath + '/default') - - // Ensure we get a 405 (Method Not Allowed) response when there is no - // exported handler for the GET method. - expect(res.status).toEqual(405) + await check(async () => { + const res = await next.fetch(basePath + '/default') - await check(() => { + // Ensure we get a 405 (Method Not Allowed) response when there is no + // exported handler for the GET method. + expect(res.status).toEqual(405) expect(next.cliOutput).toMatch( /Detected default export in '.+\/route\.ts'\. Export a named export for each HTTP method instead\./ ) diff --git a/test/e2e/app-dir/app-routes/app/default/route.ts b/test/e2e/app-dir/app-routes/app/default/route.ts deleted file mode 100644 index 83aee80041deb..0000000000000 --- a/test/e2e/app-dir/app-routes/app/default/route.ts +++ /dev/null @@ -1 +0,0 @@ -export { GET as default } from '../../handlers/hello' diff --git a/test/e2e/app-dir/app-routes/app/lowercase/delete/route.ts b/test/e2e/app-dir/app-routes/app/lowercase/delete/route.ts deleted file mode 100644 index c2f8249b31c69..0000000000000 --- a/test/e2e/app-dir/app-routes/app/lowercase/delete/route.ts +++ /dev/null @@ -1 +0,0 @@ -export { DELETE as delete } from '../../../handlers/hello' diff --git a/test/e2e/app-dir/app-routes/app/lowercase/get/route.ts b/test/e2e/app-dir/app-routes/app/lowercase/get/route.ts deleted file mode 100644 index 7735df10a0b69..0000000000000 --- a/test/e2e/app-dir/app-routes/app/lowercase/get/route.ts +++ /dev/null @@ -1 +0,0 @@ -export { GET as get } from '../../../handlers/hello' diff --git a/test/e2e/app-dir/app-routes/app/lowercase/head/route.ts b/test/e2e/app-dir/app-routes/app/lowercase/head/route.ts deleted file mode 100644 index 89f48c695a0d7..0000000000000 --- a/test/e2e/app-dir/app-routes/app/lowercase/head/route.ts +++ /dev/null @@ -1 +0,0 @@ -export { HEAD as head } from '../../../handlers/hello' diff --git a/test/e2e/app-dir/app-routes/app/lowercase/options/route.ts b/test/e2e/app-dir/app-routes/app/lowercase/options/route.ts deleted file mode 100644 index 59b63b1a3ad39..0000000000000 --- a/test/e2e/app-dir/app-routes/app/lowercase/options/route.ts +++ /dev/null @@ -1 +0,0 @@ -export { OPTIONS as options } from '../../../handlers/hello' diff --git a/test/e2e/app-dir/app-routes/app/lowercase/patch/route.ts b/test/e2e/app-dir/app-routes/app/lowercase/patch/route.ts deleted file mode 100644 index 91315bbf156db..0000000000000 --- a/test/e2e/app-dir/app-routes/app/lowercase/patch/route.ts +++ /dev/null @@ -1 +0,0 @@ -export { PATCH as patch } from '../../../handlers/hello' diff --git a/test/e2e/app-dir/app-routes/app/lowercase/post/route.ts b/test/e2e/app-dir/app-routes/app/lowercase/post/route.ts deleted file mode 100644 index 2340ade758519..0000000000000 --- a/test/e2e/app-dir/app-routes/app/lowercase/post/route.ts +++ /dev/null @@ -1 +0,0 @@ -export { POST as post } from '../../../handlers/hello' diff --git a/test/e2e/app-dir/app-routes/app/lowercase/put/route.ts b/test/e2e/app-dir/app-routes/app/lowercase/put/route.ts deleted file mode 100644 index b1cda20a07d42..0000000000000 --- a/test/e2e/app-dir/app-routes/app/lowercase/put/route.ts +++ /dev/null @@ -1 +0,0 @@ -export { PUT as put } from '../../../handlers/hello' diff --git a/test/e2e/app-dir/app-routes/app/mixed-response/route.ts b/test/e2e/app-dir/app-routes/app/mixed-response/route.ts new file mode 100644 index 0000000000000..d1207a641b918 --- /dev/null +++ b/test/e2e/app-dir/app-routes/app/mixed-response/route.ts @@ -0,0 +1,12 @@ +import { redirect } from 'next/navigation' +import { NextResponse } from 'next/server' + +export async function GET() { + if (process.env.COND_1) { + return NextResponse.json({ a: '1' }) + } else if (process.env.COND_2) { + redirect('/no-response') + } else { + return new Response('3') + } +} diff --git a/test/e2e/app-dir/app-routes/next.config.js b/test/e2e/app-dir/app-routes/next.config.js index 7670c4ad0ebb5..1bdf8c320e8a9 100644 --- a/test/e2e/app-dir/app-routes/next.config.js +++ b/test/e2e/app-dir/app-routes/next.config.js @@ -1,11 +1,7 @@ /** * @type {import('next').NextConfig} */ -const config = { - typescript: { - ignoreBuildErrors: true, - }, -} +const config = {} if (process.env.BASE_PATH) { config.basePath = process.env.BASE_PATH From 88c317e1924e562ec96e7386992caa167fc2a4c5 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Sat, 23 Sep 2023 00:38:29 +0200 Subject: [PATCH 2/5] fix test --- test/development/app-dir/app-routes-error/index.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/development/app-dir/app-routes-error/index.test.ts b/test/development/app-dir/app-routes-error/index.test.ts index 30cb5e670bcab..8e7c6ec3e5ccf 100644 --- a/test/development/app-dir/app-routes-error/index.test.ts +++ b/test/development/app-dir/app-routes-error/index.test.ts @@ -29,7 +29,7 @@ createNextDescribe( `Export the uppercase '${method.toUpperCase()}' method name to fix this error.` ) expect(next.cliOutput).toMatch( - /Detected lowercase method '.+' in '.+\/route\.ts'\. Export the uppercase '.+' method name to fix this error\./ + /Detected lowercase method '.+' in '.+\/route\.js'\. Export the uppercase '.+' method name to fix this error\./ ) return 'yes' }, 'yes') From 54d571386cf08ebef21ebed6af63f28e97f8b898 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Sat, 23 Sep 2023 00:51:19 +0200 Subject: [PATCH 3/5] fix type --- test/e2e/app-dir/app-routes/helpers.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/test/e2e/app-dir/app-routes/helpers.ts b/test/e2e/app-dir/app-routes/helpers.ts index 77df81d91164d..c87f0b3e3d07f 100644 --- a/test/e2e/app-dir/app-routes/helpers.ts +++ b/test/e2e/app-dir/app-routes/helpers.ts @@ -58,12 +58,7 @@ type Cookies = { * @returns any injected metadata on the request */ export function getRequestMeta( - headersOrCookies: - | Headers - | import('node-fetch').Headers - | Cookies - | ReadonlyHeaders - | ReadonlyRequestCookies + headersOrCookies: Headers | Cookies | ReadonlyHeaders | ReadonlyRequestCookies ): Record { const headerOrCookie = headersOrCookies.get(KEY) if (!headerOrCookie) return {} From f66d57bb5739a767a0e8f3f6e22e774172ea43a6 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Sat, 23 Sep 2023 01:18:00 +0200 Subject: [PATCH 4/5] fix type --- packages/next/src/trace/trace-uploader.ts | 1 - packages/next/types/misc.d.ts | 6 ++++++ test/e2e/app-dir/app-routes/helpers.ts | 7 ++++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/next/src/trace/trace-uploader.ts b/packages/next/src/trace/trace-uploader.ts index f6753ed636a57..04158097b1cfd 100644 --- a/packages/next/src/trace/trace-uploader.ts +++ b/packages/next/src/trace/trace-uploader.ts @@ -2,7 +2,6 @@ import findUp from 'next/dist/compiled/find-up' import fsPromise from 'fs/promises' import child_process from 'child_process' import assert from 'assert' -// @ts-ignore import fetch from 'next/dist/compiled/node-fetch' import os from 'os' import { createInterface } from 'readline' diff --git a/packages/next/types/misc.d.ts b/packages/next/types/misc.d.ts index e98312232711d..fdd55470a27af 100644 --- a/packages/next/types/misc.d.ts +++ b/packages/next/types/misc.d.ts @@ -43,6 +43,12 @@ declare module 'next/dist/compiled/@next/react-refresh-utils/dist/ReactRefreshWe export = m } +declare module 'next/dist/compiled/node-fetch' { + import fetch from 'node-fetch' + export * from 'node-fetch' + export default fetch +} + declare module 'next/dist/compiled/node-html-parser' { export * from 'node-html-parser' } diff --git a/test/e2e/app-dir/app-routes/helpers.ts b/test/e2e/app-dir/app-routes/helpers.ts index c87f0b3e3d07f..3ac89053a2da8 100644 --- a/test/e2e/app-dir/app-routes/helpers.ts +++ b/test/e2e/app-dir/app-routes/helpers.ts @@ -58,7 +58,12 @@ type Cookies = { * @returns any injected metadata on the request */ export function getRequestMeta( - headersOrCookies: Headers | Cookies | ReadonlyHeaders | ReadonlyRequestCookies + headersOrCookies: + | Headers + | Cookies + | ReadonlyHeaders + | ReadonlyRequestCookies + | import('next/dist/compiled/node-fetch').Headers ): Record { const headerOrCookie = headersOrCookies.get(KEY) if (!headerOrCookie) return {} From 8da9785f95b19d96bcd40458b4b43a3a4bad0b3d Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Sun, 24 Sep 2023 17:00:11 +0200 Subject: [PATCH 5/5] add comment --- .../src/build/webpack/plugins/next-types-plugin/index.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/next/src/build/webpack/plugins/next-types-plugin/index.ts b/packages/next/src/build/webpack/plugins/next-types-plugin/index.ts index 9196175fb202e..41576f1e9dddc 100644 --- a/packages/next/src/build/webpack/plugins/next-types-plugin/index.ts +++ b/packages/next/src/build/webpack/plugins/next-types-plugin/index.ts @@ -107,6 +107,12 @@ if ('${method}' in entry) { '${method}' > >() + ${ + '' + // Adding void to support never return type without explicit return: + // e.g. notFound() will interrupt the execution but the handler return type is inferred as void. + // x-ref: https://github.com/microsoft/TypeScript/issues/16608#issuecomment-309327984 + } checkFields< Diff< {