@@ -111,6 +111,83 @@ describe('Middleware can set the matcher in its config', () => {
111111 } , 'success' )
112112 } )
113113
114+ if ( ( global as any ) . isNextStart ) {
115+ it ( 'produces the expected middleware manifest' , async ( ) => {
116+ const manifest = JSON . parse (
117+ await next . readFile ( '.next/server/middleware-manifest.json' )
118+ )
119+
120+ // Redact volatile fields so the snapshot is stable across builds:
121+ // - `env` values are randomly generated per build (encryption keys,
122+ // preview mode ids, build id).
123+ // - `files` and `entrypoint` paths contain content hashes and may
124+ // differ between webpack and Turbopack.
125+ const normalize = ( value : unknown , key ?: string ) : unknown => {
126+ if ( key === 'env' && value && typeof value === 'object' ) {
127+ return Object . fromEntries (
128+ Object . keys ( value )
129+ . sort ( )
130+ . map ( ( k ) => [ k , '<redacted>' ] )
131+ )
132+ }
133+ if ( key === 'files' ) return '<files>'
134+ if ( key === 'entrypoint' ) return '<entrypoint>'
135+ if ( Array . isArray ( value ) ) return value . map ( ( v ) => normalize ( v ) )
136+ if ( value && typeof value === 'object' ) {
137+ return Object . fromEntries (
138+ Object . entries ( value ) . map ( ( [ k , v ] ) => [ k , normalize ( v , k ) ] )
139+ )
140+ }
141+ return value
142+ }
143+
144+ expect ( normalize ( manifest ) ) . toMatchInlineSnapshot ( `
145+ {
146+ "functions": {},
147+ "middleware": {
148+ "/": {
149+ "assets": [],
150+ "entrypoint": "<entrypoint>",
151+ "env": {
152+ "NEXT_SERVER_ACTIONS_ENCRYPTION_KEY": "<redacted>",
153+ "__NEXT_BUILD_ID": "<redacted>",
154+ "__NEXT_PREVIEW_MODE_ENCRYPTION_KEY": "<redacted>",
155+ "__NEXT_PREVIEW_MODE_ID": "<redacted>",
156+ "__NEXT_PREVIEW_MODE_SIGNING_KEY": "<redacted>",
157+ },
158+ "files": "<files>",
159+ "matchers": [
160+ {
161+ "originalSource": "/",
162+ "regexp": "^(?:\\/(_next\\/data\\/[^/]{1,}))?(?:\\/(\\/?index|\\/?index\\.json))?[\\/#\\?]?$",
163+ },
164+ {
165+ "originalSource": "/with-middleware/:path*",
166+ "regexp": "^(?:\\/(_next\\/data\\/[^/]{1,}))?\\/with-middleware(?:\\/((?:[^\\/#\\?]+?)(?:\\/(?:[^\\/#\\?]+?))*))?(\\.json)?[\\/#\\?]?$",
167+ },
168+ {
169+ "originalSource": "/another-middleware/:path*",
170+ "regexp": "^(?:\\/(_next\\/data\\/[^/]{1,}))?\\/another-middleware(?:\\/((?:[^\\/#\\?]+?)(?:\\/(?:[^\\/#\\?]+?))*))?(\\.json)?[\\/#\\?]?$",
171+ },
172+ {
173+ "originalSource": "/_sites/:path((?![^/]*\\.json$)[^/]+$)",
174+ "regexp": "^(?:\\/(_next\\/data\\/[^/]{1,}))?\\/_sites(?:\\/((?![^/]*\\.json$)[^/]+$))(\\.json)?[\\/#\\?]?$",
175+ },
176+ ],
177+ "name": "middleware",
178+ "page": "/",
179+ "wasm": [],
180+ },
181+ },
182+ "sortedMiddleware": [
183+ "/",
184+ ],
185+ "version": 3,
186+ }
187+ ` )
188+ } )
189+ }
190+
114191 it ( 'should navigate correctly with matchers' , async ( ) => {
115192 const browser = await webdriver ( next . url , '/' )
116193 await browser . eval ( 'window.beforeNav = 1' )
0 commit comments