Skip to content

Commit 52884d9

Browse files
authored
feat(theme): allow setting base path in sidebar items (#2734)
1 parent b55b529 commit 52884d9

File tree

4 files changed

+103
-137
lines changed

4 files changed

+103
-137
lines changed

Diff for: __tests__/unit/client/theme-default/support/sidebar.test.ts

+24-10
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,19 @@ describe('client/theme-default/support/sidebar', () => {
2828
}
2929

3030
test('gets `/` sidebar', () => {
31-
expect(getSidebar(normalSidebar, '/')).toBe(root)
31+
expect(getSidebar(normalSidebar, '/')).toStrictEqual(root)
3232
})
3333

3434
test('gets `/multi-sidebar/` sidebar', () => {
35-
expect(getSidebar(normalSidebar, '/multi-sidebar/')).toBe(another)
35+
expect(getSidebar(normalSidebar, '/multi-sidebar/')).toStrictEqual(
36+
another
37+
)
3638
})
3739

3840
test('gets `/` sidebar again', () => {
39-
expect(getSidebar(normalSidebar, '/some-entry.html')).toBe(root)
41+
expect(getSidebar(normalSidebar, '/some-entry.html')).toStrictEqual(
42+
root
43+
)
4044
})
4145
})
4246

@@ -47,15 +51,19 @@ describe('client/theme-default/support/sidebar', () => {
4751
}
4852

4953
test('gets `/` sidebar', () => {
50-
expect(getSidebar(reversedSidebar, '/')).toBe(root)
54+
expect(getSidebar(reversedSidebar, '/')).toStrictEqual(root)
5155
})
5256

5357
test('gets `/multi-sidebar/` sidebar', () => {
54-
expect(getSidebar(reversedSidebar, '/multi-sidebar/')).toBe(another)
58+
expect(getSidebar(reversedSidebar, '/multi-sidebar/')).toStrictEqual(
59+
another
60+
)
5561
})
5662

5763
test('gets `/` sidebar again', () => {
58-
expect(getSidebar(reversedSidebar, '/some-entry.html')).toBe(root)
64+
expect(getSidebar(reversedSidebar, '/some-entry.html')).toStrictEqual(
65+
root
66+
)
5967
})
6068
})
6169

@@ -74,19 +82,25 @@ describe('client/theme-default/support/sidebar', () => {
7482
}
7583

7684
test('gets `/` sidebar', () => {
77-
expect(getSidebar(nestedSidebar, '/')).toBe(root)
85+
expect(getSidebar(nestedSidebar, '/')).toStrictEqual(root)
7886
})
7987

8088
test('gets `/multi-sidebar/` sidebar', () => {
81-
expect(getSidebar(nestedSidebar, '/multi-sidebar/')).toBe(another)
89+
expect(getSidebar(nestedSidebar, '/multi-sidebar/')).toStrictEqual(
90+
another
91+
)
8292
})
8393

8494
test('gets `/multi-sidebar/nested/` sidebar', () => {
85-
expect(getSidebar(nestedSidebar, '/multi-sidebar/nested/')).toBe(nested)
95+
expect(
96+
getSidebar(nestedSidebar, '/multi-sidebar/nested/')
97+
).toStrictEqual(nested)
8698
})
8799

88100
test('gets `/` sidebar again', () => {
89-
expect(getSidebar(nestedSidebar, '/some-entry.html')).toBe(root)
101+
expect(getSidebar(nestedSidebar, '/some-entry.html')).toStrictEqual(
102+
root
103+
)
90104
})
91105
})
92106
})

Diff for: docs/.vitepress/config.ts

+47-106
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { createRequire } from 'module'
2-
import { defineConfig } from 'vitepress'
2+
import { defineConfig, type DefaultTheme } from 'vitepress'
33

44
const require = createRequire(import.meta.url)
55
const pkg = require('vitepress/package.json')
@@ -36,8 +36,8 @@ export default defineConfig({
3636
nav: nav(),
3737

3838
sidebar: {
39-
'/guide/': sidebarGuide(),
40-
'/reference/': sidebarReference()
39+
'/guide/': { base: '/guide/', items: sidebarGuide() },
40+
'/reference/': { base: '/reference/', items: sidebarReference() }
4141
},
4242

4343
editLink: {
@@ -70,9 +70,13 @@ export default defineConfig({
7070
}
7171
})
7272

73-
function nav() {
73+
function nav(): DefaultTheme.NavItem[] {
7474
return [
75-
{ text: 'Guide', link: '/guide/what-is-vitepress', activeMatch: '/guide/' },
75+
{
76+
text: 'Guide',
77+
link: '/guide/what-is-vitepress',
78+
activeMatch: '/guide/'
79+
},
7680
{
7781
text: 'Reference',
7882
link: '/reference/site-config',
@@ -94,142 +98,79 @@ function nav() {
9498
]
9599
}
96100

97-
function sidebarGuide() {
101+
/* prettier-ignore */
102+
function sidebarGuide(): DefaultTheme.SidebarItem[] {
98103
return [
99104
{
100105
text: 'Introduction',
101106
collapsed: false,
102107
items: [
103-
{ text: 'What is VitePress?', link: '/guide/what-is-vitepress' },
104-
{ text: 'Getting Started', link: '/guide/getting-started' },
105-
{ text: 'Routing', link: '/guide/routing' },
106-
{ text: 'Deploy', link: '/guide/deploy' }
108+
{ text: 'What is VitePress?', link: 'what-is-vitepress' },
109+
{ text: 'Getting Started', link: 'getting-started' },
110+
{ text: 'Routing', link: 'routing' },
111+
{ text: 'Deploy', link: 'deploy' }
107112
]
108113
},
109114
{
110115
text: 'Writing',
111116
collapsed: false,
112117
items: [
113-
{ text: 'Markdown Extensions', link: '/guide/markdown' },
114-
{ text: 'Asset Handling', link: '/guide/asset-handling' },
115-
{ text: 'Frontmatter', link: '/guide/frontmatter' },
116-
{ text: 'Using Vue in Markdown', link: '/guide/using-vue' },
117-
{ text: 'Internationalization', link: '/guide/i18n' }
118+
{ text: 'Markdown Extensions', link: 'markdown' },
119+
{ text: 'Asset Handling', link: 'asset-handling' },
120+
{ text: 'Frontmatter', link: 'frontmatter' },
121+
{ text: 'Using Vue in Markdown', link: 'using-vue' },
122+
{ text: 'Internationalization', link: 'i18n' }
118123
]
119124
},
120125
{
121126
text: 'Customization',
122127
collapsed: false,
123128
items: [
124-
{ text: 'Using a Custom Theme', link: '/guide/custom-theme' },
125-
{
126-
text: 'Extending the Default Theme',
127-
link: '/guide/extending-default-theme'
128-
},
129-
{ text: 'Build-Time Data Loading', link: '/guide/data-loading' },
130-
{ text: 'SSR Compatibility', link: '/guide/ssr-compat' },
131-
{ text: 'Connecting to a CMS', link: '/guide/cms' }
129+
{ text: 'Using a Custom Theme', link: 'custom-theme' },
130+
{ text: 'Extending the Default Theme', link: 'extending-default-theme' },
131+
{ text: 'Build-Time Data Loading', link: 'data-loading' },
132+
{ text: 'SSR Compatibility', link: 'ssr-compat' },
133+
{ text: 'Connecting to a CMS', link: 'cms' }
132134
]
133135
},
134136
{
135137
text: 'Experimental',
136138
collapsed: false,
137139
items: [
138-
{
139-
text: 'MPA Mode',
140-
link: '/guide/mpa-mode'
141-
},
142-
{
143-
text: 'Sitemap Generation',
144-
link: '/guide/sitemap-generation'
145-
}
140+
{ text: 'MPA Mode', link: 'mpa-mode' },
141+
{ text: 'Sitemap Generation', link: 'sitemap-generation' }
146142
]
147143
},
148-
// {
149-
// text: 'Migrations',
150-
// collapsed: false,
151-
// items: [
152-
// {
153-
// text: 'Migration from VuePress',
154-
// link: '/guide/migration-from-vuepress'
155-
// },
156-
// {
157-
// text: 'Migration from VitePress 0.x',
158-
// link: '/guide/migration-from-vitepress-0'
159-
// }
160-
// ]
161-
// },
162-
{
163-
text: 'Config & API Reference',
164-
link: '/reference/site-config'
165-
}
144+
{ text: 'Config & API Reference', base: '/reference/', link: 'site-config' }
166145
]
167146
}
168147

169-
function sidebarReference() {
148+
/* prettier-ignore */
149+
function sidebarReference(): DefaultTheme.SidebarItem[] {
170150
return [
171151
{
172152
text: 'Reference',
173153
items: [
174-
{ text: 'Site Config', link: '/reference/site-config' },
175-
{ text: 'Frontmatter Config', link: '/reference/frontmatter-config' },
176-
{ text: 'Runtime API', link: '/reference/runtime-api' },
177-
{ text: 'CLI', link: '/reference/cli' },
154+
{ text: 'Site Config', link: 'site-config' },
155+
{ text: 'Frontmatter Config', link: 'frontmatter-config' },
156+
{ text: 'Runtime API', link: 'runtime-api' },
157+
{ text: 'CLI', link: 'cli' },
178158
{
179159
text: 'Default Theme',
180160
items: [
181-
{
182-
text: 'Overview',
183-
link: '/reference/default-theme-config'
184-
},
185-
{
186-
text: 'Nav',
187-
link: '/reference/default-theme-nav'
188-
},
189-
{
190-
text: 'Sidebar',
191-
link: '/reference/default-theme-sidebar'
192-
},
193-
{
194-
text: 'Home Page',
195-
link: '/reference/default-theme-home-page'
196-
},
197-
{
198-
text: 'Footer',
199-
link: '/reference/default-theme-footer'
200-
},
201-
{
202-
text: 'Layout',
203-
link: '/reference/default-theme-layout'
204-
},
205-
{
206-
text: 'Badge',
207-
link: '/reference/default-theme-badge'
208-
},
209-
{
210-
text: 'Team Page',
211-
link: '/reference/default-theme-team-page'
212-
},
213-
{
214-
text: 'Prev / Next Links',
215-
link: '/reference/default-theme-prev-next-links'
216-
},
217-
{
218-
text: 'Edit Link',
219-
link: '/reference/default-theme-edit-link'
220-
},
221-
{
222-
text: 'Last Updated Timestamp',
223-
link: '/reference/default-theme-last-updated'
224-
},
225-
{
226-
text: 'Search',
227-
link: '/reference/default-theme-search'
228-
},
229-
{
230-
text: 'Carbon Ads',
231-
link: '/reference/default-theme-carbon-ads'
232-
}
161+
{ text: 'Overview', link: 'default-theme-config' },
162+
{ text: 'Nav', link: 'default-theme-nav' },
163+
{ text: 'Sidebar', link: 'default-theme-sidebar' },
164+
{ text: 'Home Page', link: 'default-theme-home-page' },
165+
{ text: 'Footer', link: 'default-theme-footer' },
166+
{ text: 'Layout', link: 'default-theme-layout' },
167+
{ text: 'Badge', link: 'default-theme-badge' },
168+
{ text: 'Team Page', link: 'default-theme-team-page' },
169+
{ text: 'Prev / Next Links', link: 'default-theme-prev-next-links' },
170+
{ text: 'Edit Link', link: 'default-theme-edit-link' },
171+
{ text: 'Last Updated Timestamp', link: 'default-theme-last-updated' },
172+
{ text: 'Search', link: 'default-theme-search' },
173+
{ text: 'Carbon Ads', link: 'default-theme-carbon-ads' }
233174
]
234175
}
235176
]

Diff for: src/client/theme-default/support/sidebar.ts

+26-20
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,24 @@ export interface SidebarLink {
77
link: string
88
}
99

10+
type SidebarItem = DefaultTheme.SidebarItem
11+
1012
/**
1113
* Get the `Sidebar` from sidebar option. This method will ensure to get correct
1214
* sidebar config from `MultiSideBarConfig` with various path combinations such
1315
* as matching `guide/` and `/guide/`. If no matching config was found, it will
1416
* return empty array.
1517
*/
1618
export function getSidebar(
17-
sidebar: DefaultTheme.Sidebar | undefined,
19+
_sidebar: DefaultTheme.Sidebar | undefined,
1820
path: string
19-
): DefaultTheme.SidebarItem[] {
20-
if (Array.isArray(sidebar)) {
21-
return sidebar
22-
}
23-
24-
if (sidebar == null) {
25-
return []
26-
}
21+
): SidebarItem[] {
22+
if (Array.isArray(_sidebar)) return addBase(_sidebar)
23+
if (_sidebar == null) return []
2724

2825
path = ensureStartingSlash(path)
2926

30-
const dir = Object.keys(sidebar)
27+
const dir = Object.keys(_sidebar)
3128
.sort((a, b) => {
3229
return b.split('/').length - a.split('/').length
3330
})
@@ -36,16 +33,17 @@ export function getSidebar(
3633
return path.startsWith(ensureStartingSlash(dir))
3734
})
3835

39-
return dir ? sidebar[dir] : []
36+
const sidebar = dir ? _sidebar[dir] : []
37+
return Array.isArray(sidebar)
38+
? addBase(sidebar)
39+
: addBase(sidebar.items, sidebar.base)
4040
}
4141

4242
/**
4343
* Get or generate sidebar group from the given sidebar items.
4444
*/
45-
export function getSidebarGroups(
46-
sidebar: DefaultTheme.SidebarItem[]
47-
): DefaultTheme.SidebarItem[] {
48-
const groups: DefaultTheme.SidebarItem[] = []
45+
export function getSidebarGroups(sidebar: SidebarItem[]): SidebarItem[] {
46+
const groups: SidebarItem[] = []
4947

5048
let lastGroupIndex: number = 0
5149

@@ -67,12 +65,10 @@ export function getSidebarGroups(
6765
return groups
6866
}
6967

70-
export function getFlatSideBarLinks(
71-
sidebar: DefaultTheme.SidebarItem[]
72-
): SidebarLink[] {
68+
export function getFlatSideBarLinks(sidebar: SidebarItem[]): SidebarLink[] {
7369
const links: SidebarLink[] = []
7470

75-
function recursivelyExtractLinks(items: DefaultTheme.SidebarItem[]) {
71+
function recursivelyExtractLinks(items: SidebarItem[]) {
7672
for (const item of items) {
7773
if (item.text && item.link) {
7874
links.push({ text: item.text, link: item.link })
@@ -94,7 +90,7 @@ export function getFlatSideBarLinks(
9490
*/
9591
export function hasActiveLink(
9692
path: string,
97-
items: DefaultTheme.SidebarItem | DefaultTheme.SidebarItem[]
93+
items: SidebarItem | SidebarItem[]
9894
): boolean {
9995
if (Array.isArray(items)) {
10096
return items.some((item) => hasActiveLink(path, item))
@@ -106,3 +102,13 @@ export function hasActiveLink(
106102
? hasActiveLink(path, items.items)
107103
: false
108104
}
105+
106+
function addBase(items: SidebarItem[], _base?: string): SidebarItem[] {
107+
return [...items].map((_item) => {
108+
const item = { ..._item }
109+
const base = item.base || _base
110+
if (base && item.link) item.link = base + item.link
111+
if (item.items) item.items = addBase(item.items, base)
112+
return item
113+
})
114+
}

0 commit comments

Comments
 (0)