@sugarat/theme 0.4.13 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/node.d.ts +13 -1
  2. package/node.js +147 -52
  3. package/node.mjs +755 -0
  4. package/package.json +13 -10
  5. package/src/components/BlogAlert.vue +10 -9
  6. package/src/components/BlogArticleAnalyze.vue +8 -9
  7. package/src/components/BlogAuthor.vue +6 -5
  8. package/src/components/BlogBackToTop.vue +8 -10
  9. package/src/components/BlogButtonAfterArticle.vue +5 -14
  10. package/src/components/BlogCommentWrapper.vue +11 -28
  11. package/src/components/BlogDocCover.vue +3 -3
  12. package/src/components/BlogFooter.vue +3 -1
  13. package/src/components/BlogFriendLink.vue +4 -3
  14. package/src/components/BlogHomeBanner.vue +7 -6
  15. package/src/components/BlogHomeHeaderAvatar.vue +3 -3
  16. package/src/components/BlogHomeOverview.vue +4 -4
  17. package/src/components/BlogHomeTags.vue +5 -5
  18. package/src/components/BlogHotArticle.vue +4 -7
  19. package/src/components/BlogItem.vue +4 -3
  20. package/src/components/BlogList.vue +7 -6
  21. package/src/components/BlogRecommendArticle.vue +11 -14
  22. package/src/components/BlogSidebar.vue +9 -15
  23. package/src/components/CommentArtalk.vue +7 -5
  24. package/src/components/CommentGiscus.vue +7 -8
  25. package/src/components/Icon.vue +33 -0
  26. package/src/composables/config/blog.ts +207 -89
  27. package/src/composables/config/index.ts +9 -0
  28. package/src/hooks/useOml2d.ts +15 -8
  29. package/src/index.ts +3 -0
  30. package/src/node.ts +5 -1
  31. package/src/styles/index.scss +6 -6
  32. package/src/utils/node/hot-reload-plugin.ts +31 -1
  33. package/src/utils/node/index.ts +0 -2
  34. package/src/utils/node/mdPlugins.ts +4 -0
  35. package/src/utils/node/theme.ts +15 -18
  36. package/src/utils/node/vitePlugins.ts +120 -34
@@ -1,4 +1,4 @@
1
- import { onMounted } from 'vue'
1
+ import { onMounted, watch } from 'vue'
2
2
  import type { Options } from 'oh-my-live2d'
3
3
  import { useOml2dOptions } from '../composables/config/blog'
4
4
 
@@ -37,13 +37,13 @@ const defaultOptions: Options = {
37
37
  }
38
38
  export function useOml2d() {
39
39
  const oml2dOptions = useOml2dOptions()
40
- onMounted(async () => {
41
- if (oml2dOptions) {
40
+ const init = async () => {
41
+ if (oml2dOptions.value) {
42
42
  const { loadOml2d } = await import('oh-my-live2d')
43
43
  loadOml2d({
44
44
  ...defaultOptions,
45
45
  ...oml2dOptions,
46
- models: oml2dOptions?.models?.map(model => ({
46
+ models: oml2dOptions?.value?.models?.map(model => ({
47
47
  ...defaultModelOptions,
48
48
  ...model,
49
49
  stageStyle: {
@@ -57,27 +57,34 @@ export function useOml2d() {
57
57
  })),
58
58
  tips: {
59
59
  ...defaultOptions.tips,
60
- ...oml2dOptions.tips,
60
+ ...oml2dOptions?.value.tips,
61
61
  style: {
62
62
  // @ts-expect-error
63
63
  ...defaultOptions?.tips?.style,
64
64
  // @ts-expect-error
65
- ...oml2dOptions?.tips?.style
65
+ ...oml2dOptions?.value.tips?.style
66
66
  },
67
67
  mobileStyle: {
68
68
  // @ts-expect-error
69
69
  ...defaultOptions?.tips?.mobileStyle,
70
70
  // @ts-expect-error
71
- ...oml2dOptions?.tips?.mobileStyle
71
+ ...oml2dOptions?.value.tips?.mobileStyle
72
72
  },
73
73
  copyTips: {
74
74
  // @ts-expect-error
75
75
  ...defaultOptions?.tips?.copyTips,
76
76
  // @ts-expect-error
77
- ...oml2dOptions?.tips?.copyTips
77
+ ...oml2dOptions?.value.tips?.copyTips
78
78
  }
79
79
  }
80
80
  })
81
81
  }
82
+ }
83
+ // TODO: destroy
84
+ // watch(oml2dOptions, () => {
85
+ // init()
86
+ // })
87
+ onMounted(() => {
88
+ init()
82
89
  })
83
90
  }
package/src/index.ts CHANGED
@@ -35,6 +35,9 @@ import UserWorksPage from './components/UserWorks.vue'
35
35
  // 内置一些特殊的主题色
36
36
  import './styles/theme/inline-theme.var.css'
37
37
 
38
+ // 导入group icons
39
+ import 'virtual:group-icons.css'
40
+
38
41
  export const BlogTheme: Theme = {
39
42
  ...DefaultTheme,
40
43
  Layout: withConfigProvider(BlogApp),
package/src/node.ts CHANGED
@@ -46,7 +46,7 @@ export function getThemeConfig(cfg: Partial<Theme.BlogConfig> = {}) {
46
46
  return {
47
47
  themeConfig: {
48
48
  blog: {
49
- pagesData,
49
+ pagesData, // 插件里补全
50
50
  ...cfg
51
51
  },
52
52
  // 补充一些额外的配置用于继承
@@ -63,6 +63,10 @@ export function defineConfig(config: UserConfig<Theme.Config>): any {
63
63
  return config
64
64
  }
65
65
 
66
+ export function defineLocaleConfig(cfg: Omit<Theme.BlogConfig, 'locales' | 'pagesData'>) {
67
+ return cfg
68
+ }
69
+
66
70
  // 重新导包 tabsMarkdownPlugin 导出CJS格式支持
67
71
  export { tabsMarkdownPlugin } from 'vitepress-plugin-tabs'
68
72
 
@@ -102,12 +102,12 @@ html.dark {
102
102
  }
103
103
 
104
104
  // sidebar
105
- @media (min-width: 1440px) {
106
- aside.VPSidebar {
107
- padding-left: max(32px, calc((100% - (var(--vp-layout-max-width) - 16px)) / 2)) !important;
108
- padding-right: 10px !important;
109
- }
110
- }
105
+ // @media (min-width: 1440px) {
106
+ // aside.VPSidebar {
107
+ // // padding-left: max(32px, calc((100% - (var(--vp-layout-max-width) - 16px)) / 2)) !important;
108
+ // // padding-right: 10px !important;
109
+ // }
110
+ // }
111
111
 
112
112
  .VPDoc .content main {
113
113
  img {
@@ -1,5 +1,7 @@
1
+ import fs from 'fs'
1
2
  import type { PluginOption } from 'vite'
2
3
  import type { SiteConfig } from 'vitepress'
4
+ import { grayMatter } from '@sugarat/theme-shared'
3
5
  import type { Theme } from '../../composables/config/index'
4
6
  import { getArticleMeta } from './theme'
5
7
  import { debounce, isEqual } from './index'
@@ -21,6 +23,7 @@ export function themeReloadPlugin() {
21
23
  server.restart()
22
24
  }, 500)
23
25
  server.watcher.on('add', async (path) => {
26
+ // TODO: rewrite 和 动态路由兼容
24
27
  const route = generateRoute(path)
25
28
  const meta = await getArticleMeta(path, route, blogConfig?.timeZone)
26
29
  blogConfig.pagesData.push({
@@ -32,10 +35,37 @@ export function themeReloadPlugin() {
32
35
 
33
36
  server.watcher.on('change', async (path: string) => {
34
37
  const route = generateRoute(path)
38
+ const fileContent = await fs.promises.readFile(path, 'utf-8')
39
+ const { data: frontmatter } = grayMatter(fileContent, {
40
+ excerpt: true,
41
+ })
35
42
  const meta = await getArticleMeta(path, route, blogConfig?.timeZone)
36
43
  const matched = blogConfig.pagesData.find(v => v.route === route)
37
44
 
38
- if (matched && !isEqual(matched.meta, meta, ['date', 'description'])) {
45
+ // 自动生成的部分属性不参与比较,避免刷新频繁
46
+ const excludeKeys = ['date', 'description'].filter(key => !frontmatter[key])
47
+ // 主题不关心的属性不参与比较,避免刷新频繁
48
+ const inlineKeys = [
49
+ // vitepress 默认主题 https://vitepress.dev/zh/reference/frontmatter-config
50
+ 'lang',
51
+ 'outline',
52
+ 'head',
53
+ 'layout',
54
+ 'hero',
55
+ 'features',
56
+ 'navbar',
57
+ 'sidebar',
58
+ 'aside',
59
+ 'lastUpdated',
60
+ 'editLink',
61
+ 'footer',
62
+ 'pageClass',
63
+ // 本主题扩展 https://theme.sugarat.top/config/frontmatter.html
64
+ 'hiddenCover',
65
+ 'readingTime',
66
+ 'buttonAfterArticle'
67
+ ]
68
+ if (matched && !isEqual(matched.meta, meta, inlineKeys.concat(excludeKeys))) {
39
69
  matched.meta = meta
40
70
  restart()
41
71
  }
@@ -31,8 +31,6 @@ export function getFirstImagURLFromMD(content: string, route: string) {
31
31
  return url
32
32
  }
33
33
 
34
- // TODO: 其它协议,待补充
35
-
36
34
  const paths = joinPath('/', route).split('/')
37
35
  paths.splice(paths.length - 1, 1)
38
36
  const relativePath = url.startsWith('/') ? url : path.join(paths.join('/') || '', url)
@@ -3,6 +3,7 @@ import { createRequire } from 'module'
3
3
  import { tabsMarkdownPlugin } from 'vitepress-plugin-tabs'
4
4
  import type { UserConfig } from 'vitepress'
5
5
  import timeline from 'vitepress-markdown-timeline'
6
+ import { groupIconMdPlugin } from 'vitepress-plugin-group-icons'
6
7
  import type { Theme } from '../../composables/config/index'
7
8
  import { aliasObjectToArray } from './index'
8
9
 
@@ -30,6 +31,9 @@ export function getMarkdownPlugins(cfg?: Partial<Theme.BlogConfig>) {
30
31
  if (cfg?.timeline !== false) {
31
32
  markdownPlugin.push(timeline)
32
33
  }
34
+
35
+ markdownPlugin.push(groupIconMdPlugin)
36
+
33
37
  return markdownPlugin
34
38
  }
35
39
 
@@ -1,9 +1,6 @@
1
- /* eslint-disable prefer-rest-params */
2
1
  import fs from 'node:fs'
3
2
  import path from 'node:path'
4
- import process from 'node:process'
5
- import glob from 'fast-glob'
6
- import { getDefaultTitle, getFileLastModifyTime, getTextSummary, grayMatter, normalizePath } from '@sugarat/theme-shared'
3
+ import { getDefaultTitle, getFileLastModifyTime, getTextSummary, getVitePressPages, grayMatter, normalizePath, renderDynamicMarkdown } from '@sugarat/theme-shared'
7
4
  import type { SiteConfig } from 'vitepress'
8
5
  import type { Theme } from '../../composables/config/index'
9
6
  import { formatDate } from '../client'
@@ -29,8 +26,8 @@ export function getPageRoute(filepath: string, srcDir: string) {
29
26
  }
30
27
 
31
28
  const defaultTimeZoneOffset = new Date().getTimezoneOffset() / -60
32
- export async function getArticleMeta(filepath: string, route: string, timeZone = defaultTimeZoneOffset) {
33
- const fileContent = await fs.promises.readFile(filepath, 'utf-8')
29
+ export async function getArticleMeta(filepath: string, route: string, timeZone = defaultTimeZoneOffset, baseContent?: string) {
30
+ const fileContent = baseContent || await fs.promises.readFile(filepath, 'utf-8')
34
31
 
35
32
  const { data: frontmatter, excerpt, content } = grayMatter(fileContent, {
36
33
  excerpt: true,
@@ -70,6 +67,7 @@ export async function getArticleMeta(filepath: string, route: string, timeZone =
70
67
  = meta.description || getTextSummary(content, 100) || excerpt
71
68
 
72
69
  // 获取封面图
70
+ // TODO: 耦合信息优化
73
71
  meta.cover
74
72
  = meta.cover
75
73
  ?? (getFirstImagURLFromMD(fileContent, route))
@@ -81,19 +79,18 @@ export async function getArticleMeta(filepath: string, route: string, timeZone =
81
79
  }
82
80
  return meta as Theme.PageMeta
83
81
  }
82
+
84
83
  export async function getArticles(cfg: Partial<Theme.BlogConfig>, vpConfig: SiteConfig) {
85
- const srcDir
86
- = cfg?.srcDir || vpConfig.srcDir.replace(vpConfig.root, '').replace(/^\//, '')
87
- || process.argv.slice(2)?.[1]
88
- || '.'
89
- const files = glob.sync(`${srcDir}/**/*.md`, { ignore: ['node_modules'], absolute: true })
84
+ const pages = getVitePressPages(vpConfig)
85
+ const metaResults = pages.reduce((prev, value) => {
86
+ const { page, route, originRoute, filepath, isDynamic, dynamicRoute } = value
90
87
 
91
- const metaResults = files.reduce((prev, curr) => {
92
- const route = getPageRoute(curr, vpConfig.srcDir)
93
- const metaPromise = getArticleMeta(curr, route, cfg?.timeZone)
88
+ const metaPromise = (isDynamic && dynamicRoute)
89
+ ? getArticleMeta(filepath, originRoute, cfg?.timeZone, renderDynamicMarkdown(filepath, dynamicRoute.params, dynamicRoute.content))
90
+ : getArticleMeta(filepath, originRoute, cfg?.timeZone)
94
91
 
95
92
  // 提前获取,有缓存取缓存
96
- prev[curr] = {
93
+ prev[page] = {
97
94
  route,
98
95
  metaPromise
99
96
  }
@@ -105,8 +102,8 @@ export async function getArticles(cfg: Partial<Theme.BlogConfig>, vpConfig: Site
105
102
 
106
103
  const pageData: Theme.PageData[] = []
107
104
 
108
- for (const file of files) {
109
- const { route, metaPromise } = metaResults[file]
105
+ for (const page of pages) {
106
+ const { route, metaPromise } = metaResults[page.page]
110
107
  const meta = await metaPromise
111
108
  if (meta.layout === 'home') {
112
109
  continue
@@ -142,5 +139,5 @@ export function patchVPThemeConfig(
142
139
  }
143
140
 
144
141
  export function checkConfig(cfg?: Partial<Theme.BlogConfig>) {
145
- // TODO:保留
142
+ // 保留
146
143
  }
@@ -1,6 +1,3 @@
1
- import path from 'node:path'
2
- import { existsSync, readFileSync } from 'node:fs'
3
- import { Buffer } from 'node:buffer'
4
1
  import type { HeadConfig, SiteConfig } from 'vitepress'
5
2
  import {
6
3
  pagefindPlugin
@@ -9,6 +6,7 @@ import { RssPlugin } from 'vitepress-plugin-rss'
9
6
  import type { PluginOption } from 'vite'
10
7
  import { joinPath } from '@sugarat/theme-shared'
11
8
  import { AnnouncementPlugin } from 'vitepress-plugin-announcement'
9
+ import { groupIconVitePlugin } from 'vitepress-plugin-group-icons'
12
10
  import type { Theme } from '../../composables/config/index'
13
11
  import { _require } from './mdPlugins'
14
12
  import { themeReloadPlugin } from './hot-reload-plugin'
@@ -17,7 +15,7 @@ import { getArticles } from './theme'
17
15
  export function getVitePlugins(cfg: Partial<Theme.BlogConfig> = {}) {
18
16
  const plugins: any[] = []
19
17
 
20
- // 处理cover image的路径(暂只支持自动识别的文章首图)
18
+ // 处理 cover image 的路径(暂只支持自动识别的文章首图)
21
19
  plugins.push(coverImgTransform())
22
20
 
23
21
  // 处理自定义主题色
@@ -27,10 +25,10 @@ export function getVitePlugins(cfg: Partial<Theme.BlogConfig> = {}) {
27
25
  // 自动重载首页
28
26
  plugins.push(themeReloadPlugin())
29
27
 
30
- // 主题pageData生成
28
+ // 主题 pageData生成
31
29
  plugins.push(providePageData(cfg))
32
30
 
33
- // 内置简化版的pagefind
31
+ // 内置 pagefind
34
32
  if (cfg && cfg.search !== false) {
35
33
  const ops = cfg.search instanceof Object ? cfg.search : {}
36
34
  plugins.push(
@@ -40,7 +38,7 @@ export function getVitePlugins(cfg: Partial<Theme.BlogConfig> = {}) {
40
38
  )
41
39
  }
42
40
 
43
- // 内置支持Mermaid
41
+ // 内置支持 Markdown 流程图 Mermaid
44
42
  if (cfg?.mermaid !== false) {
45
43
  const { MermaidPlugin } = _require('vitepress-plugin-mermaid')
46
44
  plugins.push(inlineInjectMermaidClient())
@@ -52,9 +50,15 @@ export function getVitePlugins(cfg: Partial<Theme.BlogConfig> = {}) {
52
50
  ;[cfg?.RSS].flat().forEach(rssConfig => plugins.push(RssPlugin(rssConfig)))
53
51
  }
54
52
 
53
+ // 内置支持 全局公告
55
54
  if (cfg?.popover) {
56
55
  plugins.push(AnnouncementPlugin(cfg.popover))
57
56
  }
57
+
58
+ // 内置支持 group icon
59
+
60
+ plugins.push(groupIconVitePlugin(cfg?.groupIcon))
61
+
58
62
  return plugins
59
63
  }
60
64
 
@@ -106,58 +110,140 @@ export function inlineBuildEndPlugin(buildEndFn: any[]) {
106
110
  }
107
111
  }
108
112
 
109
- // TODO: 支持frontmatter中的相对路径图片自动处理
113
+ // 支持frontmatter中的相对路径图片自动处理
110
114
  export function coverImgTransform() {
111
115
  let blogConfig: Theme.BlogConfig
112
116
  let vitepressConfig: SiteConfig
113
117
  let assetsDir: string
118
+
119
+ const relativeMetaName: (keyof Theme.PageMeta)[] = ['cover']
120
+ const relativeMeta: Theme.PageMeta[] = []
121
+ const relativeMetaMap: Record<string, Theme.PageMeta> = {}
122
+ const viteAssetsMap: Record<string, string> = {}
123
+ const relativePathMap: Record<string, string> = {}
114
124
  return {
115
125
  name: '@sugarat/theme-plugin-cover-transform',
116
126
  apply: 'build',
117
- enforce: 'pre',
127
+ // enforce: 'pre',
118
128
  configResolved(config: any) {
119
129
  vitepressConfig = config.vitepress
120
130
  assetsDir = vitepressConfig.assetsDir
121
131
  blogConfig = config.vitepress.site.themeConfig.blog
132
+
133
+ const pagesData = [...blogConfig.pagesData]
134
+ // 兼容国际化
135
+ if (vitepressConfig.site.locales && Object.keys(vitepressConfig.site.locales).length > 1 && blogConfig?.locales) {
136
+ Object.values(blogConfig?.locales).map(v => v.pagesData)
137
+ .filter(v => !!v)
138
+ .forEach(v => pagesData.push(...v))
139
+ }
140
+ // 提取所有相对路径的属性
141
+ pagesData.forEach((v) => {
142
+ relativeMetaName.forEach((k) => {
143
+ const value = v.meta[k]
144
+ if (value && typeof value === 'string' && value.startsWith('/')) {
145
+ const absolutePath = `${vitepressConfig.srcDir}${value}`
146
+
147
+ // 复用已经映射后的值
148
+ if (relativeMetaMap[absolutePath]) {
149
+ Object.assign(v.meta, { [k]: relativeMetaMap[absolutePath][k] })
150
+ return
151
+ }
152
+
153
+ relativePathMap[value] = absolutePath
154
+ relativePathMap[absolutePath] = value
155
+ relativeMeta.push(v.meta)
156
+ relativeMetaMap[absolutePath] = v.meta
157
+ }
158
+ })
159
+ })
122
160
  },
123
- async generateBundle(_: any, bundle: Record<string, any>) {
161
+ moduleParsed(info) {
162
+ if (!relativePathMap[info.id]) {
163
+ return
164
+ }
165
+ const asset = info.code?.match(/export default "(.*)"/)?.[1]
166
+ if (!asset) {
167
+ return
168
+ }
169
+
170
+ viteAssetsMap[info.id] = asset
171
+ viteAssetsMap[asset] = info.id
172
+
173
+ // 换成 ViteAssets,影响输出 HTML
174
+ relativeMeta.forEach((meta) => {
175
+ relativeMetaName.forEach((k) => {
176
+ const value = meta[k]
177
+ if (!value || !relativePathMap[value as string]) {
178
+ return
179
+ }
180
+ const viteAsset = viteAssetsMap[relativePathMap[value as string]]
181
+ if (viteAsset) {
182
+ Object.assign(meta, { [k]: viteAsset })
183
+ }
184
+ })
185
+ })
186
+ },
187
+ generateBundle(_: any, bundle: Record<string, any>) {
188
+ // 换成 最终输出路径,影响 CSR 内容
124
189
  const assetsMap = Object.entries(bundle).filter(([key]) => {
125
190
  return key.startsWith(assetsDir)
126
191
  }).map(([_, value]) => {
127
192
  return value
128
- })
129
- for (const page of blogConfig.pagesData) {
130
- const { cover } = page.meta
131
- // 是否相对路径引用
132
- if (!cover?.startsWith?.('/')) {
133
- continue
134
- }
135
- try {
136
- // 寻找构建后的
137
- const realPath = path.join(vitepressConfig.root, cover)
138
- if (!existsSync(realPath)) {
139
- continue
193
+ }).filter(v => v.type === 'asset')
194
+
195
+ if (!assetsMap.length) {
196
+ return
197
+ }
198
+
199
+ relativeMeta.forEach((meta) => {
200
+ relativeMetaName.forEach((k) => {
201
+ const value = meta[k]
202
+ if (!value || !viteAssetsMap[value as string]) {
203
+ return
140
204
  }
141
- const fileBuffer = readFileSync(realPath)
142
- const matchAsset = assetsMap.find(v => Buffer.compare(fileBuffer, v.source) === 0)
205
+ const absolutePath = viteAssetsMap[value as string]
206
+ const matchAsset = assetsMap.find(v => joinPath(`${vitepressConfig.srcDir}/`, v.originalFileName) === absolutePath)
143
207
  if (matchAsset) {
144
- page.meta.cover = joinPath('/', matchAsset.fileName)
208
+ Object.assign(meta, { [k]: joinPath('/', matchAsset.fileName) })
145
209
  }
146
- }
147
- catch (e: any) {
148
- vitepressConfig.logger.warn(e?.message)
149
- }
150
- }
210
+ })
211
+ })
151
212
  }
152
- }
213
+ } as PluginOption
153
214
  }
154
215
 
155
216
  export function providePageData(cfg: Partial<Theme.BlogConfig>) {
156
217
  return {
157
218
  name: '@sugarat/theme-plugin-provide-page-data',
158
- async config(config: any) {
159
- const pagesData = await getArticles(cfg, config.vitepress)
160
- config.vitepress.site.themeConfig.blog.pagesData = pagesData
219
+ async config(config: any, env) {
220
+ const vitepressConfig: SiteConfig = config.vitepress
221
+ const pagesData = await getArticles(cfg, vitepressConfig)
222
+ if (vitepressConfig.site.locales && Object.keys(vitepressConfig.site.locales).length > 1) {
223
+ if (!vitepressConfig.site.themeConfig.blog.locales) {
224
+ vitepressConfig.site.themeConfig.blog.locales = {}
225
+ }
226
+ // 兼容国际化
227
+ const localeKeys = Object.keys(vitepressConfig.site.locales)
228
+ localeKeys.forEach((localeKey) => {
229
+ if (!vitepressConfig.site.themeConfig.blog.locales[localeKey]) {
230
+ vitepressConfig.site.themeConfig.blog.locales[localeKey] = {}
231
+ }
232
+
233
+ vitepressConfig.site.themeConfig.blog.locales[localeKey].pagesData = pagesData.filter((v) => {
234
+ const { route } = v
235
+ const isRoot = localeKey === 'root'
236
+ if (isRoot) {
237
+ return !localeKeys.filter(v => v !== 'root').some(k => route.startsWith(`/${k}`))
238
+ }
239
+ return route.startsWith(`/${localeKey}`)
240
+ })
241
+ })
242
+ if (env.mode === 'production') {
243
+ return
244
+ }
245
+ }
246
+ vitepressConfig.site.themeConfig.blog.pagesData = pagesData
161
247
  },
162
248
  } as PluginOption
163
249
  }