From 960b2ca65ac9fd2a1b7b8c26eb67ff6e900b8383 Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Thu, 31 Oct 2024 04:24:58 +0800 Subject: [PATCH] refactor(theme): improve node extends page --- theme/src/client/index.ts | 2 - theme/src/node/pages/autoCategory.ts | 48 +++++++ theme/src/node/pages/createPages.ts | 63 +++++++++ theme/src/node/pages/extendsPage.ts | 57 ++++++++ theme/src/node/pages/index.ts | 2 + theme/src/node/pages/pageBulletin.ts | 29 ++++ theme/src/node/setupPages.ts | 196 --------------------------- theme/src/node/theme.ts | 11 +- 8 files changed, 203 insertions(+), 205 deletions(-) create mode 100644 theme/src/node/pages/autoCategory.ts create mode 100644 theme/src/node/pages/createPages.ts create mode 100644 theme/src/node/pages/extendsPage.ts create mode 100644 theme/src/node/pages/index.ts create mode 100644 theme/src/node/pages/pageBulletin.ts delete mode 100644 theme/src/node/setupPages.ts diff --git a/theme/src/client/index.ts b/theme/src/client/index.ts index 6738aeb4..6e994684 100644 --- a/theme/src/client/index.ts +++ b/theme/src/client/index.ts @@ -2,7 +2,6 @@ export * from '../shared/index.js' export { default as VPBadge } from './components/global/VPBadge.vue' export { default as VPCard } from './components/global/VPCard.vue' - export { default as VPCardGrid } from './components/global/VPCardGrid.vue' export { default as VPHomeBanner } from './components/Home/VPHomeBanner.vue' export { default as VPHomeBox } from './components/Home/VPHomeBox.vue' @@ -10,7 +9,6 @@ export { default as VPHomeCustom } from './components/Home/VPHomeCustom.vue' export { default as VPHomeFeatures } from './components/Home/VPHomeFeatures.vue' export { default as VPHomeHero } from './components/Home/VPHomeHero.vue' export { default as VPHomeProfile } from './components/Home/VPHomeProfile.vue' - export { default as VPHomeTextImage } from './components/Home/VPHomeTextImage.vue' export { default as VPButton } from './components/VPButton.vue' export { default as VPImage } from './components/VPImage.vue' diff --git a/theme/src/node/pages/autoCategory.ts b/theme/src/node/pages/autoCategory.ts new file mode 100644 index 00000000..30384828 --- /dev/null +++ b/theme/src/node/pages/autoCategory.ts @@ -0,0 +1,48 @@ +import type { Page } from 'vuepress/core' +import type { PageCategoryData, PlumeThemeLocaleOptions, PlumeThemePageData } from '../../shared/index.js' +import { ensureLeadingSlash } from '@vuepress/helper' +import { resolveNotesLinkList } from '../config/index.js' +import { hash } from '../utils/index.js' + +let uuid = 10000 +const cache: Record = {} + +const RE_CATEGORY = /^(?:(\d+)\.)?([\s\S]+)$/ +let LOCALE_RE: RegExp + +export function autoCategory( + page: Page, + options: PlumeThemeLocaleOptions, +) { + const pagePath = page.filePathRelative + + if (page.data.type || !pagePath) + return + const notesLinks = resolveNotesLinkList(options) + + if (notesLinks.some(link => page.path.startsWith(link))) + return + + LOCALE_RE ??= new RegExp( + `^(${Object.keys(options.locales || {}).filter(l => l !== '/').join('|')})`, + ) + const list = ensureLeadingSlash(pagePath) + .replace(LOCALE_RE, '') + .replace(/^\//, '') + .split('/') + .slice(0, -1) + + const categoryList: PageCategoryData[] = list + .map((category, index) => { + const match = category.match(RE_CATEGORY) || [] + if (!cache[match[2]] && !match[1]) { + cache[match[2]] = uuid++ + } + return { + id: hash(list.slice(0, index + 1).join('-')).slice(0, 6), + sort: Number(match[1] || cache[match[2]]), + name: match[2], + } + }) + page.data.categoryList = categoryList +} diff --git a/theme/src/node/pages/createPages.ts b/theme/src/node/pages/createPages.ts new file mode 100644 index 00000000..eb9c189b --- /dev/null +++ b/theme/src/node/pages/createPages.ts @@ -0,0 +1,63 @@ +import type { App, Page } from 'vuepress/core' +import type { PlumeThemeLocaleOptions } from '../../shared/index.js' +import { getRootLang, getRootLangPath } from '@vuepress/helper' +import { createPage } from 'vuepress/core' +import { PRESET_LOCALES } from '../locales/index.js' +import { withBase } from '../utils/index.js' + +export async function createPages(app: App, localeOption: PlumeThemeLocaleOptions) { + if (localeOption.blog === false) + return + + const pageList: Promise[] = [] + const locales = localeOption.locales || {} + const rootPath = getRootLangPath(app) + const rootLang = getRootLang(app) + + const blog = localeOption.blog || {} + const link = blog.link || '/blog/' + + const getTitle = (locale: string, key: string) => { + const opt = PRESET_LOCALES[locale] || PRESET_LOCALES[rootPath] || {} + return opt[key] || '' + } + + for (const localePath of Object.keys(locales)) { + const lang = app.siteData.locales?.[localePath]?.lang || rootLang + const locale = localePath === '/' ? rootPath : localePath + + // 添加 博客页面 + if (blog.postList !== false) { + pageList.push(createPage(app, { + path: withBase(link, localePath), + frontmatter: { lang, _pageLayout: 'blog', title: getTitle(locale, 'blog') }, + })) + } + + // 添加 标签页 + if (blog.tags !== false) { + pageList.push(createPage(app, { + path: withBase(blog.tagsLink || `${link}/tags/`, localePath), + frontmatter: { lang, _pageLayout: 'blog-tags', title: getTitle(locale, 'tag') }, + })) + } + + // 添加归档页 + if (blog.archives !== false) { + pageList.push(createPage(app, { + path: withBase(blog.archivesLink || `${link}/archives/`, localePath), + frontmatter: { lang, _pageLayout: 'blog-archives', title: getTitle(locale, 'archive') }, + })) + } + + // 添加分类页 + if (blog.categories !== false) { + pageList.push(createPage(app, { + path: withBase(blog.categoriesLink || `${link}/categories/`, localePath), + frontmatter: { lang, _pageLayout: 'blog-categories', title: getTitle(locale, 'category') }, + })) + } + } + + app.pages.push(...await Promise.all(pageList)) +} diff --git a/theme/src/node/pages/extendsPage.ts b/theme/src/node/pages/extendsPage.ts new file mode 100644 index 00000000..8a5454cc --- /dev/null +++ b/theme/src/node/pages/extendsPage.ts @@ -0,0 +1,57 @@ +import type { Page } from 'vuepress/core' +import type { PlumeThemeLocaleOptions, PlumeThemePageData } from '../../shared/index.js' +import { autoCategory } from './autoCategory.js' +import { enableBulletin } from './pageBulletin.js' + +export function extendsPageData( + page: Page, + localeOptions: PlumeThemeLocaleOptions, +) { + cleanPageData(page) + autoCategory(page, localeOptions) + enableBulletin(page, localeOptions) +} + +function cleanPageData(page: Page) { + page.data.filePathRelative = page.filePathRelative + page.routeMeta.title = page.frontmatter.title || page.title + + if (page.frontmatter.icon) { + page.routeMeta.icon = page.frontmatter.icon + } + + if (page.frontmatter.home) { + page.frontmatter.pageLayout = 'home' + delete page.frontmatter.home + } + + if (page.frontmatter.article === false) { + page.frontmatter.draft = true + } + delete page.frontmatter.article + + if (page.frontmatter.friends) { + page.frontmatter.draft = true + page.data.type = 'friends' + page.permalink = page.permalink ?? '/friends/' + page.frontmatter.pageLayout = 'friends' + delete page.frontmatter.friends + } + + const pageType = page.frontmatter._pageLayout as string + if (pageType) { + page.frontmatter.draft = true + page.data.type = pageType as any + delete page.frontmatter._pageLayout + } + + if (page.frontmatter.pageLayout === 'blog') { + page.frontmatter.draft = true + page.data.type = 'blog' + } + + if ('externalLink' in page.frontmatter) { + page.frontmatter.externalLinkIcon = page.frontmatter.externalLink + delete page.frontmatter.externalLink + } +} diff --git a/theme/src/node/pages/index.ts b/theme/src/node/pages/index.ts new file mode 100644 index 00000000..5056db2a --- /dev/null +++ b/theme/src/node/pages/index.ts @@ -0,0 +1,2 @@ +export * from './createPages.js' +export * from './extendsPage.js' diff --git a/theme/src/node/pages/pageBulletin.ts b/theme/src/node/pages/pageBulletin.ts new file mode 100644 index 00000000..db57e925 --- /dev/null +++ b/theme/src/node/pages/pageBulletin.ts @@ -0,0 +1,29 @@ +import type { Page } from 'vuepress/core' +import type { BulletinOptions, PlumeThemeLocaleOptions, PlumeThemePageData } from '../../shared/index.js' +import { isPlainObject } from '@vuepress/helper' + +export function enableBulletin( + page: Page, + options: PlumeThemeLocaleOptions, +) { + let enablePage: BulletinOptions['enablePage'] + if (isPlainObject(options.bulletin) && options.bulletin.enablePage) { + enablePage = options.bulletin.enablePage + } + else if (options.locales) { + for (const locale of Object.keys(options.locales)) { + if (isPlainObject(options.locales[locale].bulletin) && options.locales[locale].bulletin.enablePage) { + enablePage = options.locales[locale].bulletin.enablePage + break + } + } + } + + if (typeof enablePage === 'function') { + page.data.bulletin = enablePage(page) ?? true + } + + else { + page.data.bulletin = enablePage ?? !!options.bulletin + } +} diff --git a/theme/src/node/setupPages.ts b/theme/src/node/setupPages.ts deleted file mode 100644 index 0bb969f9..00000000 --- a/theme/src/node/setupPages.ts +++ /dev/null @@ -1,196 +0,0 @@ -import type { App, Page } from 'vuepress/core' -import type { - BulletinOptions, - PageCategoryData, - PlumeThemeLocaleOptions, - PlumeThemePageData, -} from '../shared/index.js' -import { - ensureLeadingSlash, - getRootLang, - getRootLangPath, - isPlainObject, -} from '@vuepress/helper' -import { createPage } from 'vuepress/core' -import { resolveNotesLinkList } from './config/index.js' -import { PRESET_LOCALES } from './locales/index.js' -import { hash, withBase } from './utils/index.js' - -export async function setupPage( - app: App, - localeOption: PlumeThemeLocaleOptions, -) { - if (localeOption.blog === false) - return - - const pageList: Promise[] = [] - const locales = localeOption.locales || {} - const rootPath = getRootLangPath(app) - const rootLang = getRootLang(app) - - const blog = localeOption.blog || {} - const link = blog.link || '/blog/' - - const getTitle = (locale: string, key: string) => { - const opt = PRESET_LOCALES[locale] || PRESET_LOCALES[rootPath] || {} - return opt[key] || '' - } - - for (const localePath of Object.keys(locales)) { - const lang = app.siteData.locales?.[localePath]?.lang || rootLang - const locale = localePath === '/' ? rootPath : localePath - - // 添加 博客页面 - if (blog.postList !== false) { - pageList.push(createPage(app, { - path: withBase(link, localePath), - frontmatter: { lang, _pageLayout: 'blog', title: getTitle(locale, 'blog') }, - })) - } - - // 添加 标签页 - if (blog.tags !== false) { - pageList.push(createPage(app, { - path: withBase(blog.tagsLink || `${link}/tags/`, localePath), - frontmatter: { lang, _pageLayout: 'blog-tags', title: getTitle(locale, 'tag') }, - })) - } - - // 添加归档页 - if (blog.archives !== false) { - pageList.push(createPage(app, { - path: withBase(blog.archivesLink || `${link}/archives/`, localePath), - frontmatter: { lang, _pageLayout: 'blog-archives', title: getTitle(locale, 'archive') }, - })) - } - - // 添加分类页 - if (blog.categories !== false) { - pageList.push(createPage(app, { - path: withBase(blog.categoriesLink || `${link}/categories/`, localePath), - frontmatter: { lang, _pageLayout: 'blog-categories', title: getTitle(locale, 'category') }, - })) - } - } - - app.pages.push(...await Promise.all(pageList)) -} - -export function extendsPageData( - page: Page, - localeOptions: PlumeThemeLocaleOptions, -) { - page.data.filePathRelative = page.filePathRelative - page.routeMeta.title = page.frontmatter.title || page.title - - if (page.frontmatter.icon) { - page.routeMeta.icon = page.frontmatter.icon - } - - if (page.frontmatter.home) { - page.frontmatter.pageLayout = 'home' - delete page.frontmatter.home - } - - if (page.frontmatter.article === false) { - page.frontmatter.draft = true - } - delete page.frontmatter.article - - if (page.frontmatter.friends) { - page.frontmatter.draft = true - page.data.type = 'friends' - page.permalink = page.permalink ?? '/friends/' - page.frontmatter.pageLayout = 'friends' - delete page.frontmatter.friends - } - - const pageType = page.frontmatter._pageLayout as string - if (pageType) { - page.frontmatter.draft = true - page.data.type = pageType as any - delete page.frontmatter._pageLayout - } - - if (page.frontmatter.pageLayout === 'blog') { - page.frontmatter.draft = true - page.data.type = 'blog' - } - - if ('externalLink' in page.frontmatter) { - page.frontmatter.externalLinkIcon = page.frontmatter.externalLink - delete page.frontmatter.externalLink - } - - autoCategory(page, localeOptions) - enableBulletin(page, localeOptions) -} - -let uuid = 10000 -const cache: Record = {} - -const RE_CATEGORY = /^(?:(\d+)\.)?([\s\S]+)$/ -let LOCALE_RE: RegExp - -export function autoCategory( - page: Page, - options: PlumeThemeLocaleOptions, -) { - const pagePath = page.filePathRelative - - if (page.data.type || !pagePath) - return - const notesLinks = resolveNotesLinkList(options) - - if (notesLinks.some(link => page.path.startsWith(link))) - return - - LOCALE_RE ??= new RegExp( - `^(${Object.keys(options.locales || {}).filter(l => l !== '/').join('|')})`, - ) - const list = ensureLeadingSlash(pagePath) - .replace(LOCALE_RE, '') - .replace(/^\//, '') - .split('/') - .slice(0, -1) - - const categoryList: PageCategoryData[] = list - .map((category, index) => { - const match = category.match(RE_CATEGORY) || [] - if (!cache[match[2]] && !match[1]) { - cache[match[2]] = uuid++ - } - return { - id: hash(list.slice(0, index + 1).join('-')).slice(0, 6), - sort: Number(match[1] || cache[match[2]]), - name: match[2], - } - }) - page.data.categoryList = categoryList -} - -function enableBulletin( - page: Page, - options: PlumeThemeLocaleOptions, -) { - let enablePage: BulletinOptions['enablePage'] - if (isPlainObject(options.bulletin) && options.bulletin.enablePage) { - enablePage = options.bulletin.enablePage - } - else if (options.locales) { - for (const locale of Object.keys(options.locales)) { - if (isPlainObject(options.locales[locale].bulletin) && options.locales[locale].bulletin.enablePage) { - enablePage = options.locales[locale].bulletin.enablePage - break - } - } - } - - if (typeof enablePage === 'function') { - page.data.bulletin = enablePage(page) ?? true - } - - else { - page.data.bulletin = enablePage ?? !!options.bulletin - } -} diff --git a/theme/src/node/theme.ts b/theme/src/node/theme.ts index 16130a20..e9d16023 100644 --- a/theme/src/node/theme.ts +++ b/theme/src/node/theme.ts @@ -4,10 +4,10 @@ import { sleep } from '@pengzhanbo/utils' import { generateAutoFrontmatter, initAutoFrontmatter, watchAutoFrontmatter } from './autoFrontmatter/index.js' import { extendsBundlerOptions, resolveAlias, resolveProvideData, resolveThemeOptions, templateBuildRenderer } from './config/index.js' import { getThemeConfig, initConfigLoader, waitForConfigLoaded, watchConfigFile } from './loadConfig/index.js' +import { createPages, extendsPageData } from './pages/index.js' import { getPlugins } from './plugins/index.js' import { prepareData, watchPrepare } from './prepare/index.js' import { prepareThemeData } from './prepare/prepareThemeData.js' -import { extendsPageData, setupPage } from './setupPages.js' import { resolve, templates, THEME_NAME } from './utils/index.js' export function plumeTheme(options: PlumeThemeOptions = {}): Theme { @@ -52,18 +52,15 @@ export function plumeTheme(options: PlumeThemeOptions = {}): Theme { }, extendsPage: async (page) => { - const { localeOptions } = getThemeConfig() - extendsPageData(page as Page, localeOptions) + extendsPageData(page as Page, getThemeConfig().localeOptions) }, onInitialized: async (app) => { - const { localeOptions } = getThemeConfig() - await setupPage(app, localeOptions) + await createPages(app, getThemeConfig().localeOptions) }, onPrepared: async (app) => { - const { localeOptions } = getThemeConfig() - await prepareThemeData(app, localeOptions, pluginOptions) + await prepareThemeData(app, getThemeConfig().localeOptions, pluginOptions) await prepareData(app) },