feat(plugin-notes-data): provide notes-data
This commit is contained in:
parent
8120ad3e65
commit
86f51ff271
@ -1,6 +1,8 @@
|
||||
import { definePlumeNotesConfig } from '@vuepress-plume/vuepress-theme-plume'
|
||||
|
||||
export default definePlumeNotesConfig({
|
||||
dir: 'notes',
|
||||
link: '/note',
|
||||
notes: [
|
||||
{
|
||||
text: '',
|
||||
@ -10,22 +12,22 @@ export default definePlumeNotesConfig({
|
||||
'',
|
||||
{
|
||||
text: '指南',
|
||||
children: ['快速开始', '编写文章'],
|
||||
items: ['快速开始', '编写文章'],
|
||||
},
|
||||
{
|
||||
text: '配置',
|
||||
children: [
|
||||
items: [
|
||||
{
|
||||
text: '主题配置',
|
||||
link: '主题配置',
|
||||
children: ['主题插件配置', 'notes配置'],
|
||||
items: ['主题插件配置', 'notes配置'],
|
||||
},
|
||||
'页面配置',
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '功能',
|
||||
children: ['基础功能', 'markdown增强'],
|
||||
items: ['基础功能', 'markdown增强'],
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -39,7 +41,7 @@ export default definePlumeNotesConfig({
|
||||
dir: 'netlify-functions',
|
||||
text: 'plugin-netlify-functions',
|
||||
link: 'netlify-functions',
|
||||
children: ['', '介绍', '使用', '功能', 'API', 'functions开发指南'],
|
||||
items: ['', '介绍', '使用', '功能', 'API', 'functions开发指南'],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@ -5,13 +5,13 @@ import type { BlogPostData } from '../../shared/index.js'
|
||||
|
||||
declare const __VUE_HMR_RUNTIME__: Record<string, any>
|
||||
|
||||
export type ThemeDataRef<T extends BlogPostData = BlogPostData> = Ref<T>
|
||||
export type BlogDataRef<T extends BlogPostData = BlogPostData> = Ref<T>
|
||||
|
||||
export const blogPostData: ThemeDataRef = ref(blogPostDataRaw)
|
||||
export const blogPostData: BlogDataRef = ref(blogPostDataRaw)
|
||||
|
||||
export const useBlogPostData = <
|
||||
T extends BlogPostData = BlogPostData
|
||||
>(): ThemeDataRef<T> => blogPostData as ThemeDataRef<T>
|
||||
>(): BlogDataRef<T> => blogPostData as BlogDataRef<T>
|
||||
|
||||
if (import.meta.webpackHot || import.meta.hot) {
|
||||
__VUE_HMR_RUNTIME__.updateBlogData = (data: BlogPostData) => {
|
||||
|
||||
@ -30,10 +30,14 @@
|
||||
"ts:watch": "tsc -b tsconfig.build.json --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vue/devtools-api": "^6.4.5",
|
||||
"@vuepress/client": "2.0.0-beta.60",
|
||||
"@vuepress/core": "2.0.0-beta.60",
|
||||
"@vuepress/shared": "2.0.0-beta.60",
|
||||
"@vuepress/utils": "2.0.0-beta.60"
|
||||
"@vuepress/utils": "2.0.0-beta.60",
|
||||
"chokidar": "^3.5.3",
|
||||
"create-filter": "^1.0.0",
|
||||
"vue": "^3.2.47"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@ -1,7 +1,37 @@
|
||||
import { setupDevtoolsPlugin } from '@vue/devtools-api'
|
||||
import { defineClientConfig } from '@vuepress/client'
|
||||
import { useNotesData } from './composables/index.js'
|
||||
|
||||
declare const __VUE_PROD_DEVTOOLS__: boolean
|
||||
|
||||
export default defineClientConfig({
|
||||
setup() {
|
||||
// do something
|
||||
enhance({ app }) {
|
||||
const notesData = useNotesData()
|
||||
|
||||
// setup devtools in dev mode
|
||||
if (__VUEPRESS_DEV__ || __VUE_PROD_DEVTOOLS__) {
|
||||
setupDevtoolsPlugin(
|
||||
{
|
||||
// fix recursive reference
|
||||
app: app as any,
|
||||
id: 'org.vuepress-plume.plugin-notes-data',
|
||||
label: 'VuePress Notes Data Plugin',
|
||||
packageName: '@vuepress/plugin-notes-data',
|
||||
homepage: 'https://pengzhanbo.cn',
|
||||
logo: 'https://v2.vuepress.vuejs.org/images/hero.png',
|
||||
componentStateTypes: ['VuePress'],
|
||||
},
|
||||
(api) => {
|
||||
api.on.inspectComponent((payload) => {
|
||||
payload.instanceData.state.push({
|
||||
type: 'VuePress',
|
||||
key: 'notesData',
|
||||
editable: false,
|
||||
value: notesData.value,
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@ -0,0 +1 @@
|
||||
export * from './notesDate.js'
|
||||
@ -0,0 +1,20 @@
|
||||
import { notesData as notesDataRaw } from '@internal/notesData'
|
||||
import { ref } from 'vue'
|
||||
import type { Ref } from 'vue'
|
||||
import type { NotesData } from '../../shared/index.js'
|
||||
|
||||
declare const __VUE_HMR_RUNTIME__: Record<string, any>
|
||||
|
||||
export type NotesDataRef<T extends NotesData = NotesData> = Ref<T>
|
||||
|
||||
export const notesData: NotesDataRef = ref(notesDataRaw)
|
||||
|
||||
export const useNotesData = <
|
||||
T extends NotesData = NotesData
|
||||
>(): NotesDataRef<T> => notesData as NotesDataRef<T>
|
||||
|
||||
if (import.meta.webpackHot || import.meta.hot) {
|
||||
__VUE_HMR_RUNTIME__.updateNotesData = (data: NotesData) => {
|
||||
notesData.value = data
|
||||
}
|
||||
}
|
||||
@ -1 +1,2 @@
|
||||
export * from '../shared/index.js'
|
||||
export { NotesData, NotesSidebarItem } from '../shared/index.js'
|
||||
export * from './composables/index.js'
|
||||
|
||||
7
packages/plugin-notes-data/src/client/notesData.d.ts
vendored
Normal file
7
packages/plugin-notes-data/src/client/notesData.d.ts
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
import type { NotesData } from '../shared/index.js'
|
||||
|
||||
declare module '@internal/notesData' {
|
||||
const notesData: NotesData
|
||||
|
||||
export { notesData }
|
||||
}
|
||||
@ -1,12 +1,18 @@
|
||||
import type { App, Plugin } from '@vuepress/core'
|
||||
import { path } from '@vuepress/utils'
|
||||
import { getDirname, path } from '@vuepress/utils'
|
||||
import type { NotesDataOptions } from '../shared/index.js'
|
||||
import { prepareNotesData, watchNotesData } from './prepareNotesData.js'
|
||||
|
||||
export const notesDataPlugin = (options: NotesDataOptions): Plugin => {
|
||||
return (app: App) => {
|
||||
return {
|
||||
name: '@vuepress-plume/vuepress-plugin-notes-data',
|
||||
clientConfigFile: path.resolve(__dirname, '../client/clientConfig.js'),
|
||||
clientConfigFile: path.resolve(
|
||||
getDirname(import.meta.url),
|
||||
'../client/clientConfig.js'
|
||||
),
|
||||
onPrepared: () => prepareNotesData(app, options),
|
||||
onWatched: (app, watchers) => watchNotesData(app, watchers, options),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
159
packages/plugin-notes-data/src/node/prepareNotesData.ts
Normal file
159
packages/plugin-notes-data/src/node/prepareNotesData.ts
Normal file
@ -0,0 +1,159 @@
|
||||
import path from 'node:path'
|
||||
import type { App } from '@vuepress/core'
|
||||
import * as chokidar from 'chokidar'
|
||||
import { createFilter } from 'create-filter'
|
||||
import type {
|
||||
NotesData,
|
||||
NotesDataOptions,
|
||||
NotesItem,
|
||||
NotesSidebar,
|
||||
NotesSidebarItem,
|
||||
} from '../shared/index.js'
|
||||
import { ensureArray } from './utils.js'
|
||||
|
||||
const HMR_CODE = `
|
||||
if (import.meta.webpackHot) {
|
||||
import.meta.webpackHot.accept()
|
||||
if (__VUE_HMR_RUNTIME__.updateNotesData) {
|
||||
__VUE_HMR_RUNTIME__.updateNotesData(notesData)
|
||||
}
|
||||
}
|
||||
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.accept(({ notesData }) => {
|
||||
__VUE_HMR_RUNTIME__.updateNotesData(notesData)
|
||||
})
|
||||
}
|
||||
`
|
||||
|
||||
interface NotePage {
|
||||
relativePath: string
|
||||
title: string
|
||||
link: string
|
||||
}
|
||||
|
||||
export const prepareNotesData = async (
|
||||
app: App,
|
||||
{ include, exclude, notes, dir, link }: NotesDataOptions
|
||||
) => {
|
||||
if (!notes || notes.length === 0) return
|
||||
const filter = createFilter(ensureArray(include), ensureArray(exclude), {
|
||||
resolve: false,
|
||||
})
|
||||
const DIR_PATTERN = new RegExp(`^${path.join(dir, '/')}`)
|
||||
const notesPageList: NotePage[] = app.pages
|
||||
.filter(
|
||||
(page) =>
|
||||
page.filePathRelative &&
|
||||
page.filePathRelative.startsWith(dir) &&
|
||||
filter(page.filePathRelative)
|
||||
)
|
||||
.map((page) => {
|
||||
return {
|
||||
relativePath: page.filePathRelative?.replace(DIR_PATTERN, '') || '',
|
||||
title: page.title,
|
||||
link: page.path,
|
||||
}
|
||||
})
|
||||
|
||||
const notesData: NotesData = {}
|
||||
notes.forEach((note) => {
|
||||
notesData[path.join('/', link, note.link)] = initSidebar(
|
||||
note,
|
||||
notesPageList.filter((page) =>
|
||||
page.relativePath.startsWith(note.dir.trim().replace(/^\/|\/$/g, ''))
|
||||
)
|
||||
)
|
||||
})
|
||||
let content = `
|
||||
export const notesData = ${JSON.stringify(notesData, null, 2)}
|
||||
`
|
||||
if (app.env.isDev) {
|
||||
content += HMR_CODE
|
||||
}
|
||||
|
||||
await app.writeTemp('internal/notesData.js', content)
|
||||
}
|
||||
|
||||
export const watchNotesData = (
|
||||
app: App,
|
||||
watchers: any[],
|
||||
options: NotesDataOptions
|
||||
): void => {
|
||||
if (!options.notes || options.notes.length === 0 || !options.dir) return
|
||||
const dir = path.join('pages', options.dir, '**/*')
|
||||
const watcher = chokidar.watch(dir, {
|
||||
cwd: app.dir.temp(),
|
||||
ignoreInitial: true,
|
||||
})
|
||||
|
||||
watcher.on('add', () => prepareNotesData(app, options))
|
||||
watcher.on('change', () => prepareNotesData(app, options))
|
||||
watcher.on('unlink', () => prepareNotesData(app, options))
|
||||
watchers.push(watcher)
|
||||
}
|
||||
|
||||
function initSidebar(note: NotesItem, pages: NotePage[]): NotesSidebarItem[] {
|
||||
console.log('pages:', pages)
|
||||
if (!note.sidebar) return []
|
||||
if (note.sidebar === 'auto') return []
|
||||
return initSidebarByConfig(note, pages)
|
||||
}
|
||||
|
||||
function initSidebarByConfig(
|
||||
{ text, link, dir, sidebar }: NotesItem,
|
||||
pages: NotePage[]
|
||||
): NotesSidebarItem[] {
|
||||
return (sidebar as NotesSidebar).map((item) => {
|
||||
console.log('text: ', text, 's-item: ', item, 'dir: ', dir)
|
||||
if (typeof item === 'string') {
|
||||
const current = findNotePage(item, dir, pages)
|
||||
return {
|
||||
text: current?.title || text,
|
||||
link: current?.link,
|
||||
items: [],
|
||||
}
|
||||
} else {
|
||||
// link = path.join(link || '', item.link || '')
|
||||
const current = findNotePage(item.link || '', dir, pages)
|
||||
return {
|
||||
text: item.text || item.dir || current?.title,
|
||||
link: current?.link,
|
||||
items: initSidebarByConfig(
|
||||
{
|
||||
link: item.link || '',
|
||||
text: item.text || '',
|
||||
sidebar: item.items,
|
||||
dir: path.join(dir, item.dir || ''),
|
||||
},
|
||||
pages
|
||||
),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
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
|
||||
return (
|
||||
relative === path.join(dir, 'README.md') ||
|
||||
relative === path.join(dir, 'index.md')
|
||||
)
|
||||
})
|
||||
} else {
|
||||
return notePageList.find((page) => {
|
||||
const relative = page.relativePath
|
||||
return (
|
||||
relative === path.join(dir, sidebar) ||
|
||||
relative === path.join(dir, sidebar + '.md') ||
|
||||
page.link === sidebar
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
5
packages/plugin-notes-data/src/node/utils.ts
Normal file
5
packages/plugin-notes-data/src/node/utils.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export function ensureArray<T>(thing: T | T[] | null | undefined): T[] {
|
||||
if (Array.isArray(thing)) return thing
|
||||
if (thing === null || thing === undefined) return []
|
||||
return [thing]
|
||||
}
|
||||
@ -1,3 +1,25 @@
|
||||
export interface NotesDataOptions {
|
||||
a?: string
|
||||
export type NotesDataOptions = {
|
||||
dir: string
|
||||
link: string
|
||||
include?: string | string[]
|
||||
exclude?: string | string[]
|
||||
notes: NotesItem[]
|
||||
}
|
||||
|
||||
export type NotesItem = {
|
||||
dir: string
|
||||
link: string
|
||||
text: string
|
||||
sidebar?: NotesSidebar | 'auto'
|
||||
}
|
||||
|
||||
export type NotesSidebar = (NotesSidebarItem | string)[]
|
||||
|
||||
export type NotesSidebarItem = {
|
||||
text?: string
|
||||
link?: string
|
||||
dir?: string
|
||||
items?: NotesSidebar
|
||||
}
|
||||
|
||||
export type NotesData = Record<string, NotesSidebarItem[]>
|
||||
|
||||
@ -2,7 +2,11 @@
|
||||
"extends": "../tsconfig.build.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib"
|
||||
"outDir": "./lib",
|
||||
"paths": {
|
||||
"@internal/notesData": ["./src/client/notesData.d.ts"]
|
||||
},
|
||||
"types": ["@vuepress/client/types", "vite/client", "webpack-env"]
|
||||
},
|
||||
"include": ["./src"]
|
||||
}
|
||||
|
||||
@ -39,6 +39,7 @@
|
||||
"@vuepress-plume/vuepress-plugin-blog-data": "workspace:*",
|
||||
"@vuepress-plume/vuepress-plugin-caniuse": "workspace:*",
|
||||
"@vuepress-plume/vuepress-plugin-copy-code": "workspace:*",
|
||||
"@vuepress-plume/vuepress-plugin-notes-data": "workspace:*",
|
||||
"@vuepress/client": "2.0.0-beta.60",
|
||||
"@vuepress/core": "2.0.0-beta.60",
|
||||
"@vuepress/plugin-active-header-links": "2.0.0-beta.60",
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
import type {
|
||||
PlumeThemeNotesItem,
|
||||
PlumeThemeNotesOptions,
|
||||
} from '../shared/index.js'
|
||||
NotesDataOptions,
|
||||
NotesItem,
|
||||
} from '@vuepress-plume/vuepress-plugin-notes-data'
|
||||
|
||||
export const definePlumeNotesConfig = (
|
||||
notes: PlumeThemeNotesOptions
|
||||
): PlumeThemeNotesOptions => notes
|
||||
notes: NotesDataOptions
|
||||
): NotesDataOptions => notes
|
||||
|
||||
export const definePlumeNotesItemConfig = (
|
||||
item: PlumeThemeNotesItem
|
||||
): PlumeThemeNotesItem => item
|
||||
export const definePlumeNotesItemConfig = (item: NotesItem): NotesItem => item
|
||||
|
||||
@ -3,6 +3,7 @@ import { baiduTongjiPlugin } from '@vuepress-plume/vuepress-plugin-baidu-tongji'
|
||||
import { blogDataPlugin } from '@vuepress-plume/vuepress-plugin-blog-data'
|
||||
import { caniusePlugin } from '@vuepress-plume/vuepress-plugin-caniuse'
|
||||
import { copyCodePlugin } from '@vuepress-plume/vuepress-plugin-copy-code'
|
||||
import { notesDataPlugin } from '@vuepress-plume/vuepress-plugin-notes-data'
|
||||
import type { App, PluginConfig } from '@vuepress/core'
|
||||
import { activeHeaderLinksPlugin } from '@vuepress/plugin-active-header-links'
|
||||
import { docsearchPlugin } from '@vuepress/plugin-docsearch'
|
||||
@ -39,6 +40,7 @@ export const setupPlugins = (
|
||||
include: ['**/*.md'],
|
||||
exclude: ['**/{README,index}.md', 'notes/**'],
|
||||
}),
|
||||
localeOptions.notes ? notesDataPlugin(localeOptions.notes) : [],
|
||||
activeHeaderLinksPlugin({
|
||||
headerLinkSelector: 'a.theme-plume-toc-link',
|
||||
headerAnchorSelector: '.header-anchor',
|
||||
|
||||
@ -39,10 +39,6 @@ export interface PlumeThemeOptions extends PlumeThemeLocaleOptions {
|
||||
*/
|
||||
exclude?: string[]
|
||||
}
|
||||
|
||||
notes?: {
|
||||
dir?: string
|
||||
}
|
||||
}
|
||||
|
||||
export type PlumeThemeLocaleOptions = PlumeThemeData
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import type { NotesDataOptions } from '@vuepress-plume/vuepress-plugin-notes-data'
|
||||
import type { LocaleData } from '@vuepress/core'
|
||||
// import type { NavbarConfig, NavLink } from '../layout/index.js'
|
||||
// import type { PlumeThemeNotesOptions } from './notes.js'
|
||||
@ -5,7 +6,6 @@ import type { LocaleData } from '@vuepress/core'
|
||||
// todo type
|
||||
type NavbarConfig = any
|
||||
type NavLink = any
|
||||
type PlumeThemeNotesOptions = any
|
||||
|
||||
export interface PlumeThemeAvatar {
|
||||
/**
|
||||
@ -135,7 +135,7 @@ export interface PlumeThemeLocaleData extends LocaleData {
|
||||
*
|
||||
* 注:你也可以将notes配置到navbar中,默认自动生成在右侧栏目中
|
||||
*/
|
||||
notes?: false | PlumeThemeNotesOptions
|
||||
notes?: false | NotesDataOptions
|
||||
|
||||
footer?: false | { content: string; copyright: string }
|
||||
|
||||
|
||||
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
@ -213,15 +213,23 @@ importers:
|
||||
|
||||
packages/plugin-notes-data:
|
||||
specifiers:
|
||||
'@vue/devtools-api': ^6.4.5
|
||||
'@vuepress/client': 2.0.0-beta.60
|
||||
'@vuepress/core': 2.0.0-beta.60
|
||||
'@vuepress/shared': 2.0.0-beta.60
|
||||
'@vuepress/utils': 2.0.0-beta.60
|
||||
chokidar: ^3.5.3
|
||||
create-filter: ^1.0.0
|
||||
vue: ^3.2.47
|
||||
dependencies:
|
||||
'@vue/devtools-api': 6.4.5
|
||||
'@vuepress/client': 2.0.0-beta.60
|
||||
'@vuepress/core': 2.0.0-beta.60
|
||||
'@vuepress/shared': 2.0.0-beta.60
|
||||
'@vuepress/utils': 2.0.0-beta.60
|
||||
chokidar: 3.5.3
|
||||
create-filter: 1.0.0
|
||||
vue: 3.2.47
|
||||
|
||||
packages/plugin-page-collection:
|
||||
specifiers:
|
||||
@ -271,6 +279,7 @@ importers:
|
||||
'@vuepress-plume/vuepress-plugin-blog-data': workspace:*
|
||||
'@vuepress-plume/vuepress-plugin-caniuse': workspace:*
|
||||
'@vuepress-plume/vuepress-plugin-copy-code': workspace:*
|
||||
'@vuepress-plume/vuepress-plugin-notes-data': workspace:*
|
||||
'@vuepress/client': 2.0.0-beta.60
|
||||
'@vuepress/core': 2.0.0-beta.60
|
||||
'@vuepress/plugin-active-header-links': 2.0.0-beta.60
|
||||
@ -305,6 +314,7 @@ importers:
|
||||
'@vuepress-plume/vuepress-plugin-blog-data': link:../plugin-blog-data
|
||||
'@vuepress-plume/vuepress-plugin-caniuse': link:../plugin-caniuse
|
||||
'@vuepress-plume/vuepress-plugin-copy-code': link:../plugin-copy-code
|
||||
'@vuepress-plume/vuepress-plugin-notes-data': link:../plugin-notes-data
|
||||
'@vuepress/client': 2.0.0-beta.60
|
||||
'@vuepress/core': 2.0.0-beta.60
|
||||
'@vuepress/plugin-active-header-links': 2.0.0-beta.60
|
||||
|
||||
@ -9,6 +9,9 @@
|
||||
"@internal/blogData": [
|
||||
"./packages/plugin-blog-data/src/client/blogPostData.d.ts"
|
||||
],
|
||||
"@internal/notesData": [
|
||||
"./packages/plugin-notes-data/src/client/notesData.d.ts"
|
||||
],
|
||||
"@internal/*": ["./docs/.vuepress/.temp/internal/*"],
|
||||
"@vuepress-plume/vuepress-*": ["./packages/*/src/node/index.ts"],
|
||||
"@vuepress-plume/vuepress-theme-plume": [
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user