Skip to content

Commit

Permalink
馃悰 Fix parent mapping and handling of trailing _ in path
Browse files Browse the repository at this point in the history
Fixes #11
  • Loading branch information
kiliman committed May 31, 2022
1 parent 99c9b16 commit c6c1370
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 10 deletions.
25 changes: 17 additions & 8 deletions src/index.ts
Expand Up @@ -5,8 +5,12 @@ type RouteInfo = {
path: string
file: string
name: string
parent?: string // first pass parent is undefined
isIndex: boolean
parentId?: string // first pass parent is undefined
index?: boolean
caseSensitive?: boolean
}
interface RouteManifest {
[key: string]: RouteInfo
}

type DefineRouteOptions = {
Expand Down Expand Up @@ -49,7 +53,7 @@ export default function flatRoutes(
baseDir: string,
defineRoutes: DefineRoutesFunction,
options: FlatRoutesOptions = {},
) {
): RouteManifest {
const routeMap = new Map<string, RouteInfo>()
const parentMap = new Map<string, ParentMapEntry>()
const visitor = options?.visitFiles || visitFiles
Expand All @@ -59,8 +63,8 @@ export default function flatRoutes(
path: '',
file: 'root.tsx',
name: 'root',
parent: '',
isIndex: false,
parentId: '',
index: false,
})
var routes = defineRoutes(route => {
visitor(`app/${baseDir}`, routeFile => {
Expand Down Expand Up @@ -117,15 +121,15 @@ function getRoutes(
if (parentRoute && parentRoute.children) {
const routeOptions: DefineRouteOptions = {
caseSensitive: false,
index: parentRoute!.routeInfo.isIndex,
index: parentRoute!.routeInfo.index,
}
const routeChildren: DefineRouteChildren = () => {
for (let child of parentRoute!.children) {
getRoutes(parentMap, child.name, route)
const path = child.path.substring(
parentRoute!.routeInfo.path.length + 1,
)
route(path, child.file, { index: child.isIndex })
route(path, child.file, { index: child.index })
}
}
route(
Expand Down Expand Up @@ -180,7 +184,7 @@ export function getRouteInfo(
file: path.join(baseDir, routeFile),
name,
//parent: parent will be calculated after all routes are defined,
isIndex:
index:
routeSegments.at(-1) === 'index' || routeSegments.at(-1) === '_index',
}
}
Expand All @@ -190,6 +194,10 @@ function appendPathSegment(url: string, segment: string) {
if (segment.startsWith('_')) {
// handle pathless route (not included in url)
return url
} else if (segment.endsWith('_')) {
// handle parent override
segment = segment.substring(0, segment.length - 1)
url += '/' + segment
} else if (['index', '_index'].some(name => segment === name)) {
// handle index route
if (!url.endsWith('/')) {
Expand All @@ -211,5 +219,6 @@ export type {
DefineRouteFunction,
DefineRouteOptions,
DefineRouteChildren,
RouteManifest,
RouteInfo,
}
86 changes: 84 additions & 2 deletions test/__snapshots__/index.test.ts.snap
@@ -1,5 +1,87 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`define routes should allow routes to specify different parent routes 1`] = `
Object {
"routes/parent": Object {
"caseSensitive": undefined,
"file": "routes/parent.tsx",
"id": "routes/parent",
"index": undefined,
"parentId": "root",
"path": "parent",
},
"routes/parent.some.nested": Object {
"caseSensitive": undefined,
"file": "routes/parent.some.nested.tsx",
"id": "routes/parent.some.nested",
"index": undefined,
"parentId": "routes/parent",
"path": "some/nested",
},
"routes/parent.some_.nested.route": Object {
"caseSensitive": undefined,
"file": "routes/parent.some_.nested.route.tsx",
"id": "routes/parent.some_.nested.route",
"index": undefined,
"parentId": "routes/parent",
"path": "some/nested/route",
},
}
`;

exports[`define routes should correctly nest routes 1`] = `
Object {
"routes/app.$organizationSlug": Object {
"caseSensitive": undefined,
"file": "routes/app.$organizationSlug.tsx",
"id": "routes/app.$organizationSlug",
"index": undefined,
"parentId": "root",
"path": "app/:organizationSlug",
},
"routes/app.$organizationSlug.edit": Object {
"caseSensitive": undefined,
"file": "routes/app.$organizationSlug.edit.tsx",
"id": "routes/app.$organizationSlug.edit",
"index": undefined,
"parentId": "routes/app.$organizationSlug",
"path": "edit",
},
"routes/app.$organizationSlug.projects": Object {
"caseSensitive": undefined,
"file": "routes/app.$organizationSlug.projects.tsx",
"id": "routes/app.$organizationSlug.projects",
"index": undefined,
"parentId": "routes/app.$organizationSlug",
"path": "projects",
},
"routes/app.$organizationSlug.projects.$projectId": Object {
"caseSensitive": undefined,
"file": "routes/app.$organizationSlug.projects.$projectId.tsx",
"id": "routes/app.$organizationSlug.projects.$projectId",
"index": undefined,
"parentId": "routes/app.$organizationSlug.projects",
"path": ":projectId",
},
"routes/app.$organizationSlug.projects.$projectId.edit": Object {
"caseSensitive": undefined,
"file": "routes/app.$organizationSlug.projects.$projectId.edit.tsx",
"id": "routes/app.$organizationSlug.projects.$projectId.edit",
"index": undefined,
"parentId": "routes/app.$organizationSlug.projects.$projectId",
"path": "edit",
},
"routes/app.$organizationSlug.projects.new": Object {
"caseSensitive": undefined,
"file": "routes/app.$organizationSlug.projects.new.tsx",
"id": "routes/app.$organizationSlug.projects.new",
"index": undefined,
"parentId": "routes/app.$organizationSlug.projects",
"path": "new",
},
}
`;

exports[`define routes should define routes for complex structure 1`] = `
Object {
"routes/_auth": Object {
Expand Down Expand Up @@ -120,15 +202,15 @@ Object {
"id": "routes/app_.projects.$id.roadmap",
"index": undefined,
"parentId": "root",
"path": "app_/projects/:id/roadmap",
"path": "app/projects/:id/roadmap",
},
"routes/app_.projects.$id.roadmap[.pdf]": Object {
"caseSensitive": undefined,
"file": "routes/app_.projects.$id.roadmap[.pdf].tsx",
"id": "routes/app_.projects.$id.roadmap[.pdf]",
"index": undefined,
"parentId": "root",
"path": "app_/projects/:id/roadmap.pdf",
"path": "app/projects/:id/roadmap.pdf",
},
}
`;
Expand Down
77 changes: 77 additions & 0 deletions test/index.test.ts
@@ -1,4 +1,5 @@
import { defineRoutes } from '@remix-run/dev/config/routes'
import type { RouteManifest } from '../src/index'
import flatRoutes from '../src/index'

describe('define routes', () => {
Expand Down Expand Up @@ -69,6 +70,32 @@ describe('define routes', () => {
expect(routes).toMatchSnapshot()
})

it('should correctly nest routes', () => {
const routeList = [
'app.$organizationSlug.tsx',
'app.$organizationSlug.edit.tsx',
'app.$organizationSlug.projects.tsx',
'app.$organizationSlug.projects.$projectId.tsx',
'app.$organizationSlug.projects.$projectId.edit.tsx',
'app.$organizationSlug.projects.new.tsx',
]
const routes = flatRoutes('routes', defineRoutes, {
visitFiles: visitFilesFromArray(routeList),
})
expect(routes).toMatchSnapshot()
})
it('should allow routes to specify different parent routes', () => {
const routeList = [
'parent.tsx',
'parent.some.nested.tsx',
'parent.some_.nested.route.tsx',
]
const routes = flatRoutes('routes', defineRoutes, {
visitFiles: visitFilesFromArray(routeList),
})
expect(routes).toMatchSnapshot()
})

it('should ignore non-route files in flat-folders', () => {
const flatFolders = [
'$lang.$ref/_layout.tsx',
Expand Down Expand Up @@ -107,3 +134,53 @@ function visitFilesFromArray(files: string[]) {
})
}
}

type RouteMapInfo = {
id: string
path: string
file: string
index?: boolean
children: string[]
}
function dumpRoutes(routes: RouteManifest) {
const routeMap = new Map<string, RouteMapInfo>()
const rootRoute: RouteMapInfo = {
id: 'root',
path: '',
file: 'root.tsx',
index: false,
children: [],
}
routeMap.set('root', rootRoute)
Object.entries(routes).forEach(([name, route]) => {
if (!route.parentId) return
const parent = routeMap.get(route.parentId)
if (parent) {
parent.children.push(name)
}
routeMap.set(name, {
id: name,
path: route.path,
file: route.file,
index: route.index,
children: [],
})
})
const dump = (route: RouteMapInfo, indent: string) => {
const getPath = (path?: string) => (path ? `path="${path}" ` : '')
const getIndex = (index?: boolean) => (index ? 'index ' : '')
output += `${indent}<Route ${getIndex(route.index)}${getPath(
route.path,
)}file="${route.file}"${route.children ? '' : ' /'}>\n`
if (route.children.length) {
route.children.forEach((childId: string) => {
dump(routeMap.get(childId)!, indent + ' ')
})
output += `${indent}</Route>\n`
}
}
let output = '<Routes>\n'
dump(routeMap.get('root')!, ' ')
output += '</Routes>\n'
console.log(output)
}

0 comments on commit c6c1370

Please sign in to comment.