diff --git a/packages/nextjs/src/config/manifest/createRouteManifest.ts b/packages/nextjs/src/config/manifest/createRouteManifest.ts index 1e905d858f73..32e7db61b57b 100644 --- a/packages/nextjs/src/config/manifest/createRouteManifest.ts +++ b/packages/nextjs/src/config/manifest/createRouteManifest.ts @@ -10,6 +10,10 @@ export type CreateRouteManifestOptions = { * By default, route groups are stripped from paths following Next.js convention. */ includeRouteGroups?: boolean; + /** + * Base path for the application, if any. This will be prefixed to all routes. + */ + basePath?: string; }; let manifestCache: RouteManifest | null = null; @@ -192,7 +196,7 @@ export function createRouteManifest(options?: CreateRouteManifestOptions): Route return manifestCache; } - const { dynamicRoutes, staticRoutes } = scanAppDirectory(targetDir, '', options?.includeRouteGroups); + const { dynamicRoutes, staticRoutes } = scanAppDirectory(targetDir, options?.basePath, options?.includeRouteGroups); const manifest: RouteManifest = { dynamicRoutes, diff --git a/packages/nextjs/src/config/withSentryConfig.ts b/packages/nextjs/src/config/withSentryConfig.ts index ddf761998e50..9c82e3af017c 100644 --- a/packages/nextjs/src/config/withSentryConfig.ts +++ b/packages/nextjs/src/config/withSentryConfig.ts @@ -147,7 +147,9 @@ function getFinalConfigObject( let routeManifest: RouteManifest | undefined; if (!userSentryOptions.disableManifestInjection) { - routeManifest = createRouteManifest(); + routeManifest = createRouteManifest({ + basePath: incomingUserNextConfigObject.basePath, + }); } setUpBuildTimeVariables(incomingUserNextConfigObject, userSentryOptions, releaseName); diff --git a/packages/nextjs/test/config/manifest/suites/base-path/app/about/page.tsx b/packages/nextjs/test/config/manifest/suites/base-path/app/about/page.tsx new file mode 100644 index 000000000000..e5752fa903b7 --- /dev/null +++ b/packages/nextjs/test/config/manifest/suites/base-path/app/about/page.tsx @@ -0,0 +1 @@ +// about page diff --git a/packages/nextjs/test/config/manifest/suites/base-path/app/api/test/page.tsx b/packages/nextjs/test/config/manifest/suites/base-path/app/api/test/page.tsx new file mode 100644 index 000000000000..ec89ef596f93 --- /dev/null +++ b/packages/nextjs/test/config/manifest/suites/base-path/app/api/test/page.tsx @@ -0,0 +1 @@ +// API test page diff --git a/packages/nextjs/test/config/manifest/suites/base-path/app/page.tsx b/packages/nextjs/test/config/manifest/suites/base-path/app/page.tsx new file mode 100644 index 000000000000..768d7a4f7757 --- /dev/null +++ b/packages/nextjs/test/config/manifest/suites/base-path/app/page.tsx @@ -0,0 +1 @@ +// root page diff --git a/packages/nextjs/test/config/manifest/suites/base-path/app/users/[id]/page.tsx b/packages/nextjs/test/config/manifest/suites/base-path/app/users/[id]/page.tsx new file mode 100644 index 000000000000..a7307090717b --- /dev/null +++ b/packages/nextjs/test/config/manifest/suites/base-path/app/users/[id]/page.tsx @@ -0,0 +1 @@ +// users id dynamic page diff --git a/packages/nextjs/test/config/manifest/suites/base-path/base-path.test.ts b/packages/nextjs/test/config/manifest/suites/base-path/base-path.test.ts new file mode 100644 index 000000000000..e442f77af8a6 --- /dev/null +++ b/packages/nextjs/test/config/manifest/suites/base-path/base-path.test.ts @@ -0,0 +1,49 @@ +import path from 'path'; +import { describe, expect, test } from 'vitest'; +import { createRouteManifest } from '../../../../../src/config/manifest/createRouteManifest'; + +describe('basePath', () => { + test('should generate routes with base path prefix', () => { + const manifest = createRouteManifest({ + basePath: '/my-app', + appDirPath: path.join(__dirname, 'app') + }); + + expect(manifest).toEqual({ + staticRoutes: [ + { path: '/my-app' }, + { path: '/my-app/about' }, + { path: '/my-app/api/test' } + ], + dynamicRoutes: [ + { + path: '/my-app/users/:id', + regex: '^/my-app/users/([^/]+)$', + paramNames: ['id'], + } + ], + }); + }); + + test('should validate dynamic route regex with base path', () => { + const manifest = createRouteManifest({ + basePath: '/my-app', + appDirPath: path.join(__dirname, 'app') + }); + + const dynamicRoute = manifest.dynamicRoutes.find(route => route.path === '/my-app/users/:id'); + const regex = new RegExp(dynamicRoute?.regex ?? ''); + + // Should match valid paths with base path + expect(regex.test('/my-app/users/123')).toBe(true); + expect(regex.test('/my-app/users/john-doe')).toBe(true); + + // Should not match paths without base path + expect(regex.test('/users/123')).toBe(false); + + // Should not match invalid paths + expect(regex.test('/my-app/users/')).toBe(false); + expect(regex.test('/my-app/users/123/extra')).toBe(false); + expect(regex.test('/my-app/user/123')).toBe(false); + }); +});