Skip to content

Commit

Permalink
feat: introduces support for nested wildcard routes (modular navigation)
Browse files Browse the repository at this point in the history
feat: introduces support for nested wildcard routes (modular navigation)
  • Loading branch information
Tiagoperes authored Aug 9, 2024
2 parents fc331d9 + 899806c commit eaaaa8e
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 8 deletions.
2 changes: 1 addition & 1 deletion packages/cli/src/Codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export class Codegen {
const name = this.keyToClassName.get(route.localKey)
const parentName = route.parent ? this.keyToClassName.get(route.parent.localKey) : 'undefined'
const params = route.parent ? `parent: ${parentName}` : ''
const path = route.path.map(p => typeof p === 'string' ? p : `{${p.name}}`).join('/')
const path = route.path.map(p => typeof p === 'string' ? p : `{${p.name}}`).join('/').replace(/\/\*\//g, '/')
return `
class ${name} extends Route<${parentName}, RouteParams['${route.localKey}']> {
constructor(${params}) {
Expand Down
34 changes: 34 additions & 0 deletions packages/cli/test/__snapshots__/generate.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ interface RouteParams {
'root': void,
'root.account': void,
'root.studios': { search?: string },
'root.studios.studio': { studioId: string },
'root.studios.studio.stacks': { studioId: string },
'root.download': { platform?: 'mac' | 'debian' | 'windows' }
}
Expand Down Expand Up @@ -254,6 +256,34 @@ class StudiosRoute extends Route<RootRoute, RouteParams['root.studios']> {
)
}
studio = new StudioRoute(this)
}
class StudioRoute extends Route<StudiosRoute, RouteParams['root.studios.studio']> {
constructor(parent: StudiosRoute) {
super(
'root.studios.studio',
'/studios/{studioId}/*', parent,
{ studioId: 'string' },
)
}
stacks = new StacksRoute(this)
}
class StacksRoute extends Route<StudioRoute, RouteParams['root.studios.studio.stacks']> {
constructor(parent: StudioRoute) {
super(
'root.studios.studio.stacks',
'/studios/{studioId}/stacks/*', parent,
{ studioId: 'string' },
)
}
}
Expand All @@ -280,6 +310,8 @@ const routeByKey: RouteByKey = {
'root': root,
'root.account': root.account,
'root.studios': root.studios,
'root.studios.studio': root.studios.studio,
'root.studios.studio.stacks': root.studios.studio.stacks,
'root.download': root.download
}
Expand All @@ -289,6 +321,8 @@ interface RouteByKey {
'root': RootRoute,
'root.account': AccountRoute,
'root.studios': StudiosRoute,
'root.studios.studio': StudioRoute,
'root.studios.studio.stacks': StacksRoute,
'root.download': DownloadRoute
}
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/test/navigation.parent.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@
+ account (/account/*):
+ studios (/studios/*):
search: string
+ studio (/{studioId}/*):
+ stacks (/stacks/*):
+ download (/download):
platform: string ('mac' | 'debian' | 'windows')
31 changes: 24 additions & 7 deletions packages/runtime/src/CitronNavigator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,28 @@ export class CitronNavigator {
return CitronNavigator.instance
}

/**
* Copies every child route of `source` to `target` if the child route of `source` doesn't exist in `target`.
*
* If a child of `source` exists in `target`, but it's path includes a wildcard (/*), we recursively copy its children to the same route
* in `target`.
* @param source the route to have its children copied.
* @param target the route to copy the children to.
*/
private copy(source: AnyRoute, target: AnyRoute) {
Object.keys(source).forEach((key) => {
const k = key as keyof AnyRoute
if (!k.startsWith('$') && source[k] instanceof Route) {
if (!(k in target)) {
source[k].$parent = target
target[k] = source[k]
} else if (source[k].$path.endsWith('/*')) {
this.copy(source[k], target[k])
}
}
})
}

/**
* Updates the navigation tree by merging a node with another.
*
Expand Down Expand Up @@ -86,13 +108,8 @@ export class CitronNavigator {
)
}
})
// copy all the child routes that existed in the old route, but don't exist in the new one (merge):
Object.keys(oldRoute).forEach((key) => {
if (!key.startsWith('$') && !(key in route) && oldRoute[key] instanceof Route) {
oldRoute[key].$parent = route
route[key as keyof typeof route] = oldRoute[key]
}
})

this.copy(oldRoute, route)
this.updateRoute()
}

Expand Down

0 comments on commit eaaaa8e

Please sign in to comment.