From 061ad3ff32f0ba7910c989132d0d7a953dad7a75 Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 6 Nov 2020 18:10:45 -0500 Subject: [PATCH] refactor: split SideBarItem into separate file Also fix page loading --- src/client/app/index.ts | 14 +-- src/client/app/router.ts | 4 +- .../theme-default/components/SideBar.ts | 110 ++---------------- .../theme-default/components/SideBarItem.ts | 91 +++++++++++++++ src/client/theme-default/utils.ts | 2 +- 5 files changed, 112 insertions(+), 109 deletions(-) create mode 100644 src/client/theme-default/components/SideBarItem.ts diff --git a/src/client/app/index.ts b/src/client/app/index.ts index 94b836e8..d5d4b732 100644 --- a/src/client/app/index.ts +++ b/src/client/app/index.ts @@ -14,27 +14,27 @@ export function createApp() { let isInitialPageLoad = inBrowser let initialPath: string - const router = createRouter((route) => { - let pagePath = pathToFile(route.path) + const router = createRouter((path) => { + let pageFilePath = pathToFile(path) if (isInitialPageLoad) { - initialPath = pagePath + initialPath = pageFilePath } // use lean build if this is the initial page load or navigating back // to the initial loaded path (the static vnodes already adopted the // static content on that load so no need to re-fetch the page) - if (isInitialPageLoad || initialPath === pagePath) { - pagePath = pagePath.replace(/\.js$/, '.lean.js') + if (isInitialPageLoad || initialPath === pageFilePath) { + pageFilePath = pageFilePath.replace(/\.js$/, '.lean.js') } if (inBrowser) { isInitialPageLoad = false // in browser: native dynamic import - return import(/*@vite-ignore*/ pagePath) + return import(/*@vite-ignore*/ pageFilePath) } else { // SSR, sync require - return require(pagePath) + return require(pageFilePath) } }, NotFound) diff --git a/src/client/app/router.ts b/src/client/app/router.ts index 5eabcf2c..a52f37c7 100644 --- a/src/client/app/router.ts +++ b/src/client/app/router.ts @@ -34,7 +34,7 @@ interface PageModule { } export function createRouter( - loadPageModule: (route: Route) => PageModule | Promise, + loadPageModule: (path: string) => PageModule | Promise, fallbackComponent?: Component ): Router { const route = reactive(getDefaultRoute()) @@ -61,7 +61,7 @@ export function createRouter( const targetLoc = new URL(href, fakeHost) const pendingPath = (latestPendingPath = targetLoc.pathname) try { - let page = loadPageModule(route) + let page = loadPageModule(pendingPath) // only await if it returns a Promise - this allows sync resolution // on initial render in SSR. if ('then' in page && typeof page.then === 'function') { diff --git a/src/client/theme-default/components/SideBar.ts b/src/client/theme-default/components/SideBar.ts index 86e4b583..69eb7807 100644 --- a/src/client/theme-default/components/SideBar.ts +++ b/src/client/theme-default/components/SideBar.ts @@ -1,31 +1,21 @@ -import { useRoute, useSiteDataByRoute, useSiteData } from 'vitepress' -import { computed, h, FunctionalComponent, VNode } from 'vue' +import { useRoute, useSiteDataByRoute } from 'vitepress' +import { computed } from 'vue' import { Header } from '../../../../types/shared' -import { isActive, joinUrl, getPathDirName } from '../utils' +import { getPathDirName } from '../utils' import { DefaultTheme } from '../config' import { useActiveSidebarLinks } from '../composables/activeSidebarLink' import NavBarLinks from './NavBarLinks.vue' +import { SideBarItem } from './SideBarItem' -const SideBarItem: FunctionalComponent<{ - item: ResolvedSidebarItem -}> = (props) => { - const { - item: { link: relLink, text, children } - } = props - - const route = useRoute() - const siteData = useSiteData() - - const link = resolveLink(siteData.value.base, relLink || '') - const active = isActive(route, link) - const headers = route.data.headers - - return h('li', { class: 'sidebar-item' }, [ - createLink(active, text, link), - createChildren(active, children, headers) - ]) +export interface ResolvedSidebarItem { + text: string + link?: string + isGroup?: boolean + children?: ResolvedSidebarItem[] } +type ResolvedSidebar = ResolvedSidebarItem[] + export default { components: { NavBarLinks, @@ -77,19 +67,6 @@ export default { } } -interface HeaderWithChildren extends Header { - children?: Header[] -} - -type ResolvedSidebar = ResolvedSidebarItem[] - -interface ResolvedSidebarItem { - text: string - link?: string - isGroup?: boolean - children?: ResolvedSidebarItem[] -} - function resolveAutoSidebar(headers: Header[], depth: number): ResolvedSidebar { const ret: ResolvedSidebar = [] @@ -144,68 +121,3 @@ function resolveMultiSidebar( return [] } - -function resolveLink(base: string, path: string): string | undefined { - return path - ? // keep relative hash to the same page - path.startsWith('#') - ? path - : joinUrl(base, path) - : undefined -} - -function createLink(active: boolean, text: string, link?: string): VNode { - const tag = link ? 'a' : 'p' - - const component = { - class: { 'sidebar-link': true, active }, - href: link - } - - return h(tag, component, text) -} - -function createChildren( - active: boolean, - children?: ResolvedSidebarItem[], - headers?: Header[] -): VNode | null { - if (children && children.length > 0) { - return h( - 'ul', - { class: 'sidebar-items' }, - children.map((c) => { - return h(SideBarItem, { item: c }) - }) - ) - } - - return active && headers - ? createChildren(false, resolveHeaders(headers)) - : null -} - -function resolveHeaders(headers: Header[]): ResolvedSidebarItem[] { - return mapHeaders(groupHeaders(headers)) -} - -function groupHeaders(headers: Header[]): HeaderWithChildren[] { - headers = headers.map((h) => Object.assign({}, h)) - let lastH2: HeaderWithChildren - headers.forEach((h) => { - if (h.level === 2) { - lastH2 = h - } else if (lastH2) { - ;(lastH2.children || (lastH2.children = [])).push(h) - } - }) - return headers.filter((h) => h.level === 2) -} - -function mapHeaders(headers: HeaderWithChildren[]): ResolvedSidebarItem[] { - return headers.map((header) => ({ - text: header.title, - link: `#${header.slug}`, - children: header.children ? mapHeaders(header.children) : undefined - })) -} diff --git a/src/client/theme-default/components/SideBarItem.ts b/src/client/theme-default/components/SideBarItem.ts new file mode 100644 index 00000000..0d213a30 --- /dev/null +++ b/src/client/theme-default/components/SideBarItem.ts @@ -0,0 +1,91 @@ +import { useRoute, useSiteData } from 'vitepress' +import { FunctionalComponent, h, VNode } from 'vue' +import { Header } from '../../../../types/shared' +import { joinUrl, isActive } from '../utils' +import { ResolvedSidebarItem } from './SideBar' + +interface HeaderWithChildren extends Header { + children?: Header[] +} + +export const SideBarItem: FunctionalComponent<{ + item: ResolvedSidebarItem +}> = (props) => { + const { + item: { link: relLink, text, children } + } = props + + const route = useRoute() + const siteData = useSiteData() + + const link = resolveLink(siteData.value.base, relLink || '') + const active = isActive(route, link) + const headers = route.data.headers + const childItems = createChildren(active, children, headers) + + return h('li', { class: 'sidebar-item' }, [ + h( + link ? 'a' : 'p', + { + class: { 'sidebar-link': true, active }, + href: link + }, + text + ), + childItems + ]) +} + +function resolveLink(base: string, path: string): string | undefined { + return path + ? // keep relative hash to the same page + path.startsWith('#') + ? path + : joinUrl(base, path) + : undefined +} + +function createChildren( + active: boolean, + children?: ResolvedSidebarItem[], + headers?: Header[] +): VNode | null { + if (children && children.length > 0) { + return h( + 'ul', + { class: 'sidebar-items' }, + children.map((c) => { + return h(SideBarItem, { item: c }) + }) + ) + } + + return active && headers + ? createChildren(false, resolveHeaders(headers)) + : null +} + +function resolveHeaders(headers: Header[]): ResolvedSidebarItem[] { + return mapHeaders(groupHeaders(headers)) +} + +function groupHeaders(headers: Header[]): HeaderWithChildren[] { + headers = headers.map((h) => Object.assign({}, h)) + let lastH2: HeaderWithChildren + headers.forEach((h) => { + if (h.level === 2) { + lastH2 = h + } else if (lastH2) { + ;(lastH2.children || (lastH2.children = [])).push(h) + } + }) + return headers.filter((h) => h.level === 2) +} + +function mapHeaders(headers: HeaderWithChildren[]): ResolvedSidebarItem[] { + return headers.map((header) => ({ + text: header.title, + link: `#${header.slug}`, + children: header.children ? mapHeaders(header.children) : undefined + })) +} diff --git a/src/client/theme-default/utils.ts b/src/client/theme-default/utils.ts index d0b97b95..0e8ebc67 100644 --- a/src/client/theme-default/utils.ts +++ b/src/client/theme-default/utils.ts @@ -1,7 +1,7 @@ import { useSiteData, Route } from 'vitepress' export const hashRE = /#.*$/ -export const extRE = /\.(md|html)$/ +export const extRE = /(index)?\.(md|html)$/ export const endingSlashRE = /\/$/ export const outboundRE = /^[a-z]+:/i