-
Notifications
You must be signed in to change notification settings - Fork 2
Migration CLI mishandles remix-flat-routes folder route pattern (route.tsx) #14
Description
Description
When running npx migrate-auto-routes on a project that uses the remix-flat-routes folder route pattern (route.tsx inside sub-folders), route files are misclassified as colocated files and placed under +-prefixed folders, making them invisible to the router.
Steps to Reproduce
Run npx migrate-auto-routes on a project with the following structure:
Input (remix-flat-routes)
app/routes/
├── demo+/
│ ├── _layout/route.tsx ← route file
│ ├── _index/route.tsx ← route file
│ ├── about/route.tsx ← route file
│ ├── conform.nested-array/
│ │ ├── route.tsx ← route file
│ │ ├── components/ ← colocated
│ │ ├── schema.ts ← colocated
│ │ └── faker.server.ts ← colocated
│ └── ...
Actual Output (buggy)
app/routes/
├── demo/
│ ├── +_layout/route.tsx ← folder gets + prefix → ignored by router!
│ ├── +_index/route.tsx ← same
│ ├── +about/route.tsx ← same
│ ├── +conform.nested-array/ ← same
│ │ ├── route.tsx
│ │ ├── components/
│ │ └── ...
The CLI's snapshot comparison catches the mismatch and rolls back, so no data is lost — but the migration cannot complete.
Root Cause Analysis
I found 5 interrelated bugs. Fixes for all 5 are in my fork: coji/react-router-auto-routes@fix/folder-route-migration. All 124 existing tests pass.
Bug 1: isColocatedFile() misclassifies route files as colocated
File: src/migration/fs-helpers.ts
isColocatedFile() checks whether a path inside a +-suffixed folder contains a "regular" (non-+, non-special) directory segment, and if so, marks the file as colocated. However, in remix-flat-routes, demo+/about/route.tsx has about/ as a route folder, not a colocated directory.
Example: demo+/about/route.tsx
demo+→ has+→ flat files conventionabout→ no+, no_, no()→ misclassified as colocated
This causes scanRouteModules() to find only 14 routes instead of the expected 39.
Fix: When only one regular directory segment exists after the last + folder and the file is a route entry (route.tsx, index.tsx, etc.), return false.
Bug 2: scanRouteModules() generates incorrect route IDs for route.tsx
File: src/migration/route-scanner.ts
Route IDs for route.tsx files include an extra /route segment (e.g., app/routes/demo+/_index/route instead of app/routes/demo+/_index), causing createRoutesFromFolders to fail at building the route tree.
Additionally, createRouteId() strips dot-segments as file extensions, so ($lang).biography becomes ($lang).
Fix: When basename is route, use path.dirname() directly as the route ID instead of createRouteId().
Bug 3: normalizeSnapshotRouteFilePath() doesn't normalize folder route paths
File: src/migration/normalizers.ts
The before/after snapshot comparison fails because routes/demo/about/route.tsx (before) doesn't match routes/demo/about.tsx (after). The _layout segment has skip logic but route does not.
Fix: Add route to the segment skip logic, similar to how _layout is handled.
Bug 4: +types/route import specifiers are not rewritten
File: src/migration/import-rewriter.ts
When route.tsx is converted to a flat file (e.g., about.tsx), ./+types/route should become ./+types/about. Since +types/ is a virtual directory generated by React Router, filesystem-based resolution doesn't work.
Fix: Add rewriteTypesRouteSpecifier() that detects +types/route specifiers and rewrites them based on the target filename.
Bug 5: index.ts extension in imports is not stripped
File: src/migration/import-rewriter.ts
Source code with explicit .ts extension imports like ./components/index.ts keeps the extension after migration, causing TypeScript errors without allowImportingTsExtensions.
Fix: Add stripTsExtension() that removes /index.ts and /index.tsx suffixes from import specifiers.
Files Changed
src/migration/fs-helpers.ts—isColocatedFile()fixsrc/migration/route-scanner.ts— route ID generation forroute.tsxsrc/migration/normalizers.ts—routesegment skip in snapshot normalizationsrc/migration/import-rewriter.ts—rewriteTypesRouteSpecifier()andstripTsExtension()src/migration/migrate.ts— route source path exclusion filter incollectColocatedMappings
Branch with Fix
https://github.com/coji/react-router-auto-routes/tree/fix/folder-route-migration
Happy to open a PR if you'd like.