fix: buffer is not usable on edge runtime (#39227)
* fix: buffer is not usable on edge runtime * chore: improves implementation to allow any fallbacks
This commit is contained in:
parent
d315ee1786
commit
147a24e320
3 changed files with 97 additions and 46 deletions
|
@ -103,13 +103,22 @@ export default class MiddlewarePlugin {
|
|||
|
||||
export async function handleWebpackExtenalForEdgeRuntime({
|
||||
request,
|
||||
context,
|
||||
contextInfo,
|
||||
getResolve,
|
||||
}: {
|
||||
request: string
|
||||
context: string
|
||||
contextInfo: any
|
||||
getResolve: () => any
|
||||
}) {
|
||||
if (contextInfo.issuerLayer === 'middleware' && isNodeJsModule(request)) {
|
||||
return `root globalThis.__import_unsupported('${request}')`
|
||||
// allows user to provide and use their polyfills, as we do with buffer.
|
||||
try {
|
||||
await getResolve()(context, request)
|
||||
} catch {
|
||||
return `root globalThis.__import_unsupported('${request}')`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -490,26 +490,50 @@ describe('Edge runtime code with imports', () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe('Edge API importing vanilla 3rd party module', () => {
|
||||
describe.each([
|
||||
{
|
||||
title: 'Edge API',
|
||||
url: routeUrl,
|
||||
init(importStatement) {
|
||||
context.api.write(`
|
||||
${importStatement}
|
||||
export default async function handler(request) {
|
||||
const response = Response.json({ ok: true })
|
||||
response.headers.set('x-from-runtime', nanoid())
|
||||
return response
|
||||
}
|
||||
|
||||
export const config = { runtime: 'experimental-edge' }
|
||||
`)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Middleware',
|
||||
url: middlewareUrl,
|
||||
init(importStatement) {
|
||||
context.middleware.write(`
|
||||
import { NextResponse } from 'next/server'
|
||||
${importStatement}
|
||||
|
||||
export async function middleware(request) {
|
||||
const response = NextResponse.next()
|
||||
response.headers.set('x-from-runtime', nanoid())
|
||||
return response
|
||||
}
|
||||
`)
|
||||
},
|
||||
},
|
||||
])('$title importing vanilla 3rd party module', ({ init, url }) => {
|
||||
const moduleName = 'nanoid'
|
||||
const importStatement = `import { nanoid } from "${moduleName}"`
|
||||
|
||||
beforeEach(() => {
|
||||
context.api.write(`
|
||||
${importStatement}
|
||||
export default async function handler(request) {
|
||||
return Response.json({ ok: nanoid() })
|
||||
}
|
||||
|
||||
export const config = { runtime: 'experimental-edge' }
|
||||
`)
|
||||
})
|
||||
beforeEach(() => init(importStatement))
|
||||
|
||||
it('does not throw in dev at runtime', async () => {
|
||||
context.app = await launchApp(context.appDir, context.appPort, appOption)
|
||||
const res = await fetchViaHTTP(context.appPort, routeUrl)
|
||||
const res = await fetchViaHTTP(context.appPort, url)
|
||||
expect(res.status).toBe(200)
|
||||
expect(await res.json()).toEqual({ ok: expect.any(String) })
|
||||
expect(res.headers.get('x-from-runtime')).toBeDefined()
|
||||
expectNoError(moduleName)
|
||||
})
|
||||
|
||||
|
@ -519,47 +543,67 @@ describe('Edge runtime code with imports', () => {
|
|||
})
|
||||
expect(stderr).not.toContain(getUnsupportedModuleWarning(moduleName))
|
||||
context.app = await nextStart(context.appDir, context.appPort, appOption)
|
||||
const res = await fetchViaHTTP(context.appPort, routeUrl)
|
||||
const res = await fetchViaHTTP(context.appPort, url)
|
||||
expect(res.status).toBe(200)
|
||||
expect(await res.json()).toEqual({ ok: expect.any(String) })
|
||||
expect(res.headers.get('x-from-runtime')).toBeDefined()
|
||||
expectNoError(moduleName)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Middleware importing vanilla 3rd party module', () => {
|
||||
const moduleName = 'nanoid'
|
||||
const importStatement = `import { nanoid } from "${moduleName}"`
|
||||
describe.each([
|
||||
{
|
||||
title: 'Edge API',
|
||||
url: routeUrl,
|
||||
init(importStatement) {
|
||||
context.api.write(`
|
||||
${importStatement}
|
||||
|
||||
beforeEach(() => {
|
||||
context.middleware.write(`
|
||||
import { NextResponse } from 'next/server'
|
||||
${importStatement}
|
||||
export default async function handler(request) {
|
||||
const response = Response.json({ ok: true })
|
||||
response.headers.set('x-from-runtime', Buffer.isBuffer('a string'))
|
||||
return response
|
||||
}
|
||||
|
||||
export const config = { runtime: 'experimental-edge' }
|
||||
`)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Middleware',
|
||||
url: middlewareUrl,
|
||||
init(importStatement) {
|
||||
context.middleware.write(`
|
||||
import { NextResponse } from 'next/server'
|
||||
${importStatement}
|
||||
|
||||
export async function middleware(request) {
|
||||
const response = NextResponse.next()
|
||||
response.headers.set('x-from-middleware', nanoid())
|
||||
return response
|
||||
}
|
||||
`)
|
||||
})
|
||||
export async function middleware(request) {
|
||||
const response = NextResponse.next()
|
||||
response.headers.set('x-from-runtime', Buffer.isBuffer('a string'))
|
||||
return response
|
||||
}
|
||||
`)
|
||||
},
|
||||
},
|
||||
])('$title using Buffer polyfill', ({ init, url }) => {
|
||||
const moduleName = 'buffer'
|
||||
const importStatement = `import { Buffer } from "${moduleName}"`
|
||||
|
||||
beforeEach(() => init(importStatement))
|
||||
|
||||
it('does not throw in dev at runtime', async () => {
|
||||
context.app = await launchApp(context.appDir, context.appPort, appOption)
|
||||
const res = await fetchViaHTTP(context.appPort, middlewareUrl)
|
||||
const res = await fetchViaHTTP(context.appPort, url)
|
||||
expect(res.status).toBe(200)
|
||||
expect(res.headers.get('x-from-middleware')).toBeDefined()
|
||||
expect(res.headers.get('x-from-runtime')).toBe('false')
|
||||
expectNoError(moduleName)
|
||||
})
|
||||
|
||||
it('does not throw in production at runtime', async () => {
|
||||
const { stderr } = await nextBuild(context.appDir, undefined, {
|
||||
stderr: true,
|
||||
})
|
||||
expect(stderr).not.toContain(getUnsupportedModuleWarning(moduleName))
|
||||
await nextBuild(context.appDir, undefined, { stderr: true })
|
||||
context.app = await nextStart(context.appDir, context.appPort, appOption)
|
||||
const res = await fetchViaHTTP(context.appPort, middlewareUrl)
|
||||
const res = await fetchViaHTTP(context.appPort, url)
|
||||
expect(res.status).toBe(200)
|
||||
expect(res.headers.get('x-from-middleware')).toBeDefined()
|
||||
expect(res.headers.get('x-from-runtime')).toBe('false')
|
||||
expectNoError(moduleName)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -22,7 +22,7 @@ const unsupportedFunctions = [
|
|||
'process.cpuUsage',
|
||||
'process.getuid',
|
||||
]
|
||||
const undefinedPropertoes = [
|
||||
const undefinedProperties = [
|
||||
// no need to test all of the process properties
|
||||
'process.arch',
|
||||
'process.version',
|
||||
|
@ -83,7 +83,7 @@ describe.each([
|
|||
|
||||
afterAll(() => killApp(app))
|
||||
|
||||
it.each(undefinedPropertoes.map((api) => ({ api })))(
|
||||
it.each(undefinedProperties.map((api) => ({ api })))(
|
||||
'does not throw on using $api',
|
||||
async ({ api }) => {
|
||||
const res = await fetchViaHTTP(appPort, computeRoute(api))
|
||||
|
@ -125,16 +125,14 @@ Learn more: https://nextjs.org/docs/api-reference/edge-runtime`)
|
|||
})
|
||||
|
||||
it.each(
|
||||
['Buffer', ...unsupportedFunctions, ...unsupportedClasses].map(
|
||||
(api, index) => ({
|
||||
api,
|
||||
})
|
||||
)
|
||||
[...unsupportedFunctions, ...unsupportedClasses].map((api, index) => ({
|
||||
api,
|
||||
}))
|
||||
)(`warns for $api during build`, ({ api }) => {
|
||||
expect(buildResult.stderr).toContain(`A Node.js API is used (${api}`)
|
||||
})
|
||||
|
||||
it.each(undefinedPropertoes.map((api) => ({ api })))(
|
||||
it.each(['Buffer', ...undefinedProperties].map((api) => ({ api })))(
|
||||
'does not warn on using $api',
|
||||
({ api }) => {
|
||||
expect(buildResult.stderr).toContain(`A Node.js API is used (${api}`)
|
||||
|
|
Loading…
Reference in a new issue