Repository: vercel/next.js
Pull request: fix: node.js module import error when using middleware

The previous [backport](https://github.com/vercel/next.js/pull/77794) caused an issue due to how imports are resolved on canary vs v14, resulting in a message about using node.js APIs in middleware.

This moves the import to the `web` directory and adds a test case for the warning. 

Closes NDX-1014

Changed files:
filename: packages/next/src/server/base-server.ts
patch: @@ -72,7 +72,7 @@ import { removeTrailingSlash } from '../shared/lib/router/utils/remove-trailing-
 import { denormalizePagePath } from '../shared/lib/page-path/denormalize-page-path'
 import * as Log from '../build/output/log'
 import escapePathDelimiters from '../shared/lib/router/utils/escape-path-delimiters'
-import { getUtils, normalizeNextQueryParam } from './server-utils'
+import { getUtils } from './server-utils'
 import isError, { getProperError } from '../lib/is-error'
 import {
   addRequestMeta,
@@ -111,6 +111,7 @@ import { sendResponse } from './send-response'
 import { handleInternalServerErrorResponse } from './future/route-modules/helpers/response-handlers'
 import {
   fromNodeOutgoingHttpHeaders,
+  normalizeNextQueryParam,
   toNodeOutgoingHttpHeaders,
 } from './web/utils'
 import { CACHE_ONE_YEAR, NEXT_CACHE_TAGS_HEADER } from '../lib/constants'

filename: packages/next/src/server/server-utils.ts
patch: @@ -55,24 +55,6 @@ export function normalizeVercelUrl(
   }
 }
 
-/**
- * Normalizes `nxtP` and `nxtI` query param values to remove the prefix.
- * This function does not mutate the input key; it calls the provided function
- * with the normalized key.
- */
-export function normalizeNextQueryParam(
-  key: string,
-  onKeyNormalized: (normalizedKey: string) => void
-) {
-  const prefixes = [NEXT_QUERY_PARAM_PREFIX, NEXT_INTERCEPTION_MARKER_PREFIX]
-  for (const prefix of prefixes) {
-    if (key !== prefix && key.startsWith(prefix)) {
-      const normalizedKey = key.substring(prefix.length)
-      onKeyNormalized(normalizedKey)
-    }
-  }
-}
-
 export function interpolateDynamicPath(
   pathname: string,
   params: ParsedUrlQuery,

filename: packages/next/src/server/web/adapter.ts
patch: @@ -1,7 +1,7 @@
 import type { RequestData, FetchEventResult } from './types'
 import type { RequestInit } from './spec-extension/request'
 import { PageSignatureError } from './error'
-import { fromNodeOutgoingHttpHeaders } from './utils'
+import { fromNodeOutgoingHttpHeaders, normalizeNextQueryParam } from './utils'
 import { NextFetchEvent } from './spec-extension/fetch-event'
 import { NextRequest } from './spec-extension/request'
 import { NextResponse } from './spec-extension/response'
@@ -18,7 +18,6 @@ import { getTracer } from '../lib/trace/tracer'
 import type { TextMapGetter } from 'next/dist/compiled/@opentelemetry/api'
 import { MiddlewareSpan } from '../lib/trace/constants'
 import { getEdgePreviewProps } from './get-edge-preview-props'
-import { normalizeNextQueryParam } from '../server-utils'
 
 export class NextRequestHint extends NextRequest {
   sourcePage: string

filename: packages/next/src/server/web/utils.ts
patch: @@ -1,4 +1,8 @@
 import type { OutgoingHttpHeaders } from 'http'
+import {
+  NEXT_INTERCEPTION_MARKER_PREFIX,
+  NEXT_QUERY_PARAM_PREFIX,
+} from '../../lib/constants'
 
 /**
  * Converts a Node.js IncomingHttpHeaders object to a Headers object. Any
@@ -146,3 +150,21 @@ export function validateURL(url: string | URL): string {
     )
   }
 }
+
+/**
+ * Normalizes `nxtP` and `nxtI` query param values to remove the prefix.
+ * This function does not mutate the input key; it calls the provided function
+ * with the normalized key.
+ */
+export function normalizeNextQueryParam(
+  key: string,
+  onKeyNormalized: (normalizedKey: string) => void
+) {
+  const prefixes = [NEXT_QUERY_PARAM_PREFIX, NEXT_INTERCEPTION_MARKER_PREFIX]
+  for (const prefix of prefixes) {
+    if (key !== prefix && key.startsWith(prefix)) {
+      const normalizedKey = key.substring(prefix.length)
+      onKeyNormalized(normalizedKey)
+    }
+  }
+}

filename: test/e2e/app-dir/app-middleware/app-middleware.test.ts
patch: @@ -23,6 +23,10 @@ createNextDescribe(
       }, /app-dir/)
     })
 
+    it('should not include any warnings about using Node.js APIs', async () => {
+      expect(next.cliOutput).not.toContain('A Node.js module is loaded')
+    })
+
     describe.each([
       {
         title: 'Serverless Functions',