From 794f1b9169bc4e1a2d7f93f302d2472e53959997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Born=C3=B6?= Date: Thu, 4 Aug 2022 15:31:24 +0200 Subject: [PATCH 01/13] Move middlewareManifest from module scope, should be revalidated each build --- .../next/build/webpack/plugins/middleware-plugin.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/next/build/webpack/plugins/middleware-plugin.ts b/packages/next/build/webpack/plugins/middleware-plugin.ts index ebdd8a173a35..04df4eb09e70 100644 --- a/packages/next/build/webpack/plugins/middleware-plugin.ts +++ b/packages/next/build/webpack/plugins/middleware-plugin.ts @@ -44,12 +44,6 @@ interface EntryMetadata { } const NAME = 'MiddlewarePlugin' -const middlewareManifest: MiddlewareManifest = { - sortedMiddleware: [], - middleware: {}, - functions: {}, - version: 1, -} export default class MiddlewarePlugin { dev: boolean @@ -550,6 +544,13 @@ function getCreateAssets(params: { }) { const { compilation, metadataByEntry } = params return (assets: any) => { + const middlewareManifest: MiddlewareManifest = { + sortedMiddleware: [], + middleware: {}, + functions: {}, + version: 1, + } + for (const entrypoint of compilation.entrypoints.values()) { if (!entrypoint.name) { continue From de26fafbe6f66e9af66f72c24f089f8868a0350d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Born=C3=B6?= Date: Thu, 4 Aug 2022 15:31:59 +0200 Subject: [PATCH 02/13] Invalidate server and edge when runtime has changed from one to the other --- packages/next/server/dev/hot-reloader.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/next/server/dev/hot-reloader.ts b/packages/next/server/dev/hot-reloader.ts index 996eca68a0e3..d2ff609d003d 100644 --- a/packages/next/server/dev/hot-reloader.ts +++ b/packages/next/server/dev/hot-reloader.ts @@ -29,6 +29,7 @@ import { findPageFile } from '../lib/find-page-file' import { BUILDING, entries, + getInvalidator, onDemandEntryHandler, } from './on-demand-entry-handler' import { denormalizePagePath } from '../../shared/lib/page-path/denormalize-page-path' @@ -587,6 +588,11 @@ export default class HotReloader { page, pageRuntime: staticInfo.runtime, onEdgeServer: () => { + if (entries[`server${page}`]) { + // Runtime switched from server to edge + delete entries[`server${page}`] + getInvalidator()?.invalidate(['server', 'edgeServer']) + } if (!isEdgeServerCompilation) return entries[pageKey].status = BUILDING entrypoints[bundlePath] = finalizeEntrypoint({ @@ -636,6 +642,11 @@ export default class HotReloader { } }, onServer: () => { + if (entries[`edge-server${page}`]) { + // Runtime switched from edge to server + delete entries[`edge-server${page}`] + getInvalidator()?.invalidate(['server', 'edgeServer']) + } if (!isNodeServerCompilation) return entries[pageKey].status = BUILDING let request = relative(config.context!, absolutePagePath) From a1fb10ba3c2cf31e95c1499a7bb291fa1adcaa4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Born=C3=B6?= Date: Thu, 4 Aug 2022 15:32:28 +0200 Subject: [PATCH 03/13] Tests for changing runtime --- .../development/edge-api-routes/index.test.ts | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 test/development/edge-api-routes/index.test.ts diff --git a/test/development/edge-api-routes/index.test.ts b/test/development/edge-api-routes/index.test.ts new file mode 100644 index 000000000000..2c41ca2f4042 --- /dev/null +++ b/test/development/edge-api-routes/index.test.ts @@ -0,0 +1,160 @@ +import { createNext } from 'e2e-utils' +import { NextInstance } from 'test/lib/next-modes/base' +import { renderViaHTTP } from 'next-test-utils' + +describe('edge-api-routes', () => { + let next: NextInstance + + beforeAll(async () => { + next = await createNext({ + files: { + 'pages/index.js': ` + export default function Page() { + return

hello world

+ } + `, + }, + dependencies: {}, + }) + }) + afterAll(() => next.destroy()) + + test('Switch between runtimes - edge first', async () => { + // Edge + await next.patchFile( + 'pages/api/hello.js', + ` + export const config = { + runtime: 'experimental-edge', + } + + export default () => new Response('edge response') + ` + ) + let response = await renderViaHTTP(next.url, '/api/hello') + expect(response).toBe('edge response') + + // Server + await next.patchFile( + 'pages/api/hello.js', + ` + export default function (req, res) { + res.send('server response') + } + ` + ) + response = await renderViaHTTP(next.url, '/api/hello') + expect(response).toBe('server response') + }) + + test('Switch between runtimes - server first', async () => { + // Server + await next.patchFile( + 'pages/api/hello2.js', + ` + export default function (req, res) { + res.send('server response') + } + ` + ) + let response = await renderViaHTTP(next.url, '/api/hello2') + expect(response).toBe('server response') + + // Edge + await next.patchFile( + 'pages/api/hello2.js', + ` + export const config = { + runtime: 'experimental-edge', + } + + export default () => new Response('edge response') + ` + ) + response = await renderViaHTTP(next.url, '/api/hello2') + expect(response).toBe('edge response') + }) + + test('Recover from syntax error', async () => { + await next.patchFile( + 'pages/api/hello3.js', + ` + export const config = { + runtime: 'experimental-edge', + } + + export default () => new Response('edge response') + ` + ) + let response = await renderViaHTTP(next.url, '/api/hello3') + expect(response).toBe('edge response') + + // Syntax error + await next.patchFile( + 'pages/api/hello3.js', + ` + export const config = { + runtime: 'experimental-edge', + } + + export default => new Response('edge response') + ` + ) + response = await renderViaHTTP(next.url, '/api/hello3') + expect(response).toInclude('Unexpected token') + + // Fix syntax error + await next.patchFile( + 'pages/api/hello3.js', + ` + export const config = { + runtime: 'experimental-edge', + } + + export default () => new Response('edge response 2') + ` + ) + response = await renderViaHTTP(next.url, '/api/hello3') + expect(response).toBe('edge response 2') + }) + + test('Switch to new runtime and then back ', async () => { + // Server + await next.patchFile( + 'pages/api/hello4.js', + ` + export default function (req, res) { + res.send('server response') + } + ` + ) + let response = await renderViaHTTP(next.url, '/api/hello4') + expect(response).toBe('server response') + + // Edge + await next.patchFile( + 'pages/api/hello4.js', + ` + export const config = { + runtime: 'experimental-edge', + } + + export default () => new Response('edge response') + ` + ) + response = await renderViaHTTP(next.url, '/api/hello4') + expect(response).toBe('edge response') + + // Server + await next.patchFile( + 'pages/api/hello4.js', + ` + export default function (req, res) { + res.send('server response') + } + ` + ) + response = await renderViaHTTP(next.url, '/api/hello4') + expect(response).toBe('server response') + }) +}) From c6ecbedc62235bb4735d825f0bda4e5fb25d3fcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Born=C3=B6?= Date: Thu, 4 Aug 2022 17:46:19 +0200 Subject: [PATCH 04/13] Move entries deleting to on-demand-entry-handler --- packages/next/server/dev/hot-reloader.ts | 10 ---------- packages/next/server/dev/on-demand-entry-handler.ts | 8 +++++++- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/packages/next/server/dev/hot-reloader.ts b/packages/next/server/dev/hot-reloader.ts index d2ff609d003d..dd8ce5dc6c34 100644 --- a/packages/next/server/dev/hot-reloader.ts +++ b/packages/next/server/dev/hot-reloader.ts @@ -588,11 +588,6 @@ export default class HotReloader { page, pageRuntime: staticInfo.runtime, onEdgeServer: () => { - if (entries[`server${page}`]) { - // Runtime switched from server to edge - delete entries[`server${page}`] - getInvalidator()?.invalidate(['server', 'edgeServer']) - } if (!isEdgeServerCompilation) return entries[pageKey].status = BUILDING entrypoints[bundlePath] = finalizeEntrypoint({ @@ -642,11 +637,6 @@ export default class HotReloader { } }, onServer: () => { - if (entries[`edge-server${page}`]) { - // Runtime switched from edge to server - delete entries[`edge-server${page}`] - getInvalidator()?.invalidate(['server', 'edgeServer']) - } if (!isNodeServerCompilation) return entries[pageKey].status = BUILDING let request = relative(config.context!, absolutePagePath) diff --git a/packages/next/server/dev/on-demand-entry-handler.ts b/packages/next/server/dev/on-demand-entry-handler.ts index 9d34f9a3df76..e010c73c4c3f 100644 --- a/packages/next/server/dev/on-demand-entry-handler.ts +++ b/packages/next/server/dev/on-demand-entry-handler.ts @@ -310,6 +310,13 @@ export function onDemandEntryHandler({ const addPageEntry = (type: 'client' | 'server' | 'edge-server') => { return new Promise((resolve, reject) => { + if (type === 'server' && entries[`edge-server${page}`]) { + // Runtime switched from edge to server + delete entries[`edge-server${page}`] + } else if (type === 'edge-server' && entries[`server${page}`]) { + // Runtime switched from server to edge + delete entries[`server${page}`] + } const isServerComponent = serverComponentRegex.test( pagePathData.absolutePagePath ) @@ -461,7 +468,6 @@ class Invalidator { // So, it can re-build the queued pages at once. if (this.building) { this.rebuildAgain = true - return } this.building = true From fc8b98d2186431ecd0b7fe326665322efa06eb90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Born=C3=B6?= Date: Thu, 4 Aug 2022 17:48:21 +0200 Subject: [PATCH 05/13] Always emit files without comparing --- packages/next/build/webpack-config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 5f9d502d507a..8a2057c3f179 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -1223,6 +1223,7 @@ export default async function getBaseWebpackConfig( }, watchOptions, output: { + ['compareBeforeEmit' as any]: false, // we must set publicPath to an empty value to override the default of // auto which doesn't work in IE11 publicPath: `${config.assetPrefix || ''}/_next/`, From 65d50628b667833cfaf30a73870f92648f9412ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Born=C3=B6?= Date: Thu, 4 Aug 2022 17:48:43 +0200 Subject: [PATCH 06/13] Unflakify tests --- .../development/edge-api-routes/index.test.ts | 90 ++++++++----------- 1 file changed, 35 insertions(+), 55 deletions(-) diff --git a/test/development/edge-api-routes/index.test.ts b/test/development/edge-api-routes/index.test.ts index 2c41ca2f4042..98e183bac381 100644 --- a/test/development/edge-api-routes/index.test.ts +++ b/test/development/edge-api-routes/index.test.ts @@ -1,6 +1,6 @@ import { createNext } from 'e2e-utils' import { NextInstance } from 'test/lib/next-modes/base' -import { renderViaHTTP } from 'next-test-utils' +import { check, renderViaHTTP } from 'next-test-utils' describe('edge-api-routes', () => { let next: NextInstance @@ -31,8 +31,7 @@ describe('edge-api-routes', () => { export default () => new Response('edge response') ` ) - let response = await renderViaHTTP(next.url, '/api/hello') - expect(response).toBe('edge response') + await check(() => renderViaHTTP(next.url, '/api/hello'), 'edge response') // Server await next.patchFile( @@ -43,8 +42,20 @@ describe('edge-api-routes', () => { } ` ) - response = await renderViaHTTP(next.url, '/api/hello') - expect(response).toBe('server response') + await check(() => renderViaHTTP(next.url, '/api/hello'), 'server response') + + // Edge + await next.patchFile( + 'pages/api/hello.js', + ` + export const config = { + runtime: 'experimental-edge', + } + + export default () => new Response('edge response') + ` + ) + await check(() => renderViaHTTP(next.url, '/api/hello'), 'edge response') }) test('Switch between runtimes - server first', async () => { @@ -57,8 +68,7 @@ describe('edge-api-routes', () => { } ` ) - let response = await renderViaHTTP(next.url, '/api/hello2') - expect(response).toBe('server response') + await check(() => renderViaHTTP(next.url, '/api/hello2'), 'server response') // Edge await next.patchFile( @@ -71,8 +81,18 @@ describe('edge-api-routes', () => { export default () => new Response('edge response') ` ) - response = await renderViaHTTP(next.url, '/api/hello2') - expect(response).toBe('edge response') + await check(() => renderViaHTTP(next.url, '/api/hello2'), 'edge response') + + // Server + await next.patchFile( + 'pages/api/hello2.js', + ` + export default function (req, res) { + res.send('server response') + } + ` + ) + await check(() => renderViaHTTP(next.url, '/api/hello2'), 'server response') }) test('Recover from syntax error', async () => { @@ -86,8 +106,7 @@ describe('edge-api-routes', () => { export default () => new Response('edge response') ` ) - let response = await renderViaHTTP(next.url, '/api/hello3') - expect(response).toBe('edge response') + await check(() => renderViaHTTP(next.url, '/api/hello3'), 'edge response') // Syntax error await next.patchFile( @@ -100,8 +119,10 @@ describe('edge-api-routes', () => { export default => new Response('edge response') ` ) - response = await renderViaHTTP(next.url, '/api/hello3') - expect(response).toInclude('Unexpected token') + await check( + () => renderViaHTTP(next.url, '/api/hello3'), + /Unexpected token/ + ) // Fix syntax error await next.patchFile( @@ -114,47 +135,6 @@ describe('edge-api-routes', () => { export default () => new Response('edge response 2') ` ) - response = await renderViaHTTP(next.url, '/api/hello3') - expect(response).toBe('edge response 2') - }) - - test('Switch to new runtime and then back ', async () => { - // Server - await next.patchFile( - 'pages/api/hello4.js', - ` - export default function (req, res) { - res.send('server response') - } - ` - ) - let response = await renderViaHTTP(next.url, '/api/hello4') - expect(response).toBe('server response') - - // Edge - await next.patchFile( - 'pages/api/hello4.js', - ` - export const config = { - runtime: 'experimental-edge', - } - - export default () => new Response('edge response') - ` - ) - response = await renderViaHTTP(next.url, '/api/hello4') - expect(response).toBe('edge response') - - // Server - await next.patchFile( - 'pages/api/hello4.js', - ` - export default function (req, res) { - res.send('server response') - } - ` - ) - response = await renderViaHTTP(next.url, '/api/hello4') - expect(response).toBe('server response') + await check(() => renderViaHTTP(next.url, '/api/hello3'), 'edge response 2') }) }) From f7f9dd76a3d31ed5b30a986e298920d904546994 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Born=C3=B6?= Date: Thu, 4 Aug 2022 17:55:08 +0200 Subject: [PATCH 07/13] fix lint --- packages/next/server/dev/hot-reloader.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/next/server/dev/hot-reloader.ts b/packages/next/server/dev/hot-reloader.ts index dd8ce5dc6c34..996eca68a0e3 100644 --- a/packages/next/server/dev/hot-reloader.ts +++ b/packages/next/server/dev/hot-reloader.ts @@ -29,7 +29,6 @@ import { findPageFile } from '../lib/find-page-file' import { BUILDING, entries, - getInvalidator, onDemandEntryHandler, } from './on-demand-entry-handler' import { denormalizePagePath } from '../../shared/lib/page-path/denormalize-page-path' From 7cdf2547989b6858e74b43e7de4d6cfdba7be2e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Born=C3=B6?= Date: Thu, 4 Aug 2022 18:29:29 +0200 Subject: [PATCH 08/13] the return returns --- packages/next/server/dev/on-demand-entry-handler.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/next/server/dev/on-demand-entry-handler.ts b/packages/next/server/dev/on-demand-entry-handler.ts index e010c73c4c3f..b5c681642ffe 100644 --- a/packages/next/server/dev/on-demand-entry-handler.ts +++ b/packages/next/server/dev/on-demand-entry-handler.ts @@ -468,6 +468,7 @@ class Invalidator { // So, it can re-build the queued pages at once. if (this.building) { this.rebuildAgain = true + return } this.building = true From ee1a84986dea5704b4cc0b9178eb91993a6c3fed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Born=C3=B6?= Date: Tue, 16 Aug 2022 15:55:31 +0100 Subject: [PATCH 09/13] Move deletion --- .../server/dev/on-demand-entry-handler.ts | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/packages/next/server/dev/on-demand-entry-handler.ts b/packages/next/server/dev/on-demand-entry-handler.ts index 52952c59392c..dbca1e7ed9af 100644 --- a/packages/next/server/dev/on-demand-entry-handler.ts +++ b/packages/next/server/dev/on-demand-entry-handler.ts @@ -537,22 +537,6 @@ export function onDemandEntryHandler({ } => { const entryKey = `${compilerType}${pagePathData.page}` - if ( - compilerType === 'server' && - entries[`${COMPILER_NAMES.edgeServer}${pagePathData.page}`] - ) { - // Runtime switched from edge to server - delete entries[`${COMPILER_NAMES.edgeServer}${page}`] - invalidator.invalidate(['edge-server', 'server']) - } else if ( - compilerType === 'edge-server' && - entries[`${COMPILER_NAMES.server}${pagePathData.page}`] - ) { - // Runtime switched from server to edge - delete entries[`${COMPILER_NAMES.server}${page}`] - invalidator.invalidate(['edge-server', 'server']) - } - if (entries[entryKey]) { entries[entryKey].dispose = false entries[entryKey].lastActiveTime = Date.now() @@ -607,12 +591,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] + } }, }) From bbad378d7ec968af48d0d743b3cd01fe33138165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Born=C3=B6?= Date: Tue, 16 Aug 2022 17:16:20 +0100 Subject: [PATCH 10/13] Add invalidate --- .../next/server/dev/on-demand-entry-handler.ts | 2 ++ test/development/edge-api-routes/index.test.ts | 18 +++++++++--------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/next/server/dev/on-demand-entry-handler.ts b/packages/next/server/dev/on-demand-entry-handler.ts index dbca1e7ed9af..6ea5cae35c35 100644 --- a/packages/next/server/dev/on-demand-entry-handler.ts +++ b/packages/next/server/dev/on-demand-entry-handler.ts @@ -595,6 +595,7 @@ export function onDemandEntryHandler({ if (entries[edgeServerEntry]) { // Runtime switched from edge to server delete entries[edgeServerEntry] + invalidator.invalidate(['edge-server']) } }, onEdgeServer: () => { @@ -606,6 +607,7 @@ export function onDemandEntryHandler({ if (entries[serverEntry]) { // Runtime switched from server to edge delete entries[serverEntry] + invalidator.invalidate(['server']) } }, }) diff --git a/test/development/edge-api-routes/index.test.ts b/test/development/edge-api-routes/index.test.ts index 98e183bac381..3c1c413771a7 100644 --- a/test/development/edge-api-routes/index.test.ts +++ b/test/development/edge-api-routes/index.test.ts @@ -48,12 +48,12 @@ describe('edge-api-routes', () => { await next.patchFile( 'pages/api/hello.js', ` - export const config = { - runtime: 'experimental-edge', - } + export const config = { + runtime: 'experimental-edge', + } - export default () => new Response('edge response') - ` + export default () => new Response('edge response') + ` ) await check(() => renderViaHTTP(next.url, '/api/hello'), 'edge response') }) @@ -87,10 +87,10 @@ describe('edge-api-routes', () => { await next.patchFile( 'pages/api/hello2.js', ` - export default function (req, res) { - res.send('server response') - } - ` + export default function (req, res) { + res.send('server response') + } + ` ) await check(() => renderViaHTTP(next.url, '/api/hello2'), 'server response') }) From c745d949aff9a26c62f407799f6e740385897f9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Born=C3=B6?= Date: Tue, 23 Aug 2022 10:33:52 +0200 Subject: [PATCH 11/13] Append the runtime to the filename in dev --- packages/next/build/webpack-config.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 7532dc843b02..c6503fcb5205 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -28,6 +28,7 @@ import { MODERN_BROWSERSLIST_TARGET, COMPILER_NAMES, CompilerNameValues, + EDGE_RUNTIME_WEBPACK, } from '../shared/lib/constants' import { execOnce } from '../shared/lib/utils' import { NextConfigComplete } from '../server/config-shared' @@ -1412,7 +1413,6 @@ export default async function getBaseWebpackConfig( }, watchOptions, output: { - ['compareBeforeEmit' as any]: false, // we must set publicPath to an empty value to override the default of // auto which doesn't work in IE11 publicPath: `${config.assetPrefix || ''}/_next/`, @@ -1420,8 +1420,16 @@ export default async function getBaseWebpackConfig( // On the server we don't use hashes filename: isNodeServer || isEdgeServer - ? dev || isEdgeServer - ? `[name].js` + ? isEdgeServer + ? '[name].js' + : dev + ? ({ runtime, chunk }) => + chunk?.name?.startsWith('pages/') + ? runtime === EDGE_RUNTIME_WEBPACK + ? // Append the runtime to the filename in dev to prevent both compilers outputting to the same file when switching between runtimes + '[name]-e.js' + : '[name]-s.js' + : '[name].js' : `../[name].js` : `static/chunks/${isDevFallback ? 'fallback/' : ''}[name]${ dev ? '' : appDir ? '-[chunkhash]' : '-[contenthash]' From dc68db87ec747a1d999b527fb0106635daf813ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Born=C3=B6?= Date: Tue, 23 Aug 2022 11:10:23 +0200 Subject: [PATCH 12/13] Remove invalidate call --- packages/next/server/dev/on-demand-entry-handler.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/next/server/dev/on-demand-entry-handler.ts b/packages/next/server/dev/on-demand-entry-handler.ts index 481483483eee..d4525f167d47 100644 --- a/packages/next/server/dev/on-demand-entry-handler.ts +++ b/packages/next/server/dev/on-demand-entry-handler.ts @@ -624,7 +624,6 @@ export function onDemandEntryHandler({ if (entries[edgeServerEntry]) { // Runtime switched from edge to server delete entries[edgeServerEntry] - invalidator.invalidate(['edge-server']) } }, onEdgeServer: () => { @@ -636,7 +635,6 @@ export function onDemandEntryHandler({ if (entries[serverEntry]) { // Runtime switched from server to edge delete entries[serverEntry] - invalidator.invalidate(['server']) } }, }) From a79a3b5550a5c6426c5e672497cbb92f9da290f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Born=C3=B6?= Date: Wed, 7 Sep 2022 09:45:50 +0200 Subject: [PATCH 13/13] Clean up --- packages/next/build/webpack-config.ts | 13 +- .../development/edge-api-routes/index.test.ts | 140 ------------- test/e2e/switchable-runtime/index.test.ts | 184 ++++++++++++++++++ .../pages/api/switch-in-dev-same-content.js | 3 + .../pages/api/switch-in-dev.js | 3 + .../pages/api/syntax-error-in-dev.js | 5 + .../switchable-runtime/pages/switch-in-dev.js | 7 + 7 files changed, 204 insertions(+), 151 deletions(-) delete mode 100644 test/development/edge-api-routes/index.test.ts create mode 100644 test/e2e/switchable-runtime/pages/api/switch-in-dev-same-content.js create mode 100644 test/e2e/switchable-runtime/pages/api/switch-in-dev.js create mode 100644 test/e2e/switchable-runtime/pages/api/syntax-error-in-dev.js create mode 100644 test/e2e/switchable-runtime/pages/switch-in-dev.js diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 3b9696e9f96d..02f1012f7497 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -28,7 +28,6 @@ import { MODERN_BROWSERSLIST_TARGET, COMPILER_NAMES, CompilerNameValues, - EDGE_RUNTIME_WEBPACK, } from '../shared/lib/constants' import { execOnce } from '../shared/lib/utils' import { NextConfigComplete } from '../server/config-shared' @@ -1408,16 +1407,8 @@ export default async function getBaseWebpackConfig( // On the server we don't use hashes filename: isNodeServer || isEdgeServer - ? isEdgeServer - ? '[name].js' - : dev - ? ({ runtime, chunk }) => - chunk?.name?.startsWith('pages/') - ? runtime === EDGE_RUNTIME_WEBPACK - ? // Append the runtime to the filename in dev to prevent both compilers outputting to the same file when switching between runtimes - '[name]-e.js' - : '[name]-s.js' - : '[name].js' + ? dev || isEdgeServer + ? `[name].js` : `../[name].js` : `static/chunks/${isDevFallback ? 'fallback/' : ''}[name]${ dev ? '' : appDir ? '-[chunkhash]' : '-[contenthash]' diff --git a/test/development/edge-api-routes/index.test.ts b/test/development/edge-api-routes/index.test.ts deleted file mode 100644 index 3c1c413771a7..000000000000 --- a/test/development/edge-api-routes/index.test.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { createNext } from 'e2e-utils' -import { NextInstance } from 'test/lib/next-modes/base' -import { check, renderViaHTTP } from 'next-test-utils' - -describe('edge-api-routes', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/index.js': ` - export default function Page() { - return

hello world

- } - `, - }, - dependencies: {}, - }) - }) - afterAll(() => next.destroy()) - - test('Switch between runtimes - edge first', async () => { - // Edge - await next.patchFile( - 'pages/api/hello.js', - ` - export const config = { - runtime: 'experimental-edge', - } - - export default () => new Response('edge response') - ` - ) - await check(() => renderViaHTTP(next.url, '/api/hello'), 'edge response') - - // Server - await next.patchFile( - 'pages/api/hello.js', - ` - export default function (req, res) { - res.send('server response') - } - ` - ) - await check(() => renderViaHTTP(next.url, '/api/hello'), 'server response') - - // Edge - await next.patchFile( - 'pages/api/hello.js', - ` - export const config = { - runtime: 'experimental-edge', - } - - export default () => new Response('edge response') - ` - ) - await check(() => renderViaHTTP(next.url, '/api/hello'), 'edge response') - }) - - test('Switch between runtimes - server first', async () => { - // Server - await next.patchFile( - 'pages/api/hello2.js', - ` - export default function (req, res) { - res.send('server response') - } - ` - ) - await check(() => renderViaHTTP(next.url, '/api/hello2'), 'server response') - - // Edge - await next.patchFile( - 'pages/api/hello2.js', - ` - export const config = { - runtime: 'experimental-edge', - } - - export default () => new Response('edge response') - ` - ) - await check(() => renderViaHTTP(next.url, '/api/hello2'), 'edge response') - - // Server - await next.patchFile( - 'pages/api/hello2.js', - ` - export default function (req, res) { - res.send('server response') - } - ` - ) - await check(() => renderViaHTTP(next.url, '/api/hello2'), 'server response') - }) - - test('Recover from syntax error', async () => { - await next.patchFile( - 'pages/api/hello3.js', - ` - export const config = { - runtime: 'experimental-edge', - } - - export default () => new Response('edge response') - ` - ) - await check(() => renderViaHTTP(next.url, '/api/hello3'), 'edge response') - - // Syntax error - await next.patchFile( - 'pages/api/hello3.js', - ` - export const config = { - runtime: 'experimental-edge', - } - - export default => new Response('edge response') - ` - ) - await check( - () => renderViaHTTP(next.url, '/api/hello3'), - /Unexpected token/ - ) - - // Fix syntax error - await next.patchFile( - 'pages/api/hello3.js', - ` - export const config = { - runtime: 'experimental-edge', - } - - export default () => new Response('edge response 2') - ` - ) - await check(() => renderViaHTTP(next.url, '/api/hello3'), 'edge response 2') - }) -}) diff --git a/test/e2e/switchable-runtime/index.test.ts b/test/e2e/switchable-runtime/index.test.ts index f21f94dba24b..958e2780a8d8 100644 --- a/test/e2e/switchable-runtime/index.test.ts +++ b/test/e2e/switchable-runtime/index.test.ts @@ -202,6 +202,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' + ) + }) }) } else { describe('Switchable runtime (prod)', () => { 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', +}