diff --git a/packages/runtime/src/helpers/edge.ts b/packages/runtime/src/helpers/edge.ts index 7b0e2bbd5f..ea491bc353 100644 --- a/packages/runtime/src/helpers/edge.ts +++ b/packages/runtime/src/helpers/edge.ts @@ -348,12 +348,12 @@ export const writeRscDataEdgeFunction = async ({ ] } -const getEdgeFunctionPatternForPage = ({ +export const getEdgeFunctionPatternForPage = ({ edgeFunctionDefinition, pageRegexMap, appPathRoutesManifest, }: { - edgeFunctionDefinition: EdgeFunctionDefinitionV2 + edgeFunctionDefinition: EdgeFunctionDefinition pageRegexMap: Map appPathRoutesManifest?: Record }): string => { @@ -361,8 +361,14 @@ const getEdgeFunctionPatternForPage = ({ // appDir functions have a name that _isn't_ the route name, but rather the route with `/page` appended const regexp = pageRegexMap.get(appPathRoutesManifest?.[edgeFunctionDefinition.page] ?? edgeFunctionDefinition.page) + if (regexp) { + return regexp + } + if ('regexp' in edgeFunctionDefinition) { + return edgeFunctionDefinition.regexp.replace(/([^/])\$$/, '$1/?$') + } // If we need to fall back to the matcher, we need to add an optional trailing slash - return regexp ?? edgeFunctionDefinition.matchers[0].regexp.replace(/([^/])\$$/, '$1/?$') + return edgeFunctionDefinition.matchers?.[0].regexp.replace(/([^/])\$$/, '$1/?$') } /** diff --git a/test/matchers.spec.ts b/test/matchers.spec.ts index 5cb1c75ee3..b5d3e38697 100644 --- a/test/matchers.spec.ts +++ b/test/matchers.spec.ts @@ -1,5 +1,5 @@ import { makeLocaleOptional, stripLookahead } from '../packages/runtime/src/helpers/matchers' - +import { getEdgeFunctionPatternForPage } from '../packages/runtime/src/helpers/edge' const makeDataPath = (path: string) => `/_next/data/build-id${path === '/' ? '/index' : path}.json` function checkPath(path: string, regex: string) { @@ -68,3 +68,109 @@ describe('the middleware path matcher', () => { expect(checkPath('/shows/888', stripped)).toBe(true) }) }) + +const pageRegexMap = new Map([ + ['/api/shows/[id]', '^/api/shows/([^/]+?)(?:/)?$'], + ['/api/shows/[...params]', '^/api/shows/(.+?)(?:/)?$'], + ['/app-edge/[id]', '^/app\\-edge/([^/]+?)(?:/)?$'], + ['/blog/[author]', '^/blog/([^/]+?)(?:/)?$'], + ['/blog/[author]/[slug]', '^/blog/([^/]+?)/([^/]+?)(?:/)?$'], + ['/getServerSideProps/all/[[...slug]]', '^/getServerSideProps/all(?:/(.+?))?(?:/)?$'], + ['/getServerSideProps/[id]', '^/getServerSideProps/([^/]+?)(?:/)?$'], +]) + +const appPathRoutesManifest = { + '/app-edge/[id]/page': '/app-edge/[id]', + '/blog/[author]/[slug]/page': '/blog/[author]/[slug]', + '/blog/[author]/page': '/blog/[author]', +} + +describe('the edge function matcher helpers', () => { + it('finds the correct regex for an edge API route', () => { + const regex = getEdgeFunctionPatternForPage({ + pageRegexMap, + appPathRoutesManifest, + edgeFunctionDefinition: { + name: 'pages/api/og', + page: '/api/og', + env: [], + files: [], + matchers: [ + { + regexp: '^/api/og$', + }, + ], + wasm: [], + assets: [], + }, + }) + expect(regex).toBe('^/api/og/?$') + expect('/api/og').toMatch(new RegExp(regex)) + expect('/api/og/').toMatch(new RegExp(regex)) + }) + + it('finds the correct regex for an appDir page with a dynamic route', () => { + const regex = getEdgeFunctionPatternForPage({ + pageRegexMap, + appPathRoutesManifest, + edgeFunctionDefinition: { + env: [], + files: [], + name: 'app/app-edge/[id]/page', + page: '/app-edge/[id]/page', + matchers: [ + { + regexp: '^/app\\-edge/(?[^/]+?)$', + }, + ], + wasm: [], + assets: [], + }, + }) + expect(regex).toBe('^/app\\-edge/([^/]+?)(?:/)?$') + expect('/app-edge/1').toMatch(new RegExp(regex)) + expect('/app-edge/1/').toMatch(new RegExp(regex)) + }) + + it('finds the correct regex for a pages edge route', () => { + const regex = getEdgeFunctionPatternForPage({ + pageRegexMap, + appPathRoutesManifest, + edgeFunctionDefinition: { + env: [], + files: [], + name: 'pages/edge/[id]', + page: '/edge/[id]', + matchers: [ + { + regexp: '^/edge/(?[^/]+?)$', + }, + ], + wasm: [], + assets: [], + }, + }) + expect(regex).toBe('^/edge/(?[^/]+?)/?$') + expect('/edge/1').toMatch(new RegExp(regex)) + expect('/edge/1/').toMatch(new RegExp(regex)) + }) + + it('finds the correct regex for a pages edge route with a v1 definition', () => { + const regex = getEdgeFunctionPatternForPage({ + pageRegexMap, + appPathRoutesManifest, + edgeFunctionDefinition: { + env: [], + files: [], + name: 'pages/edge/[id]', + page: '/edge/[id]', + regexp: '^/edge/(?[^/]+?)$', + wasm: [], + assets: [], + }, + }) + expect(regex).toBe('^/edge/(?[^/]+?)/?$') + expect('/edge/1').toMatch(new RegExp(regex)) + expect('/edge/1/').toMatch(new RegExp(regex)) + }) +})