Skip to content

Commit e9c1d3e

Browse files
committed
fix: detect Next.js route groups as valid project structure
The project validator failed to recognize Next.js projects using route groups (parenthesized directories like (app), (dashboard)). The isTargetFile function did an exact directory match against potential paths, missing layout files inside route group subdirectories. Added route group pattern matching to isTargetFile so that paths like app/(marketing)/layout.tsx match the potentialPath 'app'. Fixes #2936
1 parent a242be5 commit e9c1d3e

2 files changed

Lines changed: 29 additions & 1 deletion

File tree

packages/utility/src/path.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,15 @@ export const isTargetFile = (
8484
}
8585

8686
const dirName = normalize(path.dirname(targetFile));
87-
return potentialPaths.some((p) => normalize(p) === dirName);
87+
return potentialPaths.some((p) => {
88+
const normalizedP = normalize(p);
89+
if (normalizedP === dirName) return true;
90+
// Support Next.js route groups: match files inside (group) subdirectories
91+
// e.g., app/(marketing)/layout.tsx should match potentialPath 'app'
92+
const parentDir = normalize(path.dirname(dirName));
93+
const groupDir = path.basename(dirName);
94+
return parentDir === normalizedP && /^\([^)]+\)$/.test(groupDir);
95+
});
8896
};
8997

9098
export const isRootLayoutFile = (

packages/utility/test/path.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,4 +190,24 @@ describe('isTargetFile', () => {
190190
const targetFile = 'src/components/layout.tsx';
191191
expect(isRootLayoutFile(targetFile)).toBe(false);
192192
});
193+
194+
test('returns true for layout in a Next.js route group under app/', () => {
195+
expect(isRootLayoutFile('app/(marketing)/layout.tsx')).toBe(true);
196+
});
197+
198+
test('returns true for layout in a Next.js route group under src/app/', () => {
199+
expect(isRootLayoutFile('src/app/(dashboard)/layout.tsx')).toBe(true);
200+
});
201+
202+
test('returns true for layout in route group with jsx extension', () => {
203+
expect(isRootLayoutFile('app/(app)/layout.jsx')).toBe(true);
204+
});
205+
206+
test('returns false for layout nested deeper than one route group level', () => {
207+
expect(isRootLayoutFile('app/(app)/nested/layout.tsx')).toBe(false);
208+
});
209+
210+
test('returns false for layout in directory that looks like route group but is not', () => {
211+
expect(isRootLayoutFile('app/marketing/layout.tsx')).toBe(false);
212+
});
193213
});

0 commit comments

Comments
 (0)