@@ -3,7 +3,12 @@ import { join } from 'node:path/posix'
33
44import { glob } from 'fast-glob'
55
6- import type { RoutingRule , RoutingRuleRedirect , RoutingRuleRewrite } from '../run/routing.js'
6+ import type {
7+ RoutingRule ,
8+ RoutingRuleApply ,
9+ RoutingRuleRedirect ,
10+ RoutingRuleRewrite ,
11+ } from '../run/routing.js'
712
813import {
914 DISPLAY_NAME_ROUTING ,
@@ -80,10 +85,18 @@ export function convertDynamicRouteToRoutingRule(
8085 } satisfies RoutingRuleRewrite
8186}
8287
88+ const matchOperatorsRegex = / [ | \\ { } ( ) [ \] ^ $ + * ? . - ] / g
89+
90+ export function escapeStringRegexp ( str : string ) : string {
91+ return str . replace ( matchOperatorsRegex , '\\$&' )
92+ }
93+
8394export async function generateRoutingRules (
8495 nextAdapterContext : OnBuildCompleteContext ,
8596 netlifyAdapterContext : NetlifyAdapterContext ,
8697) {
98+ const escapedBuildId = escapeStringRegexp ( nextAdapterContext . buildId )
99+
87100 const hasMiddleware = Boolean ( nextAdapterContext . outputs . middleware )
88101 const hasPages =
89102 nextAdapterContext . outputs . pages . length !== 0 ||
@@ -148,7 +161,7 @@ export async function generateRoutingRules(
148161 {
149162 description : 'Normalize _next/data' ,
150163 match : {
151- path : `^${ nextAdapterContext . config . basePath } /_next/data/${ await netlifyAdapterContext . getBuildId ( ) } /(.*)\\.json` ,
164+ path : `^${ nextAdapterContext . config . basePath } /_next/data/${ escapedBuildId } /(.*)\\.json` ,
152165 has : [
153166 {
154167 type : 'header' ,
@@ -211,7 +224,7 @@ export async function generateRoutingRules(
211224 } ,
212225 apply : {
213226 type : 'rewrite' ,
214- destination : `${ nextAdapterContext . config . basePath } /_next/data/${ await netlifyAdapterContext . getBuildId ( ) } /$1.json` ,
227+ destination : `${ nextAdapterContext . config . basePath } /_next/data/${ nextAdapterContext . buildId } /$1.json` ,
215228 } ,
216229 } ,
217230 ]
@@ -432,8 +445,44 @@ export async function generateRoutingRules(
432445 // apply normal dynamic routes
433446 ...dynamicRoutes , // originally: ...convertedDynamicRoutes,
434447
435- // apply x-nextjs-matched-path header and __next_data_catchall rewrite
436- // if middleware + pages
448+ ...( hasMiddleware && hasPages
449+ ? [
450+ // apply x-nextjs-matched-path header
451+ // if middleware + pages
452+ {
453+ description : 'Apply x-nextjs-matched-path header if middleware + pages' ,
454+ match : {
455+ path : `^${ join (
456+ '/' ,
457+ nextAdapterContext . config . basePath ,
458+ '/_next/data/' ,
459+ escapedBuildId ,
460+ '/(.*).json' ,
461+ ) } `,
462+ } ,
463+ apply : {
464+ type : 'apply' ,
465+ headers : {
466+ 'x-nextjs-matched-path' : '/$1' ,
467+ } ,
468+ } ,
469+ continue : true ,
470+ } satisfies RoutingRuleApply ,
471+ {
472+ // apply __next_data_catchall rewrite
473+ // if middleware + pages
474+ description : 'Apply __next_data_catchall rewrite if middleware + pages' ,
475+ match : {
476+ path : `` ,
477+ } ,
478+ apply : {
479+ type : 'rewrite' ,
480+ destination : '__next_data_catchall' ,
481+ statusCode : 200 ,
482+ } ,
483+ } satisfies RoutingRule ,
484+ ]
485+ : [ ] ) ,
437486
438487 {
439488 // originally: handle: 'hit' },
0 commit comments