mirror of
https://github.com/pengzhanbo/vuepress-theme-plume.git
synced 2026-04-23 10:58:13 +08:00
feat(theme): add auto frontmatter config
This commit is contained in:
parent
016d092ee0
commit
c1587fd3cd
@ -11,7 +11,7 @@ import type {
|
||||
MarkdownFile,
|
||||
} from '../shared/index.js'
|
||||
import { readMarkdown, readMarkdownList } from './readFiles.js'
|
||||
import { ensureArray } from './utils.js'
|
||||
import { ensureArray, isEmptyObject } from './utils.js'
|
||||
|
||||
export const autoFrontmatterPlugin = ({
|
||||
include = ['**/*.{md,MD}'],
|
||||
@ -35,7 +35,7 @@ export const autoFrontmatterPlugin = ({
|
||||
.map(({ include, formatter }) => {
|
||||
return {
|
||||
include,
|
||||
filter: createFilter(ensureArray(include)),
|
||||
filter: createFilter(ensureArray(include), [], { resolve: false }),
|
||||
formatter,
|
||||
}
|
||||
})
|
||||
@ -43,22 +43,28 @@ export const autoFrontmatterPlugin = ({
|
||||
function formatMarkdown(file: MarkdownFile): void {
|
||||
const { filepath, relativePath } = file
|
||||
|
||||
const formatter =
|
||||
otherFormatters.find(({ filter }) => filter(relativePath))?.formatter ||
|
||||
globFormatter
|
||||
const current = otherFormatters.find(({ filter }) => filter(relativePath))
|
||||
const formatter = current?.formatter || globFormatter
|
||||
const { data, content } = grayMatter(file.content)
|
||||
|
||||
Object.keys(formatter).forEach((key) => {
|
||||
const value = formatter[key](data[key], data, file)
|
||||
data[key] = value ?? data[key]
|
||||
})
|
||||
const yaml = jsonToYaml
|
||||
.stringify(data)
|
||||
.replace(/\n\s{2}/g, '\n')
|
||||
.replace(/"/g, '')
|
||||
const newContent = `${yaml}---\n${content}`
|
||||
|
||||
fs.writeFileSync(filepath, newContent, 'utf-8')
|
||||
try {
|
||||
const yaml = isEmptyObject(data)
|
||||
? '---\n'
|
||||
: jsonToYaml
|
||||
.stringify(data)
|
||||
.replace(/\n\s{2}/g, '\n')
|
||||
.replace(/"/g, '')
|
||||
const newContent = `${yaml}---\n${content}`
|
||||
|
||||
fs.writeFileSync(filepath, newContent, 'utf-8')
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@ -3,3 +3,7 @@ export function ensureArray<T>(thing: T | T[] | null | undefined): T[] {
|
||||
if (thing === null || thing === undefined) return []
|
||||
return [thing]
|
||||
}
|
||||
|
||||
export function isEmptyObject(obj: object) {
|
||||
return Object.keys(obj).length === 0
|
||||
}
|
||||
|
||||
@ -69,6 +69,7 @@
|
||||
"@vueuse/core": "^9.10.0",
|
||||
"body-scroll-lock": "4.0.0-beta.0",
|
||||
"date-fns": "^2.29.3",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"nanoid": "^4.0.0",
|
||||
"ts-debounce": "^4.0.0",
|
||||
"vue": "^3.2.47",
|
||||
|
||||
@ -119,9 +119,9 @@ const actions = computed(() => {
|
||||
font-weight: 500;
|
||||
margin-top: 1.5rem;
|
||||
color: var(--vp-c-text-hero-text);
|
||||
padding: 6px 20px;
|
||||
/* padding: 6px 20px; */
|
||||
border-radius: 5px;
|
||||
background-color: rgba(0, 0, 0, 0.25);
|
||||
/* background-color: rgba(0, 0, 0, 0.25); */
|
||||
}
|
||||
|
||||
.actions {
|
||||
|
||||
@ -1,20 +1,93 @@
|
||||
import { createRequire } from 'node:module'
|
||||
import path from 'node:path'
|
||||
import type { AutoFrontmatterOptions } from '@vuepress-plume/vuepress-plugin-auto-frontmatter'
|
||||
import type {
|
||||
NotesDataOptions,
|
||||
NotesItem,
|
||||
} from '@vuepress-plume/vuepress-plugin-notes-data'
|
||||
import type { App } from '@vuepress/core'
|
||||
import { format } from 'date-fns'
|
||||
import { customAlphabet } from 'nanoid'
|
||||
import type { PlumeThemeLocaleOptions } from '../shared/index.js'
|
||||
|
||||
const nanoid = customAlphabet('0123456789abcdefghijklmnopqrstuvwxyz', 8)
|
||||
|
||||
export default function (
|
||||
app: App,
|
||||
localeOption: PlumeThemeLocaleOptions
|
||||
): AutoFrontmatterOptions {
|
||||
// const sourceDir = app.dir.source()
|
||||
const sourceDir = app.dir.source()
|
||||
const require = createRequire(process.cwd())
|
||||
const pkg = require('./package.json') || {}
|
||||
const articlePrefix = localeOption.article || '/article/'
|
||||
const {
|
||||
dir,
|
||||
link: notesLink,
|
||||
notes: notesList,
|
||||
} = localeOption.notes as NotesDataOptions
|
||||
const notesDir = dir.replace(/^\//, '')
|
||||
const baseFormatter = {
|
||||
author(author: string) {
|
||||
if (author) return author
|
||||
return localeOption.avatar?.name || pkg.author || ''
|
||||
},
|
||||
createTime(formatTime: string, _: any, { createTime }) {
|
||||
if (formatTime) return formatTime
|
||||
return format(new Date(createTime), 'yyyy/MM/dd hh:mm:ss')
|
||||
},
|
||||
}
|
||||
const findNote = (filepath: string) => {
|
||||
const file = path.relative(sourceDir, filepath)
|
||||
return notesList.find((note) =>
|
||||
file.startsWith(path.join(notesDir.replace(/^\//, ''), note.dir))
|
||||
)
|
||||
}
|
||||
|
||||
const getCurrentDirname = (note: NotesItem | undefined, filepath: string) => {
|
||||
const dirList =
|
||||
(note?.dir || path.dirname(filepath))
|
||||
.replace(/^\/|\/$/g, '')
|
||||
.split('/') || []
|
||||
return dirList.length > 0 ? dirList[dirList.length - 1] : ''
|
||||
}
|
||||
return {
|
||||
include: [],
|
||||
include: ['**/*.md'],
|
||||
formatter: [
|
||||
{
|
||||
// note 首页链接
|
||||
include: path.join(notesDir, `**/{readme,README,index}.md`),
|
||||
formatter: {
|
||||
...baseFormatter,
|
||||
title(title: string, _, { filepath }) {
|
||||
if (title) return title
|
||||
const note = findNote(filepath)
|
||||
if (note?.text) return note.text
|
||||
return getCurrentDirname(note, filepath) || ''
|
||||
},
|
||||
permalink(permalink: string, _, { filepath }) {
|
||||
if (permalink) return permalink
|
||||
const note = findNote(filepath)
|
||||
const dirname = getCurrentDirname(note, filepath)
|
||||
return path.join(notesLink, note?.link || dirname, '/')
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
include: path.join(notesDir, '**/*.md'),
|
||||
formatter: {
|
||||
title(title: string, _, { filepath }) {
|
||||
if (title) return title
|
||||
const basename = path.basename(filepath, '.md')
|
||||
return basename
|
||||
},
|
||||
permalink(permalink: string, _, { filepath }) {
|
||||
if (permalink) return permalink
|
||||
const note = findNote(filepath)
|
||||
const dirname = getCurrentDirname(note, filepath)
|
||||
return path.join(notesLink, note?.link || dirname, nanoid())
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
include: '**/{readme,README,index}.md',
|
||||
formatter: {},
|
||||
@ -22,21 +95,14 @@ export default function (
|
||||
{
|
||||
include: '*',
|
||||
formatter: {
|
||||
...baseFormatter,
|
||||
title(title: string) {
|
||||
if (title) return title
|
||||
return title
|
||||
},
|
||||
author(author: string) {
|
||||
if (author) return author
|
||||
return localeOption.avatar?.name || pkg.author || ''
|
||||
},
|
||||
createTime(formatTime: string, _, { createTime }) {
|
||||
if (formatTime) return formatTime
|
||||
return format(new Date(createTime), 'yyyy/MM/dd hh:mm:ss')
|
||||
},
|
||||
permalink(permalink: string, _, { filepath }) {
|
||||
permalink(permalink: string) {
|
||||
if (permalink) return permalink
|
||||
return permalink
|
||||
return path.join(articlePrefix, nanoid())
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
11
packages/theme/src/node/defaultOptions.ts
Normal file
11
packages/theme/src/node/defaultOptions.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import merge from 'lodash.merge'
|
||||
import type { PlumeThemeLocaleOptions } from '../shared/index.js'
|
||||
|
||||
export const defaultLocaleOption: Partial<PlumeThemeLocaleOptions> = {
|
||||
article: '/article',
|
||||
notes: { link: '/note', dir: 'notes', notes: [] },
|
||||
}
|
||||
|
||||
export const mergeLocaleOptions = (options: PlumeThemeLocaleOptions) => {
|
||||
return merge(defaultLocaleOption, options)
|
||||
}
|
||||
@ -33,6 +33,11 @@ export const setupPlugins = (
|
||||
): PluginConfig => {
|
||||
const isProd = !app.env.isDev
|
||||
|
||||
let notesDir: string | undefined
|
||||
if (localeOptions.notes !== false) {
|
||||
notesDir = localeOptions.notes?.dir
|
||||
}
|
||||
|
||||
return [
|
||||
palettePlugin({ preset: 'sass' }),
|
||||
themeDataPlugin({
|
||||
@ -46,7 +51,10 @@ export const setupPlugins = (
|
||||
autoFrontmatterPlugin(autoFrontmatter(app, localeOptions)),
|
||||
blogDataPlugin({
|
||||
include: ['**/*.md'],
|
||||
exclude: ['**/{README,index}.md', 'notes/**'],
|
||||
exclude: [
|
||||
'**/{README,index}.md',
|
||||
notesDir ? `${notesDir}/**` : '',
|
||||
].filter(Boolean),
|
||||
sortBy: 'createTime',
|
||||
excerpt: true,
|
||||
extendBlogData(page: any) {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import type { App, Page, Theme } from '@vuepress/core'
|
||||
import { fs, getDirname, path } from '@vuepress/utils'
|
||||
import type { PlumeThemeOptions, PlumeThemePageData } from '../shared/index.js'
|
||||
import { mergeLocaleOptions } from './defaultOptions.js'
|
||||
import { setupPlugins } from './plugins.js'
|
||||
import { autoCategory, pageContentRendered, setupPage } from './setupPages.js'
|
||||
|
||||
@ -10,6 +11,7 @@ export const plumeTheme = ({
|
||||
themePlugins = {},
|
||||
...localeOptions
|
||||
}: PlumeThemeOptions = {}): Theme => {
|
||||
localeOptions = mergeLocaleOptions(localeOptions)
|
||||
return (app: App) => {
|
||||
return {
|
||||
name: '@vuepress-plume/theme-plume',
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { NotesDataOptions } from '@vuepress-plume/vuepress-plugin-notes-data'
|
||||
import type { LocaleData } from '@vuepress/core'
|
||||
import type { NavItem, NavItemWithLink } from './navbar.js'
|
||||
import type { NavItem } from './navbar.js'
|
||||
// import type { NavbarConfig, NavLink } from '../layout/index.js'
|
||||
// import type { PlumeThemeNotesOptions } from './notes.js'
|
||||
|
||||
@ -85,14 +85,14 @@ export interface PlumeThemeLocaleData extends LocaleData {
|
||||
*
|
||||
* @def:{ text: '标签', link: '/tag/' }
|
||||
*/
|
||||
tag?: false | NavItemWithLink
|
||||
// tag?: false | NavItemWithLink
|
||||
|
||||
/**
|
||||
* 文章分类 与 navbar配置
|
||||
*
|
||||
* @default: { text: '分类', link: '/category/ }
|
||||
*/
|
||||
category?: false | NavItemWithLink
|
||||
// category?: false | NavItemWithLink
|
||||
|
||||
/**
|
||||
* 归档页 链接与 navbar 配置
|
||||
@ -101,7 +101,7 @@ export interface PlumeThemeLocaleData extends LocaleData {
|
||||
*
|
||||
* @default: { text: '归档', link: '/timeline/' }
|
||||
*/
|
||||
archive?: false | NavItemWithLink
|
||||
// archive?: false | NavItemWithLink
|
||||
|
||||
/**
|
||||
* 笔记配置, 笔记中的文章默认不会出现在首页文章列表
|
||||
@ -113,23 +113,23 @@ export interface PlumeThemeLocaleData extends LocaleData {
|
||||
/**
|
||||
* language text
|
||||
*/
|
||||
selectLanguageText?: string
|
||||
// selectLanguageText?: string
|
||||
/**
|
||||
* language aria label
|
||||
*/
|
||||
selectLanguageAriaLabel?: string
|
||||
// selectLanguageAriaLabel?: string
|
||||
/**
|
||||
* language name
|
||||
*/
|
||||
selectLanguageName?: string
|
||||
// selectLanguageName?: string
|
||||
/**
|
||||
* repository of navbar
|
||||
*/
|
||||
repo?: null | string
|
||||
// repo?: null | string
|
||||
/**
|
||||
* repository text of navbar
|
||||
*/
|
||||
repoLabel?: string
|
||||
// repoLabel?: string
|
||||
|
||||
/**
|
||||
* Navbar config
|
||||
@ -142,9 +142,9 @@ export interface PlumeThemeLocaleData extends LocaleData {
|
||||
*/
|
||||
openInNewWindow?: string | boolean
|
||||
|
||||
notFound?: string[]
|
||||
// notFound?: string[]
|
||||
|
||||
backToHome?: string
|
||||
// backToHome?: string
|
||||
|
||||
footer?:
|
||||
| false
|
||||
|
||||
610
pnpm-lock.yaml
generated
610
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user