fix: fix "next and prev link" not working when `link` has extention

When there's extention in the sidebar item links, such as `.md` or
`.html`, the "next and prev link" is failing. This commit fixes
that issue.
pull/141/head
Kia King Ishii 5 years ago
parent af8a162cd3
commit 6dcf6b3796

@ -0,0 +1,123 @@
import {
getSideBarConfig,
getFlatSideBarLinks
} from 'client/theme-default/support/sideBar'
describe('client/theme-default/support/sideBar', () => {
it('gets the correct sidebar items', () => {
expect(getSideBarConfig(false, '')).toEqual(false)
expect(getSideBarConfig('auto', '')).toEqual('auto')
const sidebar = [{ text: 'Title 01', link: 'title-01' }]
const expected = [{ text: 'Title 01', link: 'title-01' }]
expect(getSideBarConfig(sidebar, '')).toEqual(expected)
})
it('gets the correct sidebar items from the given path', () => {
const sidebar = {
'/': [{ text: 'R', link: 'r' }],
'/guide/': [{ text: 'G', link: 'g' }]
}
expect(getSideBarConfig(sidebar, '/')).toEqual(sidebar['/'])
expect(getSideBarConfig(sidebar, '/guide/')).toEqual(sidebar['/guide/'])
})
it('gets the correct sidebar items with various combination', () => {
const s = {
'/guide/': [{ text: 'G', link: 'g' }],
api: [{ text: 'A', link: 'a' }]
}
expect(getSideBarConfig(s, '/guide/')).toEqual(s['/guide/'])
expect(getSideBarConfig(s, '/guide')).toEqual(s['/guide/'])
expect(getSideBarConfig(s, 'guide/')).toEqual(s['/guide/'])
expect(getSideBarConfig(s, 'guide/nested')).toEqual(s['/guide/'])
expect(getSideBarConfig(s, '/guide/nested')).toEqual(s['/guide/'])
expect(getSideBarConfig(s, 'guide/nested/')).toEqual(s['/guide/'])
expect(getSideBarConfig(s, '/api/')).toEqual(s['api'])
expect(getSideBarConfig(s, '/api')).toEqual(s['api'])
expect(getSideBarConfig(s, 'api/')).toEqual(s['api'])
expect(getSideBarConfig(s, 'api/nested')).toEqual(s['api'])
expect(getSideBarConfig(s, '/api/nested')).toEqual(s['api'])
expect(getSideBarConfig(s, 'api/nested/')).toEqual(s['api'])
expect(getSideBarConfig(s, '/')).toEqual('auto')
})
it('creates flat sidebar links', () => {
const sidebar = [
{ text: 'Title 01', link: '/title-01' },
{ text: 'Title 02', link: '/title-02' },
{ text: 'Title 03', link: '/title-03' }
]
const expected = [
{ text: 'Title 01', link: '/title-01' },
{ text: 'Title 02', link: '/title-02' },
{ text: 'Title 03', link: '/title-03' }
]
expect(getFlatSideBarLinks(sidebar)).toEqual(expected)
})
it('creates flat sidebar links with mixed sidebar group', () => {
const sidebar = [
{
text: 'Title 01',
link: '/title-01',
children: [
{ text: 'Children 01', link: '/children-01' },
{ text: 'Children 02', link: '/children-02' }
]
},
{ text: 'Title 02', link: '/title-02' },
{ text: 'Title 03', link: '/title-03' }
]
const expected = [
{ text: 'Title 01', link: '/title-01' },
{ text: 'Children 01', link: '/children-01' },
{ text: 'Children 02', link: '/children-02' },
{ text: 'Title 02', link: '/title-02' },
{ text: 'Title 03', link: '/title-03' }
]
expect(getFlatSideBarLinks(sidebar)).toEqual(expected)
})
it('ignores any items with no `link` property', () => {
const sidebar = [
{
text: 'Title 01',
children: [
{ text: 'Children 01', link: '/children-01' },
{ text: 'Children 02', link: '/children-02' }
]
},
{ text: 'Title 02', link: '/title-02' }
]
const expected = [
{ text: 'Children 01', link: '/children-01' },
{ text: 'Children 02', link: '/children-02' },
{ text: 'Title 02', link: '/title-02' }
]
expect(getFlatSideBarLinks(sidebar)).toEqual(expected)
})
it('removes `.md` or `.html` extention', () => {
const sidebar = [
{ text: 'Title 01', link: '/title-01.md' },
{ text: 'Title 02', link: '/title-02.html' }
]
const expected = [
{ text: 'Title 01', link: '/title-01' },
{ text: 'Title 02', link: '/title-02' }
]
expect(getFlatSideBarLinks(sidebar)).toEqual(expected)
})
})

@ -19,4 +19,23 @@ describe('client/theme-default/utils', () => {
expect(Utils.ensureEndingSlash('path/page.html')).toBe('path/page.html')
})
})
describe('removeExtention', () => {
it('removes `.md` or `.html` extention from the path', () => {
expect(Utils.removeExtention('/')).toBe('/')
expect(Utils.removeExtention('index')).toBe('/')
expect(Utils.removeExtention('index.md')).toBe('/')
expect(Utils.removeExtention('index.html')).toBe('/')
expect(Utils.removeExtention('/index')).toBe('/')
expect(Utils.removeExtention('/index.md')).toBe('/')
expect(Utils.removeExtention('/index.html')).toBe('/')
expect(Utils.removeExtention('path')).toBe('path')
expect(Utils.removeExtention('path.md')).toBe('path')
expect(Utils.removeExtention('path.html')).toBe('path')
expect(Utils.removeExtention('path/')).toBe('path/')
expect(Utils.removeExtention('path/nested.md')).toBe('path/nested')
expect(Utils.removeExtention('path/nested.html')).toBe('path/nested')
expect(Utils.removeExtention('path/nested/index')).toBe('path/nested/')
})
})
})

@ -1,44 +1,41 @@
import { computed } from 'vue'
import { useSiteDataByRoute, usePageData } from 'vitepress'
import { isArray, getPathDirName, ensureStartingSlash } from '../utils'
import { DefaultTheme } from '../config'
import { isArray, ensureStartingSlash, removeExtention } from '../utils'
import { getSideBarConfig, getFlatSideBarLinks } from '../support/sideBar'
export function useNextAndPrevLinks() {
const site = useSiteDataByRoute()
const page = usePageData()
const candidates = computed(() => {
const path = ensureStartingSlash(page.value.relativePath)
const sidebar = site.value.themeConfig.sidebar
return getFlatSidebarLinks(path, sidebar)
const path = computed(() => {
return removeExtention(ensureStartingSlash(page.value.relativePath))
})
const currentPath = computed(() => {
const path = ensureStartingSlash(page.value.relativePath)
const candidates = computed(() => {
const config = getSideBarConfig(site.value.themeConfig.sidebar, path.value)
return path.replace(/(index)?\.(md|html)$/, '')
return isArray(config) ? getFlatSideBarLinks(config) : []
})
const currentIndex = computed(() => {
const index = computed(() => {
return candidates.value.findIndex((item) => {
return item.link === currentPath.value
return item.link === path.value
})
})
const next = computed(() => {
if (
site.value.themeConfig.nextLinks !== false &&
currentIndex.value > -1 &&
currentIndex.value < candidates.value.length - 1
index.value > -1 &&
index.value < candidates.value.length - 1
) {
return candidates.value[currentIndex.value + 1]
return candidates.value[index.value + 1]
}
})
const prev = computed(() => {
if (site.value.themeConfig.prevLinks !== false && currentIndex.value > 0) {
return candidates.value[currentIndex.value - 1]
if (site.value.themeConfig.prevLinks !== false && index.value > 0) {
return candidates.value[index.value - 1]
}
})
@ -50,53 +47,3 @@ export function useNextAndPrevLinks() {
hasLinks
}
}
function getFlatSidebarLinks(
path: string,
sidebar?: DefaultTheme.SideBarConfig
): DefaultTheme.SideBarLink[] {
if (!sidebar || sidebar === 'auto') {
return []
}
return isArray(sidebar)
? getFlatSidebarLinksFromArray(path, sidebar)
: getFlatSidebarLinksFromObject(path, sidebar)
}
function getFlatSidebarLinksFromArray(
path: string,
sidebar: DefaultTheme.SideBarItem[]
): DefaultTheme.SideBarLink[] {
return sidebar.reduce<DefaultTheme.SideBarLink[]>((links, item) => {
if (item.link) {
links.push({ text: item.text, link: item.link })
}
if (isSideBarGroup(item)) {
links = [...links, ...getFlatSidebarLinks(path, item.children)]
}
return links
}, [])
}
function getFlatSidebarLinksFromObject(
path: string,
sidebar: DefaultTheme.MultiSideBarConfig
): DefaultTheme.SideBarLink[] {
const paths = [path, Object.keys(sidebar)[0]]
const item = paths.map((p) => sidebar[getPathDirName(p)]).find(Boolean)
if (isArray(item)) {
return getFlatSidebarLinksFromArray(path, item)
}
return []
}
function isSideBarGroup(
item: DefaultTheme.SideBarItem
): item is DefaultTheme.SideBarGroup {
return (item as DefaultTheme.SideBarGroup).children !== undefined
}

@ -0,0 +1,70 @@
import { DefaultTheme } from '../config'
import {
isArray,
ensureSlash,
ensureStartingSlash,
removeExtention
} from '../utils'
export function isSideBarConfig(
sidebar: DefaultTheme.SideBarConfig | DefaultTheme.MultiSideBarConfig
): sidebar is DefaultTheme.SideBarConfig {
return sidebar === false || sidebar === 'auto' || isArray(sidebar)
}
export function isSideBarGroup(
item: DefaultTheme.SideBarItem
): item is DefaultTheme.SideBarGroup {
return (item as DefaultTheme.SideBarGroup).children !== undefined
}
/**
* Get the `SideBarConfig` from sidebar option. This method will ensure to get
* correct sidebar config from `MultiSideBarConfig` with various path
* combinations such as matching `guide/` and `/guide/`. If no matching config
* was found, it will return `auto` as a fallback.
*/
export function getSideBarConfig(
sidebar: DefaultTheme.SideBarConfig | DefaultTheme.MultiSideBarConfig,
path: string
): DefaultTheme.SideBarConfig {
if (isSideBarConfig(sidebar)) {
return sidebar
}
// get the very first segment of the path to compare with nulti sidebar keys
// and make sure it's surrounded by slash
path = ensureStartingSlash(path).split('/')[1] || '/'
path = ensureSlash(path)
for (const dir in sidebar) {
// make sure the multi sidebar key is surrounded by slash too
if (path === ensureSlash(dir)) {
return sidebar[dir]
}
}
return 'auto'
}
/**
* Get flat sidebar links from the sidebar items. This method is useful for
* creating the "next and prev link" feature. It will ignore any items that
* don't have `link` property and removes `.md` or `.html` extension if a
* link contains it.
*/
export function getFlatSideBarLinks(
sidebar: DefaultTheme.SideBarItem[]
): DefaultTheme.SideBarLink[] {
return sidebar.reduce<DefaultTheme.SideBarLink[]>((links, item) => {
if (item.link) {
links.push({ text: item.text, link: removeExtention(item.link) })
}
if (isSideBarGroup(item)) {
links = [...links, ...getFlatSideBarLinks(item.children)]
}
return links
}, [])
}

@ -66,6 +66,10 @@ export function getPathDirName(path: string): string {
return ensureEndingSlash(segments.join('/'))
}
export function ensureSlash(path: string): string {
return ensureEndingSlash(ensureStartingSlash(path))
}
export function ensureStartingSlash(path: string): string {
return /^\//.test(path) ? path : `/${path}`
}
@ -73,3 +77,11 @@ export function ensureStartingSlash(path: string): string {
export function ensureEndingSlash(path: string): string {
return /(\.html|\/)$/.test(path) ? path : `${path}/`
}
/**
* Remove `.md` or `.html` extention from the given path. It also converts
* `index` to slush.
*/
export function removeExtention(path: string): string {
return path.replace(/(index)?(\.(md|html))?$/, '') || '/'
}

Loading…
Cancel
Save