Skip to content

Commit

Permalink
feat: wip extendroutes
Browse files Browse the repository at this point in the history
  • Loading branch information
posva committed Feb 14, 2023
1 parent 17b79ec commit 627f417
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 12 deletions.
14 changes: 10 additions & 4 deletions src/core/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,16 @@ import { generateDTS as _generateDTS } from '../codegen/generateDTS'
import { generateVueRouterProxy as _generateVueRouterProxy } from '../codegen/vueRouterModule'
import { hasNamedExports } from '../data-fetching/parse'
import { definePageTransform, extractDefinePageNameAndPath } from './definePage'
import { EditableTreeNode } from './extendRoutes'

export function createRoutesContext(options: ResolvedOptions) {
const { dts: preferDTS, root, routesFolder } = options
const dts =
preferDTS === false
? false
: preferDTS === true
? resolve(root, 'typed-router.d.ts')
: resolve(root, preferDTS)
? resolve(root, 'typed-router.d.ts')
: resolve(root, preferDTS)

const routeTree = createPrefixTree(options)
const routeMap = new Map<string, TreeNode>()
Expand Down Expand Up @@ -53,8 +54,8 @@ export function createRoutesContext(options: ResolvedOptions) {
(options.extensions.length === 1
? options.extensions[0]
: `.{${options.extensions
.map((extension) => extension.replace('.', ''))
.join(',')}}`)
.map((extension) => extension.replace('.', ''))
.join(',')}}`)

await Promise.all(
routesFolder.map((folder) => {
Expand Down Expand Up @@ -124,6 +125,7 @@ export function createRoutesContext(options: ResolvedOptions) {
writeRouteInfoToNode(node, path)
}

// TODO: the map should be integrated with the root tree to have one source of truth only
function removePage({ filePath: path, routePath }: HandlerContext) {
log(`remove "${routePath}" for "${path}"`)
routeTree.remove(routePath)
Expand Down Expand Up @@ -198,6 +200,10 @@ ${routesExport}
let lastDTS: string | undefined
async function _writeConfigFiles() {
log('💾 writing...')

// TODO: extendRoutes
// const editable = new EditableTreeNode(routeTree)

logTree(routeTree, log)
if (dts) {
const content = generateDTS()
Expand Down
122 changes: 122 additions & 0 deletions src/core/extendRoutes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { createPrefixTree, TreeNode } from "./tree";

export class ExtendableRoutes {
tree: TreeNode

constructor(tree: TreeNode) {
this.tree = tree
}

/**
* Traverse the tree in BFS order.
*
* @returns
*/
*[Symbol.iterator]() {
let currentNode: TreeNode | null = this.tree
let currentChildren = this.tree.children

while (currentNode) {
yield* currentChildren


}
}
}

export class EditableTreeNode {
node: TreeNode
parentName?: string

constructor(node: TreeNode, parentName?: string) {
this.node = node
this.parentName = parentName
}

/**
* Remove the current route and all its children.
*/
delete() {
if (this.node.parent) {
this.node.delete()
// throw new Error('Cannot delete the root node.')
}
// this.node.parent.remove(this.node.path.slice(1))
}

/**
* Append a new route as a children of this route.
*/
append() {

}

get name() {
return this.node.name
}

get meta() {
return this.node.metaAsObject
}

get path() {
return this.node.path
}

get fullPath() {
return this.node.fullPath
}

/**
* DFS traversal of the tree.
* @example
* ```ts
* for (const node of tree) {
* // ...
* }
* ```
*/
*traverseDFS(): Generator<EditableTreeNode, void, unknown> {
// the root node is special
if (!this.node.isRoot()) {
yield this
}
for (const [name, child] of this.node.children) {
// console.debug(`CHILD: ${_name} - ${child.fullPath}`)
yield* new EditableTreeNode(child, name).traverseDFS()
}
}

*[Symbol.iterator](): Generator<EditableTreeNode, void, unknown> {
yield* this.traverseBFS()
}

/**
* BFS traversal of the tree as a generator.
*
* @example
* ```ts
* for (const node of tree) {
* // ...
* }
* ```
*/
*traverseBFS(): Generator<EditableTreeNode, void, unknown> {
for (const [name, child] of this.node.children) {
yield new EditableTreeNode(child, name)
}
// we need to traverse again in case the user removed a route
for (const [name, child] of this.node.children) {
yield* new EditableTreeNode(child, name).traverseBFS()
}
}
}

function testy() {
const tree = createPrefixTree({} as any)
const route = new EditableTreeNode(tree)

for (const r of route) {
console.log(r.name)
}
}
12 changes: 12 additions & 0 deletions src/core/tree.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,18 @@ describe('Tree', () => {
expect(tree.children.get('index')).toBeUndefined()
})

it('can remove itself from the tree', () => {
const tree = createPrefixTree(DEFAULT_OPTIONS)
tree.insert('index.vue').insert('nested.vue')
tree.insert('a.vue').insert('nested.vue')
tree.insert('b.vue')
expect(tree.children.size).toBe(3)
tree.children.get('a')!.delete()
expect(tree.children.size).toBe(2)
tree.children.get('index')!.delete()
expect(tree.children.size).toBe(1)
})

it('handles multiple params', () => {
const tree = createPrefixTree(DEFAULT_OPTIONS)
tree.insert('[a]-[b].vue')
Expand Down
50 changes: 42 additions & 8 deletions src/core/tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import { trimExtension } from './utils'
import { CustomRouteBlock } from './customBlock'

export class TreeNode {
/**
* File path that created the node if any.
*/
filePath?: string

/**
* value of the node
*/
Expand Down Expand Up @@ -53,6 +58,7 @@ export class TreeNode {
this.children.set(segment, new TreeNode(this.options, segment, this))
}
const child = this.children.get(segment)!
child.filePath = segment

if (isComponent) {
child.value.filePaths.set(viewName, filePath)
Expand All @@ -75,7 +81,19 @@ export class TreeNode {
}

/**
* Remove a route from the tree.
* Delete itself and all its children.
*/
delete() {
// TODO: rename remove to removeChild
if (!this.parent || !this.filePath) {
throw new Error('Cannot delete the root node.')
}
this.parent.children.delete(this.filePath)
}

/**
* Remove a route from the tree. The path shouldn't start with a `/` but it can be a nested one. e.g. `foo/bar.vue`.
* The `path` should be relative to the page folder.
*
* @param path - path segment of the file
*/
Expand Down Expand Up @@ -127,16 +145,32 @@ export class TreeNode {
return this.value.overrides.path ?? this.value.path
}

/**
* Returns the route name of the node. If the name was overridden, it returns the override.
*/
get name() {
return this.value.overrides.name || this.options.getRouteName(this)
}

get meta() {
const overrideMeta = { ...this.value.overrides.meta }

/**
* Returns the meta property as an object.
*/
get metaAsObject() {
const meta = {
...this.value.overrides.meta,
}
if (this.value.includeLoaderGuard) {
overrideMeta._loaderGuard = true
meta._loaderGuard = true
}
return meta
}

/**
* Returns the JSON string of the meta object of the node. If the meta was overridden, it returns the override. If
* there is no override, it returns an empty string.
*/
get meta() {
const overrideMeta = this.metaAsObject

return Object.keys(overrideMeta).length > 0
? JSON.stringify(overrideMeta, null, 2)
Expand Down Expand Up @@ -170,11 +204,11 @@ export class TreeNode {
return `${this.value}${
// either we have multiple names
this.value.filePaths.size > 1 ||
// or we have one name and it's not default
(this.value.filePaths.size === 1 && !this.value.filePaths.get('default'))
// or we have one name and it's not default
(this.value.filePaths.size === 1 && !this.value.filePaths.get('default'))
? ` ⎈(${Array.from(this.value.filePaths.keys()).join(', ')})`
: ''
}${this.hasDefinePage ? ' ⚑ definePage()' : ''}`
}${this.hasDefinePage ? ' ⚑ definePage()' : ''}`
}
}

Expand Down

0 comments on commit 627f417

Please sign in to comment.