-
Notifications
You must be signed in to change notification settings - Fork 2
migrate-auto-routes does not support @react-router/fs-routes ._index convention #16
Description
Description
migrate-auto-routes only supports the remix-flat-routes index convention (.index suffix) and does not handle the @react-router/fs-routes convention (._index suffix). This causes a path conflict error when migrating projects that use React Router v7's built-in flatRoutes.
Steps to Reproduce
Run npx migrate-auto-routes on a project using @react-router/fs-routes with the following structure:
Input (@react-router/fs-routes)
app/routes/
├── newsletter.tsx ← layout route (has <Outlet />)
├── newsletter._index.tsx ← index child route
├── newsletter.subscribe.ts
├── newsletter.error.tsx
├── products.tsx ← layout route
├── products._index/
│ └── route.tsx ← index child route (folder-based)
├── products.detail/
│ └── route.tsx
└── ...
And app/routes.ts:
import { flatRoutes } from '@react-router/fs-routes'
export default flatRoutes({ ignoredRouteFiles: ['**/*.test.{js,jsx,ts,tsx}'] })Error
$ npx migrate-auto-routes app/routes app/new-routes
❌ Path "newsletter" defined by route "app/routes/newsletter" conflicts with route "app/routes/newsletter._index"Root Cause Analysis
Two locations in src/migration/create-routes-from-folders.ts:
1. getParentRouteIds() (line 133) — parent resolution misses ._index
The dot-notation parent lookup only strips .index (remix-flat-routes convention):
if (!parentRouteId && childRouteId.endsWith(DOT_INDEX_SUFFIX)) { // DOT_INDEX_SUFFIX = '.index'
const dotNotationParentId = childRouteId.slice(0, -DOT_INDEX_SUFFIX.length)For routes/newsletter._index, stripping .index yields routes/newsletter._ — which doesn't match the parent routes/newsletter. The ._index suffix is never checked.
2. isIndexRouteId() (line 216) — does not recognize ._index
function isIndexRouteId(routeId: string): boolean {
return (
routeId === 'index' ||
routeId.endsWith('/index') ||
routeId.endsWith(DOT_INDEX_SUFFIX) // '.index' only
)
}routes/newsletter._index is not recognized as an index route, so it generates uniqueKey "newsletter" instead of "newsletter?index", conflicting with the layout route.
Context
- remix-flat-routes uses
.index(e.g.,newsletter.index.tsx) - @react-router/fs-routes uses
._index(e.g.,newsletter._index.tsx) — see source:routeId.endsWith("_index") - Since
@react-router/fs-routesis the official React Router v7 file routing solution, supporting._indexwould expand the migration tool's reach beyond remix-flat-routes users
Suggested Fix
Add ._index handling in both locations. For example:
constants.ts: Add DOT_UNDERSCORE_INDEX_SUFFIX = '._index'
isIndexRouteId():
routeId.endsWith(DOT_INDEX_SUFFIX) ||
routeId.endsWith(DOT_UNDERSCORE_INDEX_SUFFIX)getParentRouteIds(): Duplicate the existing .index parent-lookup block for ._index:
if (!parentRouteId && childRouteId.endsWith(DOT_UNDERSCORE_INDEX_SUFFIX)) {
const dotNotationParentId = childRouteId.slice(0, -DOT_UNDERSCORE_INDEX_SUFFIX.length)
if (routeIdMap.has(dotNotationParentId)) {
parentRouteId = dotNotationParentId
}
}Happy to open a PR if you'd like.