diff --git a/packages/next/src/build/webpack/plugins/middleware-plugin.ts b/packages/next/src/build/webpack/plugins/middleware-plugin.ts index 00cb6b889e251..430f9ec8a54c1 100644 --- a/packages/next/src/build/webpack/plugins/middleware-plugin.ts +++ b/packages/next/src/build/webpack/plugins/middleware-plugin.ts @@ -9,6 +9,7 @@ import { getModuleBuildInfo } from '../loaders/get-module-build-info' import { getSortedRoutes } from '../../../shared/lib/router/utils' import { webpack, sources } from 'next/dist/compiled/webpack/webpack' import { isMatch } from 'next/dist/compiled/micromatch' +import path from 'path' import { EDGE_RUNTIME_WEBPACK, EDGE_UNSUPPORTED_NODE_APIS, @@ -29,6 +30,9 @@ import { normalizeAppPath } from '../../../shared/lib/router/utils/app-paths' import { INSTRUMENTATION_HOOK_FILENAME } from '../../../lib/constants' import { NextBuildContext } from '../../build-context' +const KNOWN_SAFE_DYNAMIC_PACKAGES = + require('../../../lib/known-edge-safe-packages.json') as string[] + export interface EdgeFunctionDefinition { files: string[] name: string @@ -249,6 +253,16 @@ function isDynamicCodeEvaluationAllowed( middlewareConfig?: MiddlewareConfig, rootDir?: string ) { + // Some packages are known to use `eval` but are safe to use in the Edge + // Runtime because the dynamic code will never be executed. + if ( + KNOWN_SAFE_DYNAMIC_PACKAGES.some((pkg) => + fileName.includes(`/node_modules/${pkg}/`.replace(/\//g, path.sep)) + ) + ) { + return true + } + const name = fileName.replace(rootDir ?? '', '') return isMatch(name, middlewareConfig?.unstable_allowDynamicGlobs ?? []) } diff --git a/packages/next/src/lib/known-edge-safe-packages.json b/packages/next/src/lib/known-edge-safe-packages.json new file mode 100644 index 0000000000000..be767c48edc8b --- /dev/null +++ b/packages/next/src/lib/known-edge-safe-packages.json @@ -0,0 +1 @@ +["function-bind"] diff --git a/test/production/edge-safe-dynamic-code/index.test.ts b/test/production/edge-safe-dynamic-code/index.test.ts new file mode 100644 index 0000000000000..959d51dfca460 --- /dev/null +++ b/test/production/edge-safe-dynamic-code/index.test.ts @@ -0,0 +1,41 @@ +import { createNext } from 'e2e-utils' +import { NextInstance } from 'test/lib/next-modes/base' + +// This test is basically for https://github.com/vercel/next.js/discussions/51910 +// to make sure that some libs that we know are using `eval` but don't break +// because it will never run into that condition, but still can't to be DCE'd. + +describe('Edge safe dynamic code', () => { + let next: NextInstance + + afterAll(() => next.destroy()) + + it('should not fail when "function-bind" package is used', async () => { + next = await createNext({ + skipStart: true, + dependencies: { + 'function-bind': 'latest', + }, + files: { + 'pages/index.js': ` + export default function Page() { + return
hello world
+ } + `, + 'middleware.js': ` + import { NextResponse } from 'next/server' + import * as bind from 'function-bind' + console.log(bind) + export default async function middleware(request) { + return NextResponse.next() + } + `, + }, + }) + await next.start() + + expect(next.cliOutput).not.toContain( + `Dynamic Code Evaluation (e. g. 'eval', 'new Function', 'WebAssembly.compile') not allowed in Edge Runtime` + ) + }) +})