From 5ff9570018d36b4017038cc3419951cfc6481aed Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Fri, 5 Jan 2024 01:37:45 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=BC=BA=E5=A4=9A=E8=AF=AD?= =?UTF-8?q?=E8=A8=80=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- theme/src/client/composables/langs.ts | 29 +++++-- theme/src/node/autoFrontmatter.ts | 28 +++--- theme/src/node/plugins.ts | 113 ++++++++++--------------- theme/src/node/resolveLocaleOptions.ts | 26 ++++++ theme/src/node/resolveNotesList.ts | 15 ++++ theme/src/node/setupPages.ts | 5 +- 6 files changed, 128 insertions(+), 88 deletions(-) create mode 100644 theme/src/node/resolveLocaleOptions.ts create mode 100644 theme/src/node/resolveNotesList.ts diff --git a/theme/src/client/composables/langs.ts b/theme/src/client/composables/langs.ts index 5e6182d8..b1df741c 100644 --- a/theme/src/client/composables/langs.ts +++ b/theme/src/client/composables/langs.ts @@ -3,6 +3,7 @@ import { computed } from 'vue' import type { PlumeThemePageData } from '../../shared/index.js' import { ensureStartingSlash } from '../utils/index.js' import { useThemeData } from './themeData.js' +import { normalizePath } from './sidebar.js' export function useLangs({ removeCurrent = true, @@ -20,18 +21,34 @@ export function useLangs({ } }) + const addPath = computed(() => { + if (page.value.frontmatter.home || (page.value.type && page.value.type !== 'friends')) + return true + + return correspondingLink + }) + + const getBlogLink = (locale: string) => { + const blog = theme.value.locales?.[`/${locale}/`]?.blog + const defaultBlog = theme.value.locales?.['/']?.blog ?? theme.value.blog + const link = blog?.link ? blog.link : normalizePath(`${locale}${defaultBlog?.link || 'blog/'}`) + return link + } + const localeLinks = computed(() => Object.entries(theme.value.locales || {}).flatMap(([key, value]) => removeCurrent && currentLang.value.label === value.selectLanguageName ? [] : { text: value.selectLanguageName, - link: normalizeLink( - key, - correspondingLink, - page.value.path.slice(currentLang.value.link.length - 1), - true, - ), + link: page.value.isBlogPost + ? getBlogLink(key) + : normalizeLink( + key, + addPath.value, + page.value.path.slice(currentLang.value.link.length - 1), + true, + ), }, ), ) diff --git a/theme/src/node/autoFrontmatter.ts b/theme/src/node/autoFrontmatter.ts index b042c525..28cc2532 100644 --- a/theme/src/node/autoFrontmatter.ts +++ b/theme/src/node/autoFrontmatter.ts @@ -13,6 +13,8 @@ import type { PlumeThemePluginOptions, } from '../shared/index.js' import { getCurrentDirname, getPackage, nanoid, pathJoin } from './utils.js' +import { resolveNotesList } from './resolveNotesList.js' +import { resolveLocaleOptions } from './resolveLocaleOptions.js' export default function autoFrontmatter( app: App, @@ -21,18 +23,12 @@ export default function autoFrontmatter( ): AutoFrontmatterOptions { const sourceDir = app.dir.source() const pkg = getPackage() - const { locales = {}, avatar, article: articlePrefix = '/article/' } = localeOption + const { locales = {}, article: articlePrefix = '/article/' } = localeOption const { frontmatter } = options - - const localesNotesDirs = Object.keys(app.siteData.locales || {}) - .map((locale) => { - // fixed: #15 - const notes = locales[locale]?.notes - if (!notes) - return '' - - return notes.dir ? pathJoin(locale, notes.dir).replace(/^\//, '') : '' - }) + const avatar = resolveLocaleOptions(localeOption, 'avatar') + const notesList = resolveNotesList(localeOption) + const localesNotesDirs = notesList + .map(notes => notes.dir?.replace(/^\//, '')) .filter(Boolean) const baseFrontmatter: FrontmatterObject = { @@ -58,7 +54,7 @@ export default function autoFrontmatter( return resolveLocalePath(localeOption.locales!, file) } const notesByLocale = (locale: string) => { - const notes = locales[locale]?.notes || localeOption.notes + const notes = resolveLocaleOptions(localeOption, 'notes', locale) if (notes === false) return undefined return notes @@ -162,7 +158,13 @@ export default function autoFrontmatter( if (permalink) return permalink const locale = resolveLocale(filepath) - return pathJoin(locale, articlePrefix, nanoid(), '/') + const prefix = resolveLocaleOptions(localeOption, 'article', locale, false) + const args: string[] = [] + prefix + ? args.push(prefix) + : args.push(locale, articlePrefix) + + return pathJoin(...args, nanoid(), '/') }, }, }, diff --git a/theme/src/node/plugins.ts b/theme/src/node/plugins.ts index e30d7311..8c3e7c94 100644 --- a/theme/src/node/plugins.ts +++ b/theme/src/node/plugins.ts @@ -1,4 +1,3 @@ -import path from 'node:path' import type { App, PluginConfig } from '@vuepress/core' import { activeHeaderLinksPlugin } from '@vuepress/plugin-active-header-links' import { docsearchPlugin } from '@vuepress/plugin-docsearch' @@ -28,62 +27,56 @@ import type { PlumeThemePluginOptions, } from '../shared/index.js' import autoFrontmatter from './autoFrontmatter.js' +import { resolveLocaleOptions } from './resolveLocaleOptions.js' +import { pathJoin } from './utils.js' +import { resolveNotesList } from './resolveNotesList.js' -export function setupPlugins(app: App, options: PlumeThemePluginOptions, localeOptions: PlumeThemeLocaleOptions): PluginConfig { +export function setupPlugins( + app: App, + options: PlumeThemePluginOptions, + localeOptions: PlumeThemeLocaleOptions, +): PluginConfig { const isProd = !app.env.isDev - const locales = (localeOptions.locales || {}) as PlumeThemeLocaleOptions - const localeNotesDirs = Object.keys(locales) - .map((locale) => { - const dir = locales[locale].notes?.dir || '' - return dir - ? path.join(locale, dir, '**').replace(/\\+/g, '/').replace(/^\//, '') - : '' - }) + const notesList = resolveNotesList(localeOptions) + const notesDirList = notesList + .map(notes => notes.dir && pathJoin(notes.dir, '**').replace(/^\//, '')) .filter(Boolean) + const blog = resolveLocaleOptions(localeOptions, 'blog') + const plugins: PluginConfig = [ palettePlugin({ preset: 'sass' }), - themeDataPlugin({ - themeData: { - ...localeOptions, - notes: localeOptions.notes - ? { dir: localeOptions.notes.dir, link: localeOptions.notes.link } - : undefined, - } as any, - }), + themeDataPlugin({ themeData: localeOptions }), autoFrontmatterPlugin(autoFrontmatter(app, options, localeOptions)), blogDataPlugin({ - include: localeOptions.blog?.include ?? ['**/*.md'], + include: blog?.include ?? ['**/*.md'], exclude: [ '**/{README,readme,index}.md', '.vuepress/', 'node_modules/', - ...(localeOptions.blog?.exclude ?? []), - ...localeNotesDirs, + ...(blog?.exclude ?? []), + ...notesDirList, ].filter(Boolean), sortBy: 'createTime', excerpt: true, - pageFilter(page: any) { - if (page.frontmatter.article !== undefined) - return !!page.frontmatter.article - - return true - }, - extendBlogData(page: any) { - return { - categoryList: page.data.categoryList, - tags: page.frontmatter.tags, - sticky: page.frontmatter.sticky, - createTime: page.data.frontmatter.createTime, - lang: page.lang, - } - }, + pageFilter: (page: any) => page.frontmatter.article !== undefined + ? !!page.frontmatter.article + : true, + extendBlogData: (page: any) => ({ + categoryList: page.data.categoryList, + tags: page.frontmatter.tags, + sticky: page.frontmatter.sticky, + createTime: page.data.frontmatter.createTime, + lang: page.lang, + }), }), + notesDataPlugin(notesList), + iconifyPlugin(), contentUpdatePlugin(), @@ -99,37 +92,27 @@ export function setupPlugins(app: App, options: PlumeThemePluginOptions, localeO if (options.readingTime !== false) plugins.push(readingTimePlugin(options.readingTime || {})) - if (localeOptions.notes) - plugins.push(notesDataPlugin(localeOptions.notes)) - if (options.nprogress !== false) plugins.push(nprogressPlugin()) if (options.git !== false) { plugins.push(gitPlugin({ createdTime: false, - updatedTime: localeOptions.lastUpdated !== false, - contributors: localeOptions.contributors !== false, + updatedTime: resolveLocaleOptions(localeOptions, 'lastUpdated') !== false, + contributors: resolveLocaleOptions(localeOptions, 'contributors') !== false, })) } if (options.mediumZoom !== false) { plugins.push(mediumZoomPlugin({ selector: '.plume-content > img, .plume-content :not(a) > img', - zoomOptions: { - background: 'var(--vp-c-bg)', - }, + zoomOptions: { background: 'var(--vp-c-bg)' }, delay: 300, })) } - if (options.caniuse !== false) { - plugins.push(caniusePlugin( - options.caniuse || { - mode: 'embed', - }, - )) - } + if (options.caniuse !== false) + plugins.push(caniusePlugin(options.caniuse || { mode: 'embed' })) if (options.externalLinkIcon !== false) { plugins.push(externalLinkIconPlugin({ @@ -150,14 +133,11 @@ export function setupPlugins(app: App, options: PlumeThemePluginOptions, localeO plugins.push(searchPlugin(options.search)) if (options.docsearch !== false && !options.search) { - if (options.docsearch?.appId && options.docsearch?.apiKey) { + if (options.docsearch?.appId && options.docsearch?.apiKey) plugins.push(docsearchPlugin(options.docsearch)) - } - else { - console.error( - 'docsearch plugin: appId and apiKey are both required', - ) - } + + else + console.error('docsearch plugin: appId and apiKey are both required') } if (options.shikiji !== false) { @@ -180,7 +160,6 @@ export function setupPlugins(app: App, options: PlumeThemePluginOptions, localeO { hint: true, // info note tip warning danger details codetabs: true, - tabs: true, align: true, mark: true, tasklist: true, @@ -188,6 +167,7 @@ export function setupPlugins(app: App, options: PlumeThemePluginOptions, localeO attrs: true, sup: true, sub: true, + katex: true, } as MarkdownEnhanceOptions, options.markdownEnhance || {}, ), @@ -200,16 +180,15 @@ export function setupPlugins(app: App, options: PlumeThemePluginOptions, localeO if (options.baiduTongji !== false && options.baiduTongji?.key) plugins.push(baiduTongjiPlugin(options.baiduTongji)) - if (options.sitemap !== false && localeOptions.hostname && isProd) { - plugins.push(sitemapPlugin({ - hostname: localeOptions.hostname, - })) - } + const hostname = resolveLocaleOptions(localeOptions, 'hostname') - if (options.seo !== false && localeOptions.hostname && isProd) { + if (options.sitemap !== false && hostname && isProd) + plugins.push(sitemapPlugin({ hostname })) + + if (options.seo !== false && hostname && isProd) { plugins.push(seoPlugin({ - hostname: localeOptions.hostname || '', - author: localeOptions.avatar?.name, + hostname, + author: resolveLocaleOptions(localeOptions, 'avatar')?.name, })) } diff --git a/theme/src/node/resolveLocaleOptions.ts b/theme/src/node/resolveLocaleOptions.ts new file mode 100644 index 00000000..ed21b3cb --- /dev/null +++ b/theme/src/node/resolveLocaleOptions.ts @@ -0,0 +1,26 @@ +import type { PlumeThemeLocaleOptions } from '../shared/index.js' +import { normalizePath } from './utils.js' + +export function resolveLocaleOptions< + T extends PlumeThemeLocaleOptions = PlumeThemeLocaleOptions, + K extends Exclude = Exclude, +>(options: T, key: K, locale = '', fallback = true): T[K] | undefined { + const locales = options.locales + + if (!locales) + return options[key] + + locale = !locale || locale === '/' ? '/' : normalizePath(`/${locale}/`) + + const localeOptions = locales[locale] + const fallbackLocaleOptions = locales['/'] + if (!localeOptions) + return fallback ? options[key] : undefined + + const _key = key as keyof typeof localeOptions + const fallbackData = (fallbackLocaleOptions[_key] ?? options[key]) as T[K] + + const value = localeOptions[_key] as T[K] + + return value ?? (fallback ? fallbackData : undefined) +} diff --git a/theme/src/node/resolveNotesList.ts b/theme/src/node/resolveNotesList.ts new file mode 100644 index 00000000..fa348af0 --- /dev/null +++ b/theme/src/node/resolveNotesList.ts @@ -0,0 +1,15 @@ +import type { NotesDataOptions } from '@vuepress-plume/plugin-notes-data' +import type { PlumeThemeLocaleOptions } from '../shared/index.js' +import { resolveLocaleOptions } from './resolveLocaleOptions.js' + +export function resolveNotesList(options: PlumeThemeLocaleOptions) { + const locales = options.locales || {} + const notesList: NotesDataOptions[] = [] + + for (const locale of Object.keys(locales)) { + const notes = resolveLocaleOptions(options, 'notes', locale, false) + notes && notesList.push(notes) + } + + return notesList +} diff --git a/theme/src/node/setupPages.ts b/theme/src/node/setupPages.ts index 45526f0a..12f61687 100644 --- a/theme/src/node/setupPages.ts +++ b/theme/src/node/setupPages.ts @@ -7,15 +7,16 @@ import type { PlumeThemePageData, } from '../shared/index.js' import { pathJoin } from './utils.js' +import { resolveLocaleOptions } from './resolveLocaleOptions.js' export async function setupPage( app: App, localeOption: PlumeThemeLocaleOptions, ) { const locales = Object.keys(app.siteData.locales || {}) + const defaultBlog = resolveLocaleOptions(localeOption, 'blog') for (const [, locale] of locales.entries()) { - const blog = localeOption.locales?.[locale]?.blog - const defaultBlog = localeOption.blog + const blog = resolveLocaleOptions(localeOption, 'blog', locale, false) const link = blog?.link ? blog.link : pathJoin('/', locale, defaultBlog?.link || '/blog/')