diff --git a/packages/next-swc/crates/next-core/src/next_import_map.rs b/packages/next-swc/crates/next-core/src/next_import_map.rs
index 210d9b898a537..e80def16aed70 100644
--- a/packages/next-swc/crates/next-core/src/next_import_map.rs
+++ b/packages/next-swc/crates/next-core/src/next_import_map.rs
@@ -45,11 +45,12 @@ use crate::{
/// This is not identical to the list of entire node.js internals, refer
/// https://vercel.com/docs/functions/runtimes/edge-runtime#compatible-node.js-modules
/// for the allowed imports.
-const EDGE_UNSUPPORTED_NODE_INTERNALS: [&str; 43] = [
+const EDGE_UNSUPPORTED_NODE_INTERNALS: [&str; 44] = [
"child_process",
"cluster",
"console",
"constants",
+ "crypto",
"dgram",
"diagnostics_channel",
"dns",
diff --git a/test/development/basic/node-builtins.test.ts b/test/development/basic/node-builtins.test.ts
index 96a3ce3caeaf9..b695df70cc194 100644
--- a/test/development/basic/node-builtins.test.ts
+++ b/test/development/basic/node-builtins.test.ts
@@ -167,4 +167,35 @@ describe('node builtins', () => {
expect(parsedData.sys).toBe(true)
expect(parsedData.timers).toBe(true)
})
+
+ it('should throw when unsupported builtins are used in middleware', async () => {
+ const res = await next.fetch('/middleware-test')
+ expect(res.status).toBe(200)
+ expect(JSON.parse(res.headers.get('supported-result')))
+ .toMatchInlineSnapshot(`
+ {
+ "assert": true,
+ "buffer": "hello world",
+ "eventEmitter": true,
+ "util": true,
+ }
+ `)
+ expect(JSON.parse(res.headers.get('unsupported-result')))
+ .toMatchInlineSnapshot(`
+ {
+ "constants": false,
+ "crypto": false,
+ "domain": false,
+ "http": false,
+ "https": false,
+ "os": false,
+ "path": false,
+ "stream": false,
+ "timers": false,
+ "tty": false,
+ "vm": false,
+ "zlib": false,
+ }
+ `)
+ })
})
diff --git a/test/development/basic/node-builtins/app/middleware-test/page.tsx b/test/development/basic/node-builtins/app/middleware-test/page.tsx
new file mode 100644
index 0000000000000..5a8ee22cb94bd
--- /dev/null
+++ b/test/development/basic/node-builtins/app/middleware-test/page.tsx
@@ -0,0 +1,3 @@
+export default function Page() {
+ return
Middleware Test
+}
diff --git a/test/development/basic/node-builtins/middleware.js b/test/development/basic/node-builtins/middleware.js
new file mode 100644
index 0000000000000..f683368408a8c
--- /dev/null
+++ b/test/development/basic/node-builtins/middleware.js
@@ -0,0 +1,137 @@
+// Unsupported in edge
+import { Writable } from 'stream'
+import path from 'path'
+import crypto from 'crypto'
+import vm from 'vm'
+import constants from 'constants'
+import domain from 'domain'
+import http from 'http'
+import https from 'https'
+import os from 'os'
+// TODO: These are accidentally polyfilled in edge runtime currently.
+// import punycode from 'punycode'
+// import process from 'process'
+// import querystring from 'querystring'
+// import stringDecoder from 'string_decoder'
+import sys from 'sys'
+import timers from 'timers'
+import tty from 'tty'
+import zlib from 'zlib'
+import 'setimmediate'
+// Supported in edge
+import { Buffer } from 'buffer'
+import assert from 'assert'
+import util from 'util'
+import { EventEmitter } from 'events'
+
+// Other imports
+import { NextResponse } from 'next/server'
+
+export default async function middleware(request) {
+ if (request.nextUrl.pathname !== '/middleware-test') {
+ return
+ }
+ const response = NextResponse.next()
+
+ let emitted = false
+ class MyEmitter extends EventEmitter {}
+ const myEmitter = new MyEmitter()
+ // Only do this once so we don't loop forever
+ myEmitter.once('myEvent', (_event, _listener) => {
+ emitted = true
+ })
+ myEmitter.emit('myEvent')
+
+ assert.ok(emitted)
+ assert.ok(!!util.promisify)
+ assert.ok(true)
+
+ const supportedResult = {
+ assert: true,
+ buffer: Buffer.from('hello world').toString('utf8'),
+ eventEmitter: true,
+ util: true,
+ }
+
+ response.headers.set('supported-result', JSON.stringify(supportedResult))
+
+ // TODO: These are accidentally polyfilled in edge runtime currently.
+ // assert.throws(() => {
+ // console.log(punycode)
+ // })
+ // assert.throws(() => {
+ // console.log(process.title)
+ // })
+ // assert.throws(() => {
+ // console.log(querystring)
+ // })
+ // assert.throws(() => {
+ // console.log(stringDecoder)
+ // })
+
+ assert.throws(() => {
+ console.log(domain)
+ })
+ assert.throws(() => {
+ console.log(http)
+ })
+ assert.throws(() => {
+ console.log(https)
+ })
+ assert.throws(() => {
+ console.log(zlib.Gzip)
+ })
+ assert.throws(() => {
+ console.log(constants.E2BIG)
+ })
+ assert.throws(() => {
+ console.log(crypto.createHash)
+ })
+ assert.throws(() => {
+ console.log(os.hostname)
+ })
+ assert.throws(() => {
+ console.log(path.join)
+ })
+ assert.throws(() => {
+ console.log(vm)
+ })
+ assert.throws(() => {
+ console.log(tty.isatty)
+ })
+ assert.throws(() => {
+ console.log(timers.clearImmediate)
+ })
+ assert.throws(() => {
+ console.log(Writable)
+ })
+ assert.throws(() => {
+ console.log(sys)
+ })
+
+ const unsupportedResult = {
+ // TODO: these are accidentally polyfilled in edge runtime currently.
+ // punycode: false,
+ // process: false,
+ // querystring: false,
+ // stringDecoder: false,
+
+ domain: false,
+ http: false,
+ https: false,
+ zlib: false,
+ constants: false,
+ crypto: false,
+ os: false,
+ path: false,
+
+ vm: false,
+ tty: false,
+ timers: false,
+ stream: false,
+ }
+
+ response.headers.set('unsupported-result', JSON.stringify(unsupportedResult))
+
+ return response
+}