Skip to content

Commit 6071f76

Browse files
committed
split routes again
1 parent f593c49 commit 6071f76

File tree

8 files changed

+60
-42
lines changed

8 files changed

+60
-42
lines changed

packages/nextjs/src/config/manifest/createRouteManifest.ts

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ function isRouteGroup(name: string): boolean {
1818
return name.startsWith('(') && name.endsWith(')');
1919
}
2020

21+
function normalizeRoutePath(routePath: string): string {
22+
// Remove route group segments from the path
23+
return routePath.replace(/\/\([^)]+\)/g, '');
24+
}
25+
2126
function getDynamicRouteSegment(name: string): string {
2227
if (name.startsWith('[[...') && name.endsWith(']]')) {
2328
// Optional catchall: [[...param]]
@@ -77,62 +82,65 @@ function buildRegexForDynamicRoute(routePath: string): { regex: string; paramNam
7782
return { regex: pattern, paramNames };
7883
}
7984

80-
function scanAppDirectory(dir: string, basePath: string = ''): RouteInfo[] {
81-
const routes: RouteInfo[] = [];
85+
function scanAppDirectory(
86+
dir: string,
87+
basePath: string = '',
88+
): { dynamicRoutes: RouteInfo[]; staticRoutes: RouteInfo[] } {
89+
const dynamicRoutes: RouteInfo[] = [];
90+
const staticRoutes: RouteInfo[] = [];
8291

8392
try {
8493
const entries = fs.readdirSync(dir, { withFileTypes: true });
8594
const pageFile = entries.some(entry => isPageFile(entry.name));
8695

8796
if (pageFile) {
88-
const routePath = basePath || '/';
89-
const isDynamic = routePath.includes(':');
97+
// Normalize the path by removing route groups when creating the final route
98+
const normalizedRoutePath = normalizeRoutePath(basePath || '/');
99+
const isDynamic = normalizedRoutePath.includes(':');
90100

91101
if (isDynamic) {
92-
const { regex, paramNames } = buildRegexForDynamicRoute(routePath);
93-
routes.push({
94-
path: routePath,
102+
const { regex, paramNames } = buildRegexForDynamicRoute(normalizedRoutePath);
103+
dynamicRoutes.push({
104+
path: normalizedRoutePath,
95105
regex,
96106
paramNames,
97107
});
98108
} else {
99-
routes.push({
100-
path: routePath,
109+
staticRoutes.push({
110+
path: normalizedRoutePath,
101111
});
102112
}
103113
}
104114

105115
for (const entry of entries) {
106116
if (entry.isDirectory()) {
107117
const fullPath = path.join(dir, entry.name);
108-
109-
if (isRouteGroup(entry.name)) {
110-
// Route groups don't affect the URL, just scan them
111-
const subRoutes = scanAppDirectory(fullPath, basePath);
112-
routes.push(...subRoutes);
113-
continue;
114-
}
118+
let routeSegment: string;
115119

116120
const isDynamic = entry.name.startsWith('[') && entry.name.endsWith(']');
117-
let routeSegment: string;
121+
const isRouteGroupDir = isRouteGroup(entry.name);
118122

119-
if (isDynamic) {
123+
if (isRouteGroupDir) {
124+
routeSegment = entry.name;
125+
} else if (isDynamic) {
120126
routeSegment = getDynamicRouteSegment(entry.name);
121127
} else {
122128
routeSegment = entry.name;
123129
}
124130

125131
const newBasePath = `${basePath}/${routeSegment}`;
126132
const subRoutes = scanAppDirectory(fullPath, newBasePath);
127-
routes.push(...subRoutes);
133+
134+
dynamicRoutes.push(...subRoutes.dynamicRoutes);
135+
staticRoutes.push(...subRoutes.staticRoutes);
128136
}
129137
}
130138
} catch (error) {
131139
// eslint-disable-next-line no-console
132140
console.warn('Error building route manifest:', error);
133141
}
134142

135-
return routes;
143+
return { dynamicRoutes, staticRoutes };
136144
}
137145

138146
/**
@@ -157,7 +165,8 @@ export function createRouteManifest(options?: CreateRouteManifestOptions): Route
157165

158166
if (!targetDir) {
159167
return {
160-
routes: [],
168+
dynamicRoutes: [],
169+
staticRoutes: [],
161170
};
162171
}
163172

@@ -166,10 +175,11 @@ export function createRouteManifest(options?: CreateRouteManifestOptions): Route
166175
return manifestCache;
167176
}
168177

169-
const routes = scanAppDirectory(targetDir);
178+
const { dynamicRoutes, staticRoutes } = scanAppDirectory(targetDir);
170179

171180
const manifest: RouteManifest = {
172-
routes,
181+
dynamicRoutes,
182+
staticRoutes,
173183
};
174184

175185
// set cache

packages/nextjs/src/config/manifest/types.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,12 @@ export type RouteInfo = {
2121
*/
2222
export type RouteManifest = {
2323
/**
24-
* List of all routes (static and dynamic)
24+
* List of all dynamic routes
2525
*/
26-
routes: RouteInfo[];
26+
dynamicRoutes: RouteInfo[];
27+
28+
/**
29+
* List of all static routes
30+
*/
31+
staticRoutes: RouteInfo[];
2732
};

packages/nextjs/test/config/manifest/suites/catchall/catchall.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ describe('catchall', () => {
77

88
test('should generate a manifest with catchall route', () => {
99
expect(manifest).toEqual({
10-
routes: [
11-
{ path: '/' },
10+
staticRoutes: [{ path: '/' }],
11+
dynamicRoutes: [
1212
{
1313
path: '/catchall/:path*?',
1414
regex: '^/catchall(?:/(.*))?$',
@@ -19,7 +19,7 @@ describe('catchall', () => {
1919
});
2020

2121
test('should generate correct pattern for catchall route', () => {
22-
const catchallRoute = manifest.routes.find(route => route.path === '/catchall/:path*?');
22+
const catchallRoute = manifest.dynamicRoutes.find(route => route.path === '/catchall/:path*?');
2323
const regex = new RegExp(catchallRoute?.regex ?? '');
2424
expect(regex.test('/catchall/123')).toBe(true);
2525
expect(regex.test('/catchall/abc')).toBe(true);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// Static

packages/nextjs/test/config/manifest/suites/dynamic/dynamic.test.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@ describe('dynamic', () => {
77

88
test('should generate a dynamic manifest', () => {
99
expect(manifest).toEqual({
10-
routes: [
11-
{ path: '/' },
10+
staticRoutes: [{ path: '/' }, { path: '/dynamic/static' }, { path: '/static/nested' }],
11+
dynamicRoutes: [
1212
{
1313
path: '/dynamic/:id',
1414
regex: '^/dynamic/([^/]+)$',
1515
paramNames: ['id'],
1616
},
17-
{ path: '/static/nested' },
1817
{
1918
path: '/users/:id',
2019
regex: '^/users/([^/]+)$',
@@ -35,7 +34,7 @@ describe('dynamic', () => {
3534
});
3635

3736
test('should generate correct pattern for single dynamic route', () => {
38-
const singleDynamic = manifest.routes.find(route => route.path === '/dynamic/:id');
37+
const singleDynamic = manifest.dynamicRoutes.find(route => route.path === '/dynamic/:id');
3938
const regex = new RegExp(singleDynamic?.regex ?? '');
4039
expect(regex.test('/dynamic/123')).toBe(true);
4140
expect(regex.test('/dynamic/abc')).toBe(true);
@@ -45,7 +44,7 @@ describe('dynamic', () => {
4544
});
4645

4746
test('should generate correct pattern for mixed static-dynamic route', () => {
48-
const mixedRoute = manifest.routes.find(route => route.path === '/users/:id/settings');
47+
const mixedRoute = manifest.dynamicRoutes.find(route => route.path === '/users/:id/settings');
4948
const regex = new RegExp(mixedRoute?.regex ?? '');
5049

5150
expect(regex.test('/users/123/settings')).toBe(true);
@@ -56,7 +55,7 @@ describe('dynamic', () => {
5655
});
5756

5857
test('should generate correct pattern for multiple dynamic segments', () => {
59-
const multiDynamic = manifest.routes.find(route => route.path === '/users/:id/posts/:postId');
58+
const multiDynamic = manifest.dynamicRoutes.find(route => route.path === '/users/:id/posts/:postId');
6059
const regex = new RegExp(multiDynamic?.regex ?? '');
6160

6261
expect(regex.test('/users/123/posts/456')).toBe(true);
@@ -72,8 +71,7 @@ describe('dynamic', () => {
7271
});
7372

7473
test('should handle special characters in dynamic segments', () => {
75-
// Test that dynamic segments with special characters work properly
76-
const userSettingsRoute = manifest.routes.find(route => route.path === '/users/:id/settings');
74+
const userSettingsRoute = manifest.dynamicRoutes.find(route => route.path === '/users/:id/settings');
7775
expect(userSettingsRoute).toBeDefined();
7876
expect(userSettingsRoute?.regex).toBeDefined();
7977

packages/nextjs/test/config/manifest/suites/file-extensions/file-extensions.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ describe('file-extensions', () => {
77

88
test('should detect page files with all supported extensions', () => {
99
expect(manifest).toEqual({
10-
routes: [
10+
staticRoutes: [
1111
{ path: '/' },
1212
{ path: '/javascript' },
1313
{ path: '/jsx-route' },
1414
{ path: '/mixed' },
1515
{ path: '/precedence' },
1616
{ path: '/typescript' },
1717
],
18+
dynamicRoutes: [],
1819
});
1920
});
2021
});

packages/nextjs/test/config/manifest/suites/route-groups/route-groups.test.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,26 @@ describe('route-groups', () => {
77

88
test('should generate a manifest with route groups', () => {
99
expect(manifest).toEqual({
10-
routes: [
10+
staticRoutes: [
1111
{ path: '/' },
1212
{ path: '/login' },
1313
{ path: '/signup' },
1414
{ path: '/dashboard' },
15+
{ path: '/settings/profile' },
16+
{ path: '/public/about' },
17+
],
18+
dynamicRoutes: [
1519
{
1620
path: '/dashboard/:id',
1721
regex: '^/dashboard/([^/]+)$',
1822
paramNames: ['id'],
1923
},
20-
{ path: '/settings/profile' },
21-
{ path: '/public/about' },
2224
],
2325
});
2426
});
2527

2628
test('should handle dynamic routes within route groups', () => {
27-
const dynamicRoute = manifest.routes.find(route => route.path.includes('/dashboard/:id'));
29+
const dynamicRoute = manifest.dynamicRoutes.find(route => route.path.includes('/dashboard/:id'));
2830
const regex = new RegExp(dynamicRoute?.regex ?? '');
2931
expect(regex.test('/dashboard/123')).toBe(true);
3032
expect(regex.test('/dashboard/abc')).toBe(true);

packages/nextjs/test/config/manifest/suites/static/static.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ describe('static', () => {
66
test('should generate a static manifest', () => {
77
const manifest = createRouteManifest({ appDirPath: path.join(__dirname, 'app') });
88
expect(manifest).toEqual({
9-
routes: [{ path: '/' }, { path: '/some/nested' }, { path: '/user' }, { path: '/users' }],
9+
staticRoutes: [{ path: '/' }, { path: '/some/nested' }, { path: '/user' }, { path: '/users' }],
10+
dynamicRoutes: [],
1011
});
1112
});
1213
});

0 commit comments

Comments
 (0)