@@ -202,27 +202,18 @@ const writeEdgeFunction = async ({
202202 edgeFunctionDefinition,
203203 edgeFunctionRoot,
204204 netlifyConfig,
205- pageRegexMap,
206- appPathRoutesManifest = { } ,
207- nextConfig,
208- cache,
205+ functionName,
206+ matchers = [ ] ,
207+ middleware = false ,
209208} : {
210209 edgeFunctionDefinition : EdgeFunctionDefinition
211210 edgeFunctionRoot : string
212211 netlifyConfig : NetlifyConfig
213- pageRegexMap ?: Map < string , string >
214- appPathRoutesManifest ?: Record < string , string >
215- nextConfig : NextConfig
216- cache ?: 'manual'
217- } ) : Promise <
218- Array < {
219- function : string
220- name : string
221- pattern : string
222- } >
223- > => {
224- const name = sanitizeName ( edgeFunctionDefinition . name )
225- const edgeFunctionDir = join ( edgeFunctionRoot , name )
212+ functionName : string
213+ matchers ?: Array < MiddlewareMatcher >
214+ middleware ?: boolean
215+ } ) => {
216+ const edgeFunctionDir = join ( edgeFunctionRoot , functionName )
226217
227218 const bundle = await getMiddlewareBundle ( {
228219 edgeFunctionDefinition,
@@ -234,43 +225,52 @@ const writeEdgeFunction = async ({
234225
235226 await copyEdgeSourceFile ( {
236227 edgeFunctionDir,
237- file : ' runtime.ts',
228+ file : middleware ? 'middleware-runtime.ts' : 'function- runtime.ts',
238229 target : 'index.ts' ,
239230 } )
240231
241- const matchers : EdgeFunctionDefinitionV2 [ 'matchers' ] = [ ]
232+ if ( middleware ) {
233+ // Functions don't have complex matchers, so we can rely on the Netlify matcher
234+ await writeJson ( join ( edgeFunctionDir , 'matchers.json' ) , matchers )
235+ }
236+ }
242237
238+ const generateEdgeFunctionMiddlewareMatchers = ( {
239+ edgeFunctionDefinition,
240+ nextConfig,
241+ } : {
242+ edgeFunctionDefinition : EdgeFunctionDefinition
243+ edgeFunctionRoot : string
244+ nextConfig : NextConfig
245+ cache ?: 'manual'
246+ } ) : Array < MiddlewareMatcher > => {
243247 // The v1 middleware manifest has a single regexp, but the v2 has an array of matchers
244248 if ( 'regexp' in edgeFunctionDefinition ) {
245- matchers . push ( { regexp : edgeFunctionDefinition . regexp } )
246- } else if ( nextConfig . i18n ) {
247- matchers . push (
248- ...edgeFunctionDefinition . matchers . map ( ( matcher ) => ( {
249- ...matcher ,
250- regexp : makeLocaleOptional ( matcher . regexp ) ,
251- } ) ) ,
252- )
253- } else {
254- matchers . push ( ...edgeFunctionDefinition . matchers )
249+ return [ { regexp : edgeFunctionDefinition . regexp } ]
255250 }
256-
257- // If the EF matches a page, it's an app dir page so needs a matcher too
258- // The object will be empty if appDir isn't enabled in the Next config
259- if ( pageRegexMap && edgeFunctionDefinition . page in appPathRoutesManifest ) {
260- const regexp = pageRegexMap . get ( appPathRoutesManifest [ edgeFunctionDefinition . page ] )
261- if ( regexp ) {
262- matchers . push ( { regexp } )
263- }
251+ if ( nextConfig . i18n ) {
252+ return edgeFunctionDefinition . matchers . map ( ( matcher ) => ( {
253+ ...matcher ,
254+ regexp : makeLocaleOptional ( matcher . regexp ) ,
255+ } ) )
264256 }
257+ return edgeFunctionDefinition . matchers
258+ }
265259
266- await writeJson ( join ( edgeFunctionDir , 'matchers.json' ) , matchers )
267-
268- // We add a defintion for each matching path
269- return matchers . map ( ( matcher ) => {
270- const pattern = transformCaptureGroups ( stripLookahead ( matcher . regexp ) )
271- return { function : name , pattern, name : edgeFunctionDefinition . name , cache }
272- } )
260+ const middlewareMatcherToEdgeFunctionDefinition = (
261+ matcher : MiddlewareMatcher ,
262+ name : string ,
263+ cache ?: 'manual' ,
264+ ) : {
265+ function : string
266+ name ?: string
267+ pattern : string
268+ cache ?: 'manual'
269+ } => {
270+ const pattern = transformCaptureGroups ( stripLookahead ( matcher . regexp ) )
271+ return { function : name , pattern, name, cache }
273272}
273+
274274export const cleanupEdgeFunctions = ( {
275275 INTERNAL_EDGE_FUNCTIONS_SRC = '.netlify/edge-functions' ,
276276} : NetlifyPluginConstants ) => emptyDir ( INTERNAL_EDGE_FUNCTIONS_SRC )
@@ -348,9 +348,28 @@ export const writeRscDataEdgeFunction = async ({
348348 ]
349349}
350350
351+ const getEdgeFunctionPatternForPage = ( {
352+ edgeFunctionDefinition,
353+ pageRegexMap,
354+ appPathRoutesManifest,
355+ } : {
356+ edgeFunctionDefinition : EdgeFunctionDefinitionV2
357+ pageRegexMap : Map < string , string >
358+ appPathRoutesManifest ?: Record < string , string >
359+ } ) : string => {
360+ // We don't just use the matcher from the edge function definition, because it doesn't handle trailing slashes
361+
362+ // appDir functions have a name that _isn't_ the route name, but rather the route with `/page` appended
363+ const regexp = pageRegexMap . get ( appPathRoutesManifest ?. [ edgeFunctionDefinition . page ] ?? edgeFunctionDefinition . page )
364+ // If we need to fall back to the matcher, we need to add an optional trailing slash
365+ return regexp ?? edgeFunctionDefinition . matchers [ 0 ] . regexp . replace ( / ( [ ^ / ] ) \$ $ / , '$1/?$' )
366+ }
367+
351368/**
352369 * Writes Edge Functions for the Next middleware
353370 */
371+
372+ // eslint-disable-next-line max-lines-per-function
354373export const writeEdgeFunctions = async ( {
355374 netlifyConfig,
356375 routesManifest,
@@ -415,16 +434,25 @@ export const writeEdgeFunctions = async ({
415434 for ( const middleware of middlewareManifest . sortedMiddleware ) {
416435 usesEdge = true
417436 const edgeFunctionDefinition = middlewareManifest . middleware [ middleware ]
418- const functionDefinitions = await writeEdgeFunction ( {
437+ const functionName = sanitizeName ( edgeFunctionDefinition . name )
438+ const matchers = generateEdgeFunctionMiddlewareMatchers ( {
419439 edgeFunctionDefinition,
420440 edgeFunctionRoot,
421- netlifyConfig,
422441 nextConfig,
423442 } )
424- manifest . functions . push ( ...functionDefinitions )
443+ await writeEdgeFunction ( {
444+ edgeFunctionDefinition,
445+ edgeFunctionRoot,
446+ netlifyConfig,
447+ functionName,
448+ matchers,
449+ middleware : true ,
450+ } )
451+
452+ manifest . functions . push (
453+ ...matchers . map ( ( matcher ) => middlewareMatcherToEdgeFunctionDefinition ( matcher , functionName ) ) ,
454+ )
425455 }
426- // Older versions of the manifest format don't have the functions field
427- // No, the version field was not incremented
428456 if ( typeof middlewareManifest . functions === 'object' ) {
429457 // When using the app dir, we also need to check if the EF matches a page
430458 const appPathRoutesManifest = await loadAppPathRoutesManifest ( netlifyConfig )
@@ -438,17 +466,25 @@ export const writeEdgeFunctions = async ({
438466
439467 for ( const edgeFunctionDefinition of Object . values ( middlewareManifest . functions ) ) {
440468 usesEdge = true
441- const functionDefinitions = await writeEdgeFunction ( {
469+ const functionName = sanitizeName ( edgeFunctionDefinition . name )
470+ await writeEdgeFunction ( {
442471 edgeFunctionDefinition,
443472 edgeFunctionRoot,
444473 netlifyConfig,
474+ functionName,
475+ } )
476+ const pattern = getEdgeFunctionPatternForPage ( {
477+ edgeFunctionDefinition,
445478 pageRegexMap,
446479 appPathRoutesManifest,
447- nextConfig,
480+ } )
481+ manifest . functions . push ( {
482+ function : functionName ,
483+ name : edgeFunctionDefinition . name ,
484+ pattern,
448485 // cache: "manual" is currently experimental, so we restrict it to sites that use experimental appDir
449486 cache : usesAppDir ? 'manual' : undefined ,
450487 } )
451- manifest . functions . push ( ...functionDefinitions )
452488 }
453489 }
454490 if ( usesEdge ) {
0 commit comments