diff --git a/docs/.vuepress/config.ts b/docs/.vuepress/config.ts index f78ce59c..bf1fa23e 100644 --- a/docs/.vuepress/config.ts +++ b/docs/.vuepress/config.ts @@ -31,7 +31,13 @@ export default defineUserConfig({ link: 'typescript', dir: 'typescript', text: 'Typescript', - sidebar: [], + sidebar: [ + '', + { + text: '123', + children: ['1', '2'], + }, + ], }, ], }, diff --git a/docs/notes/typescript/README.md b/docs/notes/typescript/README.md index 32eb34d4..f31962d7 100644 --- a/docs/notes/typescript/README.md +++ b/docs/notes/typescript/README.md @@ -4,3 +4,37 @@ createTime: 2022/04/04 09:45:31 author: pengzhanbo permalink: /note/typescript/ --- + +## typescript + +### haha + +内容 + +::: tip +提示 +::: + +::: info +信息 +::: + +::: note +注释 +::: + +::: warning +警告 +::: + +::: danger +危险 +::: + +::: details +详情 +::: + + +- [ ] todo +- [x] todo diff --git a/packages/theme/src/client/components/NavbarItems.vue b/packages/theme/src/client/components/NavbarItems.vue index 7ee43c8e..e8572ba6 100644 --- a/packages/theme/src/client/components/NavbarItems.vue +++ b/packages/theme/src/client/components/NavbarItems.vue @@ -41,31 +41,33 @@ const navbarLinks = computed(() => [ diff --git a/packages/theme/src/client/components/SidebarItems.vue b/packages/theme/src/client/components/SidebarItems.vue index e69de29b..1b9aef4b 100644 --- a/packages/theme/src/client/components/SidebarItems.vue +++ b/packages/theme/src/client/components/SidebarItems.vue @@ -0,0 +1,51 @@ + + + diff --git a/packages/theme/src/client/composables/index.ts b/packages/theme/src/client/composables/index.ts index 652c3c5a..5661da0c 100644 --- a/packages/theme/src/client/composables/index.ts +++ b/packages/theme/src/client/composables/index.ts @@ -5,6 +5,7 @@ export * from './navLink' export * from './resolveRouteWithRedirect' export * from './postIndex' +export * from './sidebarIndex' export * from './postList' export * from './scrollPromise' diff --git a/packages/theme/src/client/composables/sidebarIndex.ts b/packages/theme/src/client/composables/sidebarIndex.ts new file mode 100644 index 00000000..62f00eb2 --- /dev/null +++ b/packages/theme/src/client/composables/sidebarIndex.ts @@ -0,0 +1,42 @@ +import { sidebarIndex as sidebarIndexRaw } from '@internal/sidebarIndex.js' +import { ref } from 'vue' +import type { Ref } from 'vue' +import type { SidebarOptions } from '../../shared' +import { useThemeLocaleData } from './themeData' + +export type SidebarIndexRef = Ref> + +export type SidebarRef = Ref + +export const sidebarIndex: SidebarIndexRef = ref(sidebarIndexRaw) + +interface UseSidebarIndex { + sidebarList: SidebarRef + initSidebarList: (path: string) => void +} + +export const useSidebarIndex = (): UseSidebarIndex => { + const sidebarList: SidebarRef = ref([]) + const themeLocale = useThemeLocaleData() + const notes = themeLocale.value.notes + function initSidebarList(path = ''): void { + if (!notes) return + const prefix = notes.link?.replace(/^\/|\/$/g, '') + if (path.startsWith(`/${prefix}`)) { + Object.keys(sidebarIndex.value).forEach((key) => { + if (path.startsWith(key)) { + sidebarList.value = sidebarIndex.value[key] + } + }) + } + } + return { sidebarList, initSidebarList } +} + +if (import.meta.hot) { + __VUE_HMR_RUNTIME__.updateSidebarIndex = ( + data: Record + ) => { + sidebarIndex.value = data + } +} diff --git a/packages/theme/src/client/shim.d.ts b/packages/theme/src/client/shim.d.ts index ebe7ff4e..3847f280 100644 --- a/packages/theme/src/client/shim.d.ts +++ b/packages/theme/src/client/shim.d.ts @@ -11,3 +11,9 @@ declare module '@internal/postIndex.js' { const postIndex: PostIndex export { postIndex } } + +declare module '@internal/sidebarIndex.js' { + import type { SidebarOptions } from '../shared' + const sidebarIndex: Record + export { sidebarIndex } +} diff --git a/packages/theme/src/node/prepared/index.ts b/packages/theme/src/node/prepared/index.ts index dbd6a53b..a8075942 100644 --- a/packages/theme/src/node/prepared/index.ts +++ b/packages/theme/src/node/prepared/index.ts @@ -1,12 +1,14 @@ import type { App } from '@vuepress/core' import type { PlumeThemeLocaleOptions } from '../../shared' import { preparedPostIndex, watchPostIndex } from './postIndex' +import { preparedSidebarIndex } from './sidebarIndex' export const onPrepared = ( app: App, localeOption: PlumeThemeLocaleOptions ): void => { preparedPostIndex(app, localeOption) + preparedSidebarIndex(app, localeOption) } export const preparedWatch = ( diff --git a/packages/theme/src/node/prepared/sidebarIndex.ts b/packages/theme/src/node/prepared/sidebarIndex.ts new file mode 100644 index 00000000..f2389a01 --- /dev/null +++ b/packages/theme/src/node/prepared/sidebarIndex.ts @@ -0,0 +1,176 @@ +import type { App, Page } from '@vuepress/core' +import { path } from '@vuepress/utils' +import * as chokidar from 'chokidar' +import type { + PlumeThemeLocaleOptions, + PlumeThemeNotesItem, + PlumeThemeNotesOptions, + PlumeThemePageData, + PlumeThemeSidebarConfigOptions, + SidebarItem, + SidebarOptions, +} from '../../shared' + +const HMR_CODE = ` +if (import.meta.webpackHot) { + import.meta.webpackHot.accept() + if (__VUE_HMR_RUNTIME__.updateSidebarIndex) { + __VUE_HMR_RUNTIME__.updatePostIndex(sidebarIndex) + } +} + +if (import.meta.hot) { + import.meta.hot.accept(({ sidebarIndex }) => { + __VUE_HMR_RUNTIME__.updateSidebarIndex(sidebarIndex) + }) +} +` + +interface NotePage { + relativePath: string[] + title: string + link: string +} + +export const preparedSidebarIndex = ( + app: App, + { notes }: PlumeThemeLocaleOptions +): void => { + const pages = app.pages as Page[] + if (notes === false) return + const { + notes: noteList, + dir: rootDir, + link: rootLink, + } = notes as PlumeThemeNotesOptions + const notePageList: NotePage[] = pages + .filter((page) => page.filePathRelative?.startsWith(rootDir as string)) + .map((page) => { + return { + relativePath: page.filePathRelative?.split('/').slice(1) || [], + title: page.title, + link: page.path, + } + }) + + const sidebarMap: Record = {} + noteList.forEach((note) => { + sidebarMap[path.join('/', rootLink, note.link)] = noteSidebar( + note, + notePageList.filter( + (page) => + page.relativePath?.[0] === note.dir.trim().replace(/^\/|\/$/g, '') + ) + ) + }) + + let content = ` +export const sidebarIndex = ${JSON.stringify(sidebarMap, null, 2)} +` + if (app.env.isDev) { + content += HMR_CODE + } + + app.writeTemp('internal/sidebarIndex.js', content) +} + +function noteSidebar( + note: PlumeThemeNotesItem, + notePageList: NotePage[] +): SidebarOptions { + if (note.sidebar === undefined) return [] + if (note.sidebar === 'auto') { + return autoSidebar(note, notePageList) + } + return sidebarByConfig( + note.text, + note.link, + note.dir, + note.sidebar, + notePageList + ) +} + +function autoSidebar( + note: PlumeThemeNotesItem, + notePageList: NotePage[] +): SidebarOptions { + return [] +} + +function sidebarByConfig( + text: string, + link: string | undefined, + dir: string, + sidebarConfig: PlumeThemeSidebarConfigOptions, + notePageList: NotePage[] +): SidebarOptions { + return sidebarConfig.map((sidebar) => { + if (typeof sidebar === 'string') { + const current = findNotePage(sidebar, dir, notePageList) + return { + text: current ? current.title : text, + link: current ? current.link : '', + children: [], + } as SidebarItem + } else { + const current = sidebar.link + ? findNotePage(sidebar.link, dir, notePageList) + : undefined + return { + text: sidebar.text, + link: current?.link || sidebar.link, + children: sidebarByConfig( + sidebar.text, + sidebar.link, + dir, + sidebar.children, + notePageList + ), + } + } + }) +} + +function findNotePage( + sidebar: string, + dir: string, + notePageList: NotePage[] +): NotePage | undefined { + if (sidebar === '' || sidebar === 'README.md' || sidebar === 'index.md') { + return notePageList.find((page) => { + const relative = page.relativePath.join('/') + return ( + relative === path.join(dir, 'README.md') || + relative === path.join(dir, 'index.md') + ) + }) + } else { + return notePageList.find((page) => { + const relative = page.relativePath.join('/') + return ( + relative === path.join(dir, sidebar) || + relative === path.join(dir, sidebar + '.md') || + page.link === sidebar + ) + }) + } +} + +export const watchSidebarIndex = ( + app: App, + watchers: any[], + localeOption: PlumeThemeLocaleOptions +): void => { + if (!localeOption.notes || !localeOption.notes.dir) return + const dir = path.join('pages', localeOption.notes.dir, '**/*') + const watcher = chokidar.watch(dir, { + cwd: app.dir.temp(), + ignoreInitial: true, + }) + + watcher.on('add', () => preparedSidebarIndex(app, localeOption)) + watcher.on('change', () => preparedSidebarIndex(app, localeOption)) + watcher.on('unlink', () => preparedSidebarIndex(app, localeOption)) + watchers.push(watcher) +} diff --git a/packages/theme/src/shared/layout/index.ts b/packages/theme/src/shared/layout/index.ts index 06c22c60..032fb798 100644 --- a/packages/theme/src/shared/layout/index.ts +++ b/packages/theme/src/shared/layout/index.ts @@ -1 +1,3 @@ export * from './navbar' + +export * from './sidebar' diff --git a/packages/theme/src/shared/layout/sidebar.ts b/packages/theme/src/shared/layout/sidebar.ts new file mode 100644 index 00000000..72460ebb --- /dev/null +++ b/packages/theme/src/shared/layout/sidebar.ts @@ -0,0 +1,7 @@ +export interface SidebarItem { + text: string + link?: string + children: SidebarOptions +} + +export type SidebarOptions = SidebarItem[] diff --git a/packages/theme/src/shared/options/notes.ts b/packages/theme/src/shared/options/notes.ts index 7f2d9ae3..8c989aca 100644 --- a/packages/theme/src/shared/options/notes.ts +++ b/packages/theme/src/shared/options/notes.ts @@ -29,5 +29,15 @@ export interface PlumeThemeNotesItem { text: string link: string dir: string - sidebar: string[] + sidebar?: PlumeThemeSidebarConfigOptions | 'auto' +} + +export type PlumeThemeSidebarConfigOptions = ( + | PlumeThemeNotesConfigItem + | string +)[] +export interface PlumeThemeNotesConfigItem { + text: string + link?: string + children: PlumeThemeNotesConfigItem[] }