Skip to content

Commit f651961

Browse files
committed
fix: keep parent override in children
Fix #189
1 parent 3ba54ca commit f651961

File tree

4 files changed

+116
-30
lines changed

4 files changed

+116
-30
lines changed

src/codegen/generateRouteMap.spec.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,20 @@ describe('generateRouteNamedMap', () => {
123123
}"
124124
`)
125125
})
126+
127+
it('keeps parent path overrides', () => {
128+
const tree = createPrefixTree(DEFAULT_OPTIONS)
129+
const parent = tree.insert('parent.vue')
130+
const child = tree.insert('parent/child.vue')
131+
parent.value.setOverride('parent.vue', { path: '/' })
132+
expect(child.fullPath).toBe('/child')
133+
expect(formatExports(generateRouteNamedMap(tree))).toMatchInlineSnapshot(`
134+
"export interface RouteNamedMap {
135+
'/parent': RouteRecordInfo<'/parent', '/', Record<never, never>, Record<never, never>>,
136+
'/parent/child': RouteRecordInfo<'/parent/child', '/child', Record<never, never>, Record<never, never>>,
137+
}"
138+
`)
139+
})
126140
})
127141

128142
/**

src/core/context.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,19 +105,22 @@ export function createRoutesContext(options: ResolvedOptions) {
105105
await _writeConfigFiles()
106106
}
107107

108-
async function writeRouteInfoToNode(node: TreeNode, path: string) {
109-
const content = await fs.readFile(path, 'utf8')
108+
async function writeRouteInfoToNode(node: TreeNode, filePath: string) {
109+
const content = await fs.readFile(filePath, 'utf8')
110110
// TODO: cache the result of parsing the SFC so the transform can reuse the parsing
111111
node.hasDefinePage = content.includes('definePage')
112112
const [definedPageNameAndPath, routeBlock] = await Promise.all([
113-
extractDefinePageNameAndPath(content, path),
114-
getRouteBlock(path, options),
113+
extractDefinePageNameAndPath(content, filePath),
114+
getRouteBlock(filePath, options),
115115
])
116116
// TODO: should warn if hasDefinePage and customRouteBlock
117117
// if (routeBlock) log(routeBlock)
118-
node.setCustomRouteBlock(path, { ...routeBlock, ...definedPageNameAndPath })
118+
node.setCustomRouteBlock(filePath, {
119+
...routeBlock,
120+
...definedPageNameAndPath,
121+
})
119122
node.value.includeLoaderGuard =
120-
options.dataFetching && (await hasNamedExports(path))
123+
options.dataFetching && (await hasNamedExports(filePath))
121124
}
122125

123126
async function addPage(

src/core/tree.ts

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,22 @@ export class TreeNode {
3939
*/
4040
hasDefinePage: boolean = false
4141

42-
constructor(options: TreeNodeOptions, filePath: string, parent?: TreeNode) {
42+
/**
43+
* Creates a new tree node.
44+
*
45+
* @param options - TreeNodeOptions shared by all nodes
46+
* @param pathSegment - path segment of this node e.g. `users` or `:id`
47+
* @param parent
48+
*/
49+
constructor(
50+
options: TreeNodeOptions,
51+
pathSegment: string,
52+
parent?: TreeNode
53+
) {
4354
this.options = options
4455
this.parent = parent
4556
this.value = createTreeNodeValue(
46-
filePath,
57+
pathSegment,
4758
parent?.value,
4859
options.treeNodeOptions || options.pathParser
4960
)
@@ -78,7 +89,8 @@ export class TreeNode {
7889
}
7990

8091
/**
81-
* Adds a path to the tree. `path` cannot start with a `/`.
92+
* Adds a path that has already been parsed to the tree. `path` cannot start with a `/`. This method is similar to
93+
* `insert` but the path argument should be already parsed. e.g. `users/:id` for a file named `users/[id].vue`.
8294
*
8395
* @param path - path segment to insert, already parsed (e.g. users/:id)
8496
* @param filePath - file path, defaults to path for convenience and testing
@@ -109,8 +121,18 @@ export class TreeNode {
109121
return node
110122
}
111123

112-
setCustomRouteBlock(path: string, routeBlock: CustomRouteBlock | undefined) {
113-
this.value.setOverride(path, routeBlock)
124+
/**
125+
* Saves a custom route block for a specific file path. The file path is used as a key. Some special file paths will
126+
* have a lower or higher priority.
127+
*
128+
* @param filePath - file path where the custom block is located
129+
* @param routeBlock - custom block to set
130+
*/
131+
setCustomRouteBlock(
132+
filePath: string,
133+
routeBlock: CustomRouteBlock | undefined
134+
) {
135+
this.value.setOverride(filePath, routeBlock)
114136
}
115137

116138
getSortedChildren() {
@@ -123,7 +145,6 @@ export class TreeNode {
123145
* Delete and detach itself from the tree.
124146
*/
125147
delete() {
126-
// TODO: rename remove to removeChild
127148
if (!this.parent) {
128149
throw new Error('Cannot delete the root node.')
129150
}
@@ -139,6 +160,7 @@ export class TreeNode {
139160
* @param path - path segment of the file
140161
*/
141162
remove(path: string) {
163+
// TODO: rename remove to removeChild
142164
const { tail, segment, viewName, isComponent } = splitFilePath(
143165
path,
144166
this.options
@@ -272,13 +294,19 @@ export class PrefixTree extends TreeNode {
272294
return node
273295
}
274296

297+
/**
298+
* Returns the tree node of the given file path.
299+
*
300+
* @param filePath - file path of the tree node to get
301+
*/
275302
getChild(filePath: string) {
276303
return this.map.get(filePath)
277304
}
278305

279306
/**
307+
* Removes the tree node of the given file path.
280308
*
281-
* @param filePath -
309+
* @param filePath - file path of the tree node to remove
282310
*/
283311
removeChild(filePath: string) {
284312
if (this.map.has(filePath)) {
@@ -288,6 +316,9 @@ export class PrefixTree extends TreeNode {
288316
}
289317
}
290318

319+
/**
320+
* @deprecated Use `new PrefixTree()` instead.
321+
*/
291322
export function createPrefixTree(options: ResolvedOptions) {
292323
return new PrefixTree(options)
293324
}

src/core/treeNodeValue.ts

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ class _TreeNodeValueBase {
2222
* flag based on the type of the segment
2323
*/
2424
_type: TreeNodeType
25+
26+
parent: TreeNodeValue | undefined
27+
2528
/**
2629
* segment as defined by the file structure e.g. keeps the `index` name
2730
*/
@@ -37,15 +40,11 @@ class _TreeNodeValueBase {
3740
*/
3841
subSegments: SubSegment[]
3942

40-
/**
41-
* fullPath of the node based on parent nodes
42-
*/
43-
path: string
44-
4543
/**
4644
* Overrides defined by each file. The map is necessary to handle named views.
4745
*/
4846
private _overrides = new Map<string, RouteRecordOverride>()
47+
// TODO: cache the overrides generation
4948

5049
/**
5150
* Should we add the loader guard to the route record.
@@ -69,12 +68,19 @@ class _TreeNodeValueBase {
6968
this.rawSegment = rawSegment
7069
this.pathSegment = pathSegment
7170
this.subSegments = subSegments
72-
const parentPath = parent?.path
73-
this.path =
74-
// both the root record and the index record have a path of /
75-
(!parentPath || parentPath === '/') && this.pathSegment === ''
76-
? '/'
77-
: joinPath(parent?.path || '', this.pathSegment)
71+
this.parent = parent
72+
}
73+
74+
/**
75+
* fullPath of the node based on parent nodes
76+
*/
77+
get path(): string {
78+
const parentPath = this.parent?.path
79+
// both the root record and the index record have a path of /
80+
const pathSegment = this.overrides.path ?? this.pathSegment
81+
return (!parentPath || parentPath === '/') && pathSegment === ''
82+
? '/'
83+
: joinPath(parentPath || '', pathSegment)
7884
}
7985

8086
toString(): string {
@@ -105,14 +111,14 @@ class _TreeNodeValueBase {
105111
}, {} as RouteRecordOverride)
106112
}
107113

108-
setOverride(path: string, routeBlock: CustomRouteBlock | undefined) {
109-
this._overrides.set(path, routeBlock || {})
114+
setOverride(filePath: string, routeBlock: CustomRouteBlock | undefined) {
115+
this._overrides.set(filePath, routeBlock || {})
110116
}
111117

112118
/**
113119
* Remove all overrides for a given key.
114120
*
115-
* @param key - key to remove from the override
121+
* @param key - key to remove from the override, e.g. path, name, etc
116122
*/
117123
removeOverride(key: keyof CustomRouteBlock) {
118124
this._overrides.forEach((routeBlock) => {
@@ -121,15 +127,35 @@ class _TreeNodeValueBase {
121127
})
122128
}
123129

124-
mergeOverride(path: string, routeBlock: CustomRouteBlock) {
125-
const existing = this._overrides.get(path) || {}
126-
this._overrides.set(path, mergeRouteRecordOverride(existing, routeBlock))
130+
/**
131+
* Add an override to the current node by merging with the existing values.
132+
*
133+
* @param filePath - The file path to add to the override
134+
* @param routeBlock - The route block to add to the override
135+
*/
136+
mergeOverride(filePath: string, routeBlock: CustomRouteBlock) {
137+
const existing = this._overrides.get(filePath) || {}
138+
this._overrides.set(
139+
filePath,
140+
mergeRouteRecordOverride(existing, routeBlock)
141+
)
127142
}
128143

144+
/**
145+
* Add an override to the current node using the special file path `@@edits` that makes this added at build time.
146+
*
147+
* @param routeBlock - The route block to add to the override
148+
*/
129149
addEditOverride(routeBlock: CustomRouteBlock) {
130150
return this.mergeOverride(EDITS_OVERRIDE_NAME, routeBlock)
131151
}
132152

153+
/**
154+
* Set a specific value in the _edits_ override.
155+
*
156+
* @param key - key to set in the override, e.g. path, name, etc
157+
* @param value - value to set in the override
158+
*/
133159
setEditOverride<K extends keyof RouteRecordOverride>(
134160
key: K,
135161
value: RouteRecordOverride[K]
@@ -193,6 +219,13 @@ export interface TreeNodeValueOptions extends ParseSegmentOptions {
193219
format?: 'file' | 'path'
194220
}
195221

222+
/**
223+
* Creates a new TreeNodeValue based on the segment. The result can be a static segment or a param segment.
224+
*
225+
* @param segment - path segment
226+
* @param parent - parent node
227+
* @param options - options
228+
*/
196229
export function createTreeNodeValue(
197230
segment: string,
198231
parent?: TreeNodeValue,
@@ -505,6 +538,11 @@ function parseRawPathSegment(
505538
]
506539
}
507540

541+
/**
542+
* Helper function to create an empty route param used by the parser.
543+
*
544+
* @returns an empty route param
545+
*/
508546
function createEmptyRouteParam(): TreeRouteParam {
509547
return {
510548
paramName: '',

0 commit comments

Comments
 (0)