Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(switchable-runtime): Make it possible to switch between edge and server runtime in dev #39327

Merged
merged 22 commits into from Sep 7, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/next/build/webpack-config.ts
Expand Up @@ -1223,6 +1223,7 @@ export default async function getBaseWebpackConfig(
},
watchOptions,
output: {
['compareBeforeEmit' as any]: false,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as any because types didn't contain output.compareBeforeEmit

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sokra are there any performance drawbacks to doing this? Seems the comparison is failing in this case and it's not emitting the file when switched between compilers.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly this won't be needed after this is fixed https://vercel.slack.com/archives/C035J346QQL/p1659704191110839

// we must set publicPath to an empty value to override the default of
// auto which doesn't work in IE11
publicPath: `${config.assetPrefix || ''}/_next/`,
Expand Down
13 changes: 7 additions & 6 deletions packages/next/build/webpack/plugins/middleware-plugin.ts
Expand Up @@ -44,12 +44,6 @@ interface EntryMetadata {
}

const NAME = 'MiddlewarePlugin'
const middlewareManifest: MiddlewareManifest = {
sortedMiddleware: [],
middleware: {},
functions: {},
version: 1,
}

export default class MiddlewarePlugin {
dev: boolean
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions packages/next/server/dev/hot-reloader.ts
Expand Up @@ -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'
Expand Down
8 changes: 7 additions & 1 deletion packages/next/server/dev/on-demand-entry-handler.ts
Expand Up @@ -310,6 +310,13 @@ export function onDemandEntryHandler({

const addPageEntry = (type: 'client' | 'server' | 'edge-server') => {
return new Promise<void>((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
)
Expand Down Expand Up @@ -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
Expand Down
140 changes: 140 additions & 0 deletions test/development/edge-api-routes/index.test.ts
@@ -0,0 +1,140 @@
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 <p>hello world</p>
}
`,
},
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')
})
})