diff --git a/packages/next/build/webpack/plugins/middleware-plugin.ts b/packages/next/build/webpack/plugins/middleware-plugin.ts index 82b94f8b4101..6711ade2f916 100644 --- a/packages/next/build/webpack/plugins/middleware-plugin.ts +++ b/packages/next/build/webpack/plugins/middleware-plugin.ts @@ -46,12 +46,6 @@ interface EntryMetadata { } const NAME = 'MiddlewarePlugin' -const middlewareManifest: MiddlewareManifest = { - sortedMiddleware: [], - middleware: {}, - functions: {}, - version: 2, -} /** * Checks the value of usingIndirectEval and when it is a set of modules it @@ -121,6 +115,12 @@ function getCreateAssets(params: { }) { const { compilation, metadataByEntry } = params return (assets: any) => { + const middlewareManifest: MiddlewareManifest = { + sortedMiddleware: [], + middleware: {}, + functions: {}, + version: 2, + } for (const entrypoint of compilation.entrypoints.values()) { if (!entrypoint.name) { continue diff --git a/packages/next/server/dev/on-demand-entry-handler.ts b/packages/next/server/dev/on-demand-entry-handler.ts index 1c752b7b296d..4784fcf4489f 100644 --- a/packages/next/server/dev/on-demand-entry-handler.ts +++ b/packages/next/server/dev/on-demand-entry-handler.ts @@ -649,12 +649,22 @@ export function onDemandEntryHandler({ }, onServer: () => { added.set(COMPILER_NAMES.server, addEntry(COMPILER_NAMES.server)) + const edgeServerEntry = `${COMPILER_NAMES.edgeServer}${pagePathData.page}` + if (entries[edgeServerEntry]) { + // Runtime switched from edge to server + delete entries[edgeServerEntry] + } }, onEdgeServer: () => { added.set( COMPILER_NAMES.edgeServer, addEntry(COMPILER_NAMES.edgeServer) ) + const serverEntry = `${COMPILER_NAMES.server}${pagePathData.page}` + if (entries[serverEntry]) { + // Runtime switched from server to edge + delete entries[serverEntry] + } }, }) diff --git a/test/e2e/switchable-runtime/index.test.ts b/test/e2e/switchable-runtime/index.test.ts index b9cef7819887..5485116ebc5c 100644 --- a/test/e2e/switchable-runtime/index.test.ts +++ b/test/e2e/switchable-runtime/index.test.ts @@ -203,6 +203,190 @@ describe('Switchable runtime', () => { } }) + it('should be possible to switch between runtimes in API routes', async () => { + await check( + () => renderViaHTTP(next.url, '/api/switch-in-dev'), + 'server response' + ) + + // Edge + await next.patchFile( + 'pages/api/switch-in-dev.js', + ` + export const config = { + runtime: 'experimental-edge', + } + + export default () => new Response('edge response') + ` + ) + await check( + () => renderViaHTTP(next.url, '/api/switch-in-dev'), + 'edge response' + ) + + // Server + await next.patchFile( + 'pages/api/switch-in-dev.js', + ` + export default function (req, res) { + res.send('server response again') + } + ` + ) + await check( + () => renderViaHTTP(next.url, '/api/switch-in-dev'), + 'server response again' + ) + + // Edge + await next.patchFile( + 'pages/api/switch-in-dev.js', + ` + export const config = { + runtime: 'experimental-edge', + } + + export default () => new Response('edge response again') + ` + ) + await check( + () => renderViaHTTP(next.url, '/api/switch-in-dev'), + 'edge response again' + ) + }) + + it('should be possible to switch between runtimes in pages', async () => { + await check( + () => renderViaHTTP(next.url, '/switch-in-dev'), + /Hello from edge page/ + ) + + // Server + await next.patchFile( + 'pages/switch-in-dev.js', + ` + export default function Page() { + return

Hello from server page

+ } + ` + ) + await check( + () => renderViaHTTP(next.url, '/switch-in-dev'), + /Hello from server page/ + ) + + // Edge + await next.patchFile( + 'pages/switch-in-dev.js', + ` + export default function Page() { + return

Hello from edge page again

+ } + + export const config = { + runtime: 'experimental-edge', + } + ` + ) + await check( + () => renderViaHTTP(next.url, '/switch-in-dev'), + /Hello from edge page again/ + ) + + // Server + await next.patchFile( + 'pages/switch-in-dev.js', + ` + export default function Page() { + return

Hello from server page again

+ } + ` + ) + await check( + () => renderViaHTTP(next.url, '/switch-in-dev'), + /Hello from server page again/ + ) + }) + + // Doesn't work, see https://github.com/vercel/next.js/pull/39327 + it.skip('should be possible to switch between runtimes with same content', async () => { + const fileContent = await next.readFile( + 'pages/api/switch-in-dev-same-content.js' + ) + console.log({ fileContent }) + await check( + () => renderViaHTTP(next.url, '/api/switch-in-dev-same-content'), + 'server response' + ) + + // Edge + await next.patchFile( + 'pages/api/switch-in-dev-same-content.js', + ` + export const config = { + runtime: 'experimental-edge', + } + + export default () => new Response('edge response') + ` + ) + await check( + () => renderViaHTTP(next.url, '/api/switch-in-dev-same-content'), + 'edge response' + ) + + // Server - same content as first compilation of the server runtime version + await next.patchFile( + 'pages/api/switch-in-dev-same-content.js', + fileContent + ) + await check( + () => renderViaHTTP(next.url, '/api/switch-in-dev-same-content'), + 'server response' + ) + }) + + it('should recover from syntax error when using edge runtime', async () => { + await check( + () => renderViaHTTP(next.url, '/api/syntax-error-in-dev'), + 'edge response' + ) + + // Syntax error + await next.patchFile( + 'pages/api/syntax-error-in-dev.js', + ` + export const config = { + runtime: 'experimental-edge', + } + + export default => new Response('edge response') + ` + ) + await check( + () => renderViaHTTP(next.url, '/api/syntax-error-in-dev'), + /Unexpected token/ + ) + + // Fix syntax error + await next.patchFile( + 'pages/api/syntax-error-in-dev.js', + ` + export default () => new Response('edge response again') + + export const config = { + runtime: 'experimental-edge', + } + + ` + ) + await check( + () => renderViaHTTP(next.url, '/api/syntax-error-in-dev'), + 'edge response again' + ) + }) + it('should not crash the dev server when invalid runtime is configured', async () => { await check( () => renderViaHTTP(next.url, '/invalid-runtime'), diff --git a/test/e2e/switchable-runtime/pages/api/switch-in-dev-same-content.js b/test/e2e/switchable-runtime/pages/api/switch-in-dev-same-content.js new file mode 100644 index 000000000000..a587c8cb1a71 --- /dev/null +++ b/test/e2e/switchable-runtime/pages/api/switch-in-dev-same-content.js @@ -0,0 +1,3 @@ +export default (req, res) => { + res.send('server response') +} diff --git a/test/e2e/switchable-runtime/pages/api/switch-in-dev.js b/test/e2e/switchable-runtime/pages/api/switch-in-dev.js new file mode 100644 index 000000000000..a587c8cb1a71 --- /dev/null +++ b/test/e2e/switchable-runtime/pages/api/switch-in-dev.js @@ -0,0 +1,3 @@ +export default (req, res) => { + res.send('server response') +} diff --git a/test/e2e/switchable-runtime/pages/api/syntax-error-in-dev.js b/test/e2e/switchable-runtime/pages/api/syntax-error-in-dev.js new file mode 100644 index 000000000000..3b7243a1205e --- /dev/null +++ b/test/e2e/switchable-runtime/pages/api/syntax-error-in-dev.js @@ -0,0 +1,5 @@ +export default () => new Response('edge response') + +export const config = { + runtime: `experimental-edge`, +} diff --git a/test/e2e/switchable-runtime/pages/switch-in-dev.js b/test/e2e/switchable-runtime/pages/switch-in-dev.js new file mode 100644 index 000000000000..b67d4cabd53c --- /dev/null +++ b/test/e2e/switchable-runtime/pages/switch-in-dev.js @@ -0,0 +1,7 @@ +export default function Page() { + return

Hello from edge page

+} + +export const config = { + runtime: 'experimental-edge', +}