Skip to content

Commit 67d6c6b

Browse files
authored
fix(navigation): prevent duplicate nodes (#2959)
1 parent be19fb9 commit 67d6c6b

File tree

2 files changed

+161
-3
lines changed

2 files changed

+161
-3
lines changed

src/runtime/internal/navigation.ts

+31-3
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,21 @@ export async function generateNavigationTree<T extends PageCollectionItemBase>(q
8888

8989
// First-level item, push it straight to nav
9090
if (parts.length === 1) {
91-
nav.push(navItem)
91+
// Check for duplicate link
92+
const existed = nav.find(item => item.path === navItem.path && item.page === false)
93+
if (isIndex && existed) {
94+
Object.assign(existed, {
95+
page: undefined,
96+
children: [
97+
...navItem.children,
98+
...existed.children,
99+
],
100+
})
101+
}
102+
else {
103+
nav.push(navItem)
104+
}
105+
92106
return nav
93107
}
94108

@@ -115,7 +129,7 @@ export async function generateNavigationTree<T extends PageCollectionItemBase>(q
115129
...navigationConfig,
116130
title: navigationConfig.title || generateTitle(part),
117131
path: currentPathPart,
118-
stem: idParts.join('/'),
132+
stem: idParts.slice(0, i + 1).join('/'),
119133
children: [],
120134
page: false,
121135
}
@@ -125,7 +139,21 @@ export async function generateNavigationTree<T extends PageCollectionItemBase>(q
125139
return parent!.children!
126140
}, nav)
127141

128-
siblings.push(navItem)
142+
// Check for duplicate link
143+
const existed = siblings.find(item => item.path === navItem.path && item.page === false)
144+
if (existed) {
145+
Object.assign(existed, {
146+
...navItem,
147+
page: undefined,
148+
children: [
149+
...navItem.children,
150+
...existed.children,
151+
],
152+
})
153+
}
154+
else {
155+
siblings.push(navItem)
156+
}
129157

130158
return nav
131159
}, [] as ContentNavigationItem[])

test/unit/generateNavigationTree.test.ts

+130
Original file line numberDiff line numberDiff line change
@@ -243,4 +243,134 @@ describe('generateNavigationTree', () => {
243243
expect(tree[0]).toHaveProperty('description', 'Home page')
244244
expect(tree[0]).toHaveProperty('customField', 'custom value')
245245
})
246+
247+
it('index file in directory', async () => {
248+
const items = [
249+
{
250+
title: 'bourg-en-bresse',
251+
path: '/devenir-benevole/bourg-en-bresse',
252+
stem: 'devenir-benevole/bourg-en-bresse',
253+
},
254+
{
255+
title: 'index',
256+
path: '/devenir-benevole',
257+
stem: 'devenir-benevole/index',
258+
},
259+
] as Array<PageCollectionItemBase & { customField: string }>
260+
261+
const tree = await generateNavigationTree(mockQueryBuilder(items))
262+
expect(tree).toMatchObject([
263+
{
264+
title: 'Devenir Benevole',
265+
path: '/devenir-benevole',
266+
stem: 'devenir-benevole',
267+
children: [
268+
{
269+
title: 'bourg-en-bresse',
270+
path: '/devenir-benevole/bourg-en-bresse',
271+
stem: 'devenir-benevole/bourg-en-bresse',
272+
},
273+
{
274+
title: 'index',
275+
path: '/devenir-benevole',
276+
stem: 'devenir-benevole/index',
277+
},
278+
],
279+
},
280+
])
281+
})
282+
283+
it('index file in directory 2', async () => {
284+
const items = [
285+
{
286+
title: 'bourg-en-bresse',
287+
path: '/devenir-benevole/france/ain/bourg-en-bresse',
288+
stem: 'devenir-benevole/france/ain/bourg-en-bresse',
289+
},
290+
{
291+
title: 'index',
292+
path: '/devenir-benevole/france/ain',
293+
stem: 'devenir-benevole/france/ain/index',
294+
},
295+
] as Array<PageCollectionItemBase & { customField: string }>
296+
297+
const tree = await generateNavigationTree(mockQueryBuilder(items))
298+
299+
expect(tree).toMatchObject([
300+
{
301+
title: 'Devenir Benevole',
302+
path: '/devenir-benevole',
303+
stem: 'devenir-benevole',
304+
children: [
305+
{
306+
title: 'France',
307+
path: '/devenir-benevole/france',
308+
stem: 'devenir-benevole/france',
309+
children: [
310+
{
311+
title: 'index',
312+
path: '/devenir-benevole/france/ain',
313+
stem: 'devenir-benevole/france/ain/index',
314+
children: [
315+
{
316+
title: 'bourg-en-bresse',
317+
path: '/devenir-benevole/france/ain/bourg-en-bresse',
318+
stem: 'devenir-benevole/france/ain/bourg-en-bresse',
319+
},
320+
{
321+
title: 'index',
322+
path: '/devenir-benevole/france/ain',
323+
stem: 'devenir-benevole/france/ain/index',
324+
},
325+
],
326+
},
327+
],
328+
page: false,
329+
},
330+
],
331+
page: false,
332+
},
333+
])
334+
})
335+
336+
it('index file in directory 3', async () => {
337+
const items = [
338+
{
339+
title: 'Title from `.navigation.yml` file',
340+
path: '/devenir-benevole/navigation',
341+
stem: 'devenir-benevole/.navigation',
342+
},
343+
{
344+
title: 'bourg-en-bresse',
345+
path: '/devenir-benevole/bourg-en-bresse',
346+
stem: 'devenir-benevole/bourg-en-bresse',
347+
},
348+
{
349+
title: 'index',
350+
path: '/devenir-benevole',
351+
stem: 'devenir-benevole/index',
352+
},
353+
] as Array<PageCollectionItemBase & { customField: string }>
354+
355+
const tree = await generateNavigationTree(mockQueryBuilder(items))
356+
expect(tree).toMatchObject([
357+
{
358+
title: 'Title from `.navigation.yml` file',
359+
path: '/devenir-benevole',
360+
stem: 'devenir-benevole',
361+
children: [
362+
{
363+
title: 'bourg-en-bresse',
364+
path: '/devenir-benevole/bourg-en-bresse',
365+
stem: 'devenir-benevole/bourg-en-bresse',
366+
},
367+
{
368+
title: 'index',
369+
path: '/devenir-benevole',
370+
stem: 'devenir-benevole/index',
371+
},
372+
],
373+
},
374+
])
375+
})
246376
})

0 commit comments

Comments
 (0)