commit
f20af9d245
@ -1,3 +1,4 @@
|
||||
import { constants, promises as fsp } from 'node:fs'
|
||||
import type { App } from 'vuepress/core'
|
||||
import { getIconContentCSS, getIconData } from '@iconify/utils'
|
||||
import { fs, logger } from 'vuepress/utils'
|
||||
@ -15,6 +16,7 @@ export interface IconCacheItem {
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', 8)
|
||||
const iconDataCache = new Map<string, any>()
|
||||
const URL_CONTENT_RE = /(url\([^]+?\))/
|
||||
const CSS_PATH = 'internal/md-power/icons.css'
|
||||
|
||||
function resolveOption(opt?: boolean | IconsOptions): Required<IconsOptions> {
|
||||
const options = typeof opt === 'object' ? opt : {}
|
||||
@ -27,8 +29,16 @@ function resolveOption(opt?: boolean | IconsOptions): Required<IconsOptions> {
|
||||
export function createIconCSSWriter(app: App, opt?: boolean | IconsOptions) {
|
||||
const cache = new Map<string, IconCacheItem>()
|
||||
const isInstalled = isPackageExists('@iconify/json')
|
||||
const currentPath = app.dir.temp(CSS_PATH)
|
||||
|
||||
const write = (content: string) => app.writeTemp('internal/md-power/icons.css', content)
|
||||
const write = async (content: string) => {
|
||||
if (!content && app.env.isDev) {
|
||||
if (existsSync(currentPath) && (await fsp.stat(currentPath)).isFile()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
await app.writeTemp(CSS_PATH, content)
|
||||
}
|
||||
let timer: NodeJS.Timeout | null = null
|
||||
|
||||
const options = resolveOption(opt)
|
||||
@ -42,10 +52,12 @@ export function createIconCSSWriter(app: App, opt?: boolean | IconsOptions) {
|
||||
timer = setTimeout(async () => {
|
||||
let css = defaultContent
|
||||
|
||||
for (const [, { content, className }] of cache)
|
||||
css += `.${className} {\n --svg: ${content};\n}\n`
|
||||
if (cache.size > 0) {
|
||||
for (const [, { content, className }] of cache)
|
||||
css += `.${className} {\n --svg: ${content};\n}\n`
|
||||
|
||||
await write(css)
|
||||
await write(css)
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
|
||||
@ -132,3 +144,13 @@ async function genIconContent(iconName: string, cb: (content: string) => void) {
|
||||
const match = content.match(URL_CONTENT_RE)
|
||||
return cb(match ? match[1] : '')
|
||||
}
|
||||
|
||||
function existsSync(fp: string) {
|
||||
try {
|
||||
fs.accessSync(fp, constants.R_OK)
|
||||
return true
|
||||
}
|
||||
catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,7 +106,7 @@ const { theme, page } = useData()
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.vp-blog {
|
||||
min-height: calc(100vh - var(--vp-footer-height, 0px));
|
||||
min-height: calc(100vh - var(--vp-nav-height) - var(--vp-footer-height, 0px));
|
||||
}
|
||||
|
||||
.blog-container {
|
||||
|
||||
@ -173,6 +173,10 @@ watch(
|
||||
max-width: 784px;
|
||||
}
|
||||
|
||||
.vp-doc-container:not(.has-sidebar.has-aside) .content {
|
||||
max-width: 884px;
|
||||
}
|
||||
|
||||
.vp-doc-container:not(.has-sidebar) .container {
|
||||
max-width: 1104px;
|
||||
}
|
||||
|
||||
@ -373,7 +373,7 @@ export function useSidebarControl(item: ComputedRef<ResolvedSidebarItem>): Sideb
|
||||
}
|
||||
|
||||
return item.value.items
|
||||
? containsActiveLink(page.value.filePathRelative || '', item.value.items)
|
||||
? containsActiveLink(page.value.path, item.value.items)
|
||||
: false
|
||||
})
|
||||
|
||||
|
||||
@ -98,6 +98,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.vp-doc > h1:first-of-type {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vp-doc img {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@ -93,7 +93,6 @@ export async function watchAutoFrontmatter(app: App, watchers: any[], enable?: (
|
||||
async function generator(file: AutoFrontmatterMarkdownFile): Promise<void> {
|
||||
if (!generate)
|
||||
return
|
||||
|
||||
const { filepath, relativePath } = file
|
||||
|
||||
const current = generate.rules.find(({ filter }) => filter(relativePath))
|
||||
@ -115,7 +114,7 @@ async function generator(file: AutoFrontmatterMarkdownFile): Promise<void> {
|
||||
.replace(/\s+\n/g, '\n')
|
||||
const newContent = yaml ? `${yaml}---\n${content}` : content
|
||||
|
||||
fs.writeFileSync(filepath, newContent, 'utf-8')
|
||||
await fs.promises.writeFile(filepath, newContent, 'utf-8')
|
||||
}
|
||||
catch (e) {
|
||||
console.error(e)
|
||||
|
||||
@ -22,7 +22,7 @@ import { resolveNotesOptions } from '../config/index.js'
|
||||
|
||||
export function resolveOptions(
|
||||
localeOptions: PlumeThemeLocaleOptions,
|
||||
frontmatter: AutoFrontmatter,
|
||||
options: AutoFrontmatter,
|
||||
): AutoFrontmatter {
|
||||
const pkg = getPackage()
|
||||
const { locales = {}, article: articlePrefix = '/article/' } = localeOptions
|
||||
@ -46,23 +46,28 @@ export function resolveOptions(
|
||||
})
|
||||
.filter(Boolean)
|
||||
|
||||
const baseFrontmatter: AutoFrontmatterObject = {
|
||||
author(author: string, { relativePath }, data: any) {
|
||||
const baseFrontmatter: AutoFrontmatterObject = {}
|
||||
|
||||
if (options.author !== false) {
|
||||
baseFrontmatter.author = (author: string, { relativePath }, data) => {
|
||||
if (author)
|
||||
return author
|
||||
if (data.friends)
|
||||
if (data.friends || data.pageLayout === 'friends')
|
||||
return
|
||||
const profile = resolveOptions(relativePath).profile ?? resolveOptions(relativePath).avatar
|
||||
|
||||
return profile?.name || pkg.author || ''
|
||||
},
|
||||
createTime(formatTime: string, { createTime }, data: any) {
|
||||
}
|
||||
}
|
||||
|
||||
if (options.createTime !== false) {
|
||||
baseFrontmatter.createTime = (formatTime: string, { createTime }, data) => {
|
||||
if (formatTime)
|
||||
return formatTime
|
||||
if (data.friends)
|
||||
if (data.friends || data.pageLayout === 'friends')
|
||||
return
|
||||
return format(new Date(createTime), 'yyyy/MM/dd HH:mm:ss')
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const notesByLocale = (locale: string) => {
|
||||
@ -86,8 +91,8 @@ export function resolveOptions(
|
||||
}
|
||||
|
||||
return {
|
||||
include: frontmatter?.include ?? ['**/*.md'],
|
||||
exclude: uniq(['.vuepress/**/*', 'node_modules', ...(frontmatter?.exclude ?? [])]),
|
||||
include: options?.include ?? ['**/*.md'],
|
||||
exclude: uniq(['.vuepress/**/*', 'node_modules', ...(options?.exclude ?? [])]),
|
||||
|
||||
frontmatter: [
|
||||
localesNotesDirs.length
|
||||
@ -95,31 +100,39 @@ export function resolveOptions(
|
||||
// note 首页链接
|
||||
include: localesNotesDirs.map(dir => pathJoin(dir, '/{readme,README,index}.md')),
|
||||
frontmatter: {
|
||||
title(title: string, { relativePath }) {
|
||||
if (title)
|
||||
return title
|
||||
const note = findNote(relativePath)
|
||||
if (note?.text)
|
||||
return note.text
|
||||
return getCurrentDirname('', relativePath) || ''
|
||||
},
|
||||
...options.title !== false
|
||||
? {
|
||||
title(title: string, { relativePath }) {
|
||||
if (title)
|
||||
return title
|
||||
const note = findNote(relativePath)
|
||||
if (note?.text)
|
||||
return note.text
|
||||
return getCurrentDirname('', relativePath) || ''
|
||||
},
|
||||
} as AutoFrontmatterObject
|
||||
: undefined,
|
||||
...baseFrontmatter,
|
||||
permalink(permalink: string, { relativePath }, data: any) {
|
||||
if (permalink)
|
||||
return permalink
|
||||
if (data.friends)
|
||||
return
|
||||
const locale = resolveLocale(relativePath)
|
||||
...options.permalink !== false
|
||||
? {
|
||||
permalink(permalink: string, { relativePath }, data: any) {
|
||||
if (permalink)
|
||||
return permalink
|
||||
if (data.friends)
|
||||
return
|
||||
const locale = resolveLocale(relativePath)
|
||||
|
||||
const prefix = notesByLocale(locale)?.link || ''
|
||||
const note = findNote(relativePath)
|
||||
return pathJoin(
|
||||
locale,
|
||||
prefix,
|
||||
note?.link || getCurrentDirname(note?.dir, relativePath),
|
||||
'/',
|
||||
)
|
||||
},
|
||||
const prefix = notesByLocale(locale)?.link || ''
|
||||
const note = findNote(relativePath)
|
||||
return pathJoin(
|
||||
locale,
|
||||
prefix,
|
||||
note?.link || getCurrentDirname(note?.dir, relativePath),
|
||||
'/',
|
||||
)
|
||||
},
|
||||
} as AutoFrontmatterObject
|
||||
: undefined,
|
||||
},
|
||||
}
|
||||
: '',
|
||||
@ -127,45 +140,53 @@ export function resolveOptions(
|
||||
? {
|
||||
include: localesNotesDirs.map(dir => `${dir}**/**.md`),
|
||||
frontmatter: {
|
||||
title(title: string, { relativePath }) {
|
||||
if (title)
|
||||
return title
|
||||
...options.title !== false
|
||||
? {
|
||||
title(title: string, { relativePath }) {
|
||||
if (title)
|
||||
return title
|
||||
|
||||
const note = findNote(relativePath)
|
||||
let basename = path.basename(relativePath, '.md')
|
||||
if (note?.sidebar === 'auto')
|
||||
basename = basename.replace(/^\d+\./, '')
|
||||
const note = findNote(relativePath)
|
||||
let basename = path.basename(relativePath, '.md')
|
||||
if (note?.sidebar === 'auto')
|
||||
basename = basename.replace(/^\d+\./, '')
|
||||
|
||||
return basename
|
||||
},
|
||||
return basename
|
||||
},
|
||||
} as AutoFrontmatterObject
|
||||
: undefined,
|
||||
...baseFrontmatter,
|
||||
permalink(permalink: string, { relativePath }, data: any) {
|
||||
if (permalink)
|
||||
return permalink
|
||||
if (data.friends)
|
||||
return
|
||||
const locale = resolveLocale(relativePath)
|
||||
const notes = notesByLocale(locale)
|
||||
const note = findNote(relativePath)
|
||||
const prefix = notes?.link || ''
|
||||
const args: string[] = [
|
||||
locale,
|
||||
prefix,
|
||||
note?.link || '',
|
||||
]
|
||||
const sidebar = note?.sidebar
|
||||
...options.permalink !== false
|
||||
? {
|
||||
permalink(permalink: string, { relativePath }, data: any) {
|
||||
if (permalink)
|
||||
return permalink
|
||||
if (data.friends)
|
||||
return
|
||||
const locale = resolveLocale(relativePath)
|
||||
const notes = notesByLocale(locale)
|
||||
const note = findNote(relativePath)
|
||||
const prefix = notes?.link || ''
|
||||
const args: string[] = [
|
||||
locale,
|
||||
prefix,
|
||||
note?.link || '',
|
||||
]
|
||||
const sidebar = note?.sidebar
|
||||
|
||||
if (note && sidebar && sidebar !== 'auto') {
|
||||
const res = resolveLinkBySidebar(sidebar, pathJoin(notes?.dir || '', note.dir || ''))
|
||||
const file = ensureLeadingSlash(relativePath)
|
||||
if (res[file])
|
||||
args.push(res[file])
|
||||
else
|
||||
res[path.dirname(file)] && args.push(res[path.dirname(file)])
|
||||
}
|
||||
if (note && sidebar && sidebar !== 'auto') {
|
||||
const res = resolveLinkBySidebar(sidebar, pathJoin(notes?.dir || '', note.dir || ''))
|
||||
const file = ensureLeadingSlash(relativePath)
|
||||
if (res[file])
|
||||
args.push(res[file])
|
||||
else
|
||||
res[path.dirname(file)] && args.push(res[path.dirname(file)])
|
||||
}
|
||||
|
||||
return pathJoin(...args, nanoid(), '/')
|
||||
},
|
||||
return pathJoin(...args, nanoid(), '/')
|
||||
},
|
||||
} as AutoFrontmatterObject
|
||||
: undefined,
|
||||
},
|
||||
}
|
||||
: '',
|
||||
@ -176,21 +197,29 @@ export function resolveOptions(
|
||||
{
|
||||
include: '*',
|
||||
frontmatter: {
|
||||
title(title: string, { relativePath }) {
|
||||
if (title)
|
||||
return title
|
||||
const basename = path.basename(relativePath || '', '.md')
|
||||
return basename
|
||||
},
|
||||
...options.title !== false
|
||||
? {
|
||||
title(title: string, { relativePath }) {
|
||||
if (title)
|
||||
return title
|
||||
const basename = path.basename(relativePath || '', '.md')
|
||||
return basename
|
||||
},
|
||||
} as AutoFrontmatterObject
|
||||
: undefined,
|
||||
...baseFrontmatter,
|
||||
permalink(permalink: string, { relativePath }) {
|
||||
if (permalink)
|
||||
return permalink
|
||||
const locale = resolveLocale(relativePath)
|
||||
const prefix = withBase(articlePrefix, locale)
|
||||
...options.permalink !== false
|
||||
? {
|
||||
permalink(permalink: string, { relativePath }) {
|
||||
if (permalink)
|
||||
return permalink
|
||||
const locale = resolveLocale(relativePath)
|
||||
const prefix = withBase(articlePrefix, locale)
|
||||
|
||||
return normalizePath(`${prefix}/${nanoid()}/`)
|
||||
},
|
||||
return normalizePath(`${prefix}/${nanoid()}/`)
|
||||
},
|
||||
} as AutoFrontmatterObject
|
||||
: undefined,
|
||||
},
|
||||
},
|
||||
].filter(Boolean) as AutoFrontmatterArray,
|
||||
|
||||
@ -27,7 +27,6 @@ export interface Loader {
|
||||
dependencies: string[]
|
||||
load: () => Promise<{ config: ThemeConfig, dependencies: string[] }>
|
||||
loaded: boolean
|
||||
watcher: FSWatcher | null
|
||||
changeEvents: ChangeEvent[]
|
||||
whenLoaded: ChangeEvent[]
|
||||
defaultConfig: ThemeConfig
|
||||
@ -47,7 +46,6 @@ export async function initConfigLoader(
|
||||
dependencies: [],
|
||||
load: () => compiler(loader!.configFile),
|
||||
loaded: false,
|
||||
watcher: null,
|
||||
changeEvents: [],
|
||||
whenLoaded: [],
|
||||
defaultConfig,
|
||||
@ -64,7 +62,7 @@ export async function initConfigLoader(
|
||||
|
||||
const { config, dependencies = [] } = await loader.load()
|
||||
loader.loaded = true
|
||||
addDependencies(dependencies)
|
||||
loader.dependencies = [...dependencies]
|
||||
updateResolvedConfig(app, config)
|
||||
runChangeEvents()
|
||||
|
||||
@ -81,14 +79,14 @@ export function watchConfigFile(app: App, watchers: any[]) {
|
||||
cwd: path.join(path.dirname(loader.configFile), '../'),
|
||||
})
|
||||
|
||||
addDependencies()
|
||||
addDependencies(watcher)
|
||||
|
||||
watcher.on('change', async () => {
|
||||
if (loader) {
|
||||
loader.loaded = false
|
||||
const { config, dependencies = [] } = await loader.load()
|
||||
loader.loaded = true
|
||||
addDependencies(dependencies)
|
||||
addDependencies(watcher, dependencies)
|
||||
updateResolvedConfig(app, config)
|
||||
runChangeEvents()
|
||||
}
|
||||
@ -99,8 +97,6 @@ export function watchConfigFile(app: App, watchers: any[]) {
|
||||
runChangeEvents()
|
||||
})
|
||||
|
||||
loader.watcher = watcher
|
||||
|
||||
watchers.push(watcher)
|
||||
}
|
||||
|
||||
@ -147,7 +143,7 @@ function runChangeEvents() {
|
||||
}
|
||||
}
|
||||
|
||||
function addDependencies(dependencies?: string[]) {
|
||||
function addDependencies(watcher: FSWatcher, dependencies?: string[]) {
|
||||
if (!loader)
|
||||
return
|
||||
|
||||
@ -155,9 +151,9 @@ function addDependencies(dependencies?: string[]) {
|
||||
const deps = dependencies
|
||||
.filter(dep => !loader!.dependencies.includes(dep) && dep[0] === '.')
|
||||
loader.dependencies.push(...deps)
|
||||
deps.length && loader.watcher?.add(deps)
|
||||
watcher.add(deps)
|
||||
}
|
||||
else {
|
||||
loader.watcher?.add(loader.dependencies)
|
||||
watcher.add(loader.dependencies)
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,7 +138,7 @@ function getAutoDirSidebar(
|
||||
current.icon = frontmatter.icon as ThemeIcon
|
||||
}
|
||||
if (parent?.items?.length) {
|
||||
parent.collapsed = true
|
||||
parent.collapsed = false
|
||||
}
|
||||
parent = current
|
||||
items = current.items as ResolvedSidebarItem[]
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { Stats } from 'node:fs'
|
||||
import type { PlumeThemePageFrontmatter } from './frontmatter/page.js'
|
||||
|
||||
export interface AutoFrontmatterMarkdownFile {
|
||||
filepath: string
|
||||
@ -8,13 +9,13 @@ export interface AutoFrontmatterMarkdownFile {
|
||||
stats: Stats
|
||||
}
|
||||
|
||||
export type FrontmatterFn<T = any, K = object> = (
|
||||
export type FrontmatterFn<T = any> = (
|
||||
value: T,
|
||||
file: AutoFrontmatterMarkdownFile,
|
||||
data: K
|
||||
data: PlumeThemePageFrontmatter
|
||||
) => T | PromiseLike<T>
|
||||
|
||||
export type AutoFrontmatterObject<K = object, T = any> = Record<string, FrontmatterFn<T, K>>
|
||||
export type AutoFrontmatterObject<T = any> = Record<string, FrontmatterFn<T>>
|
||||
|
||||
export type AutoFrontmatterArray = {
|
||||
include: string | string[]
|
||||
@ -23,12 +24,42 @@ export type AutoFrontmatterArray = {
|
||||
|
||||
export interface AutoFrontmatter {
|
||||
/**
|
||||
* FilterPattern
|
||||
* glob 匹配,被匹配的文件将会自动生成 frontmatter
|
||||
*
|
||||
* @default ['**\/*.md']
|
||||
*/
|
||||
include?: string | string[]
|
||||
|
||||
/**
|
||||
* glob 匹配,被匹配的文件将不会自动生成 frontmatter
|
||||
*/
|
||||
exclude?: string | string[]
|
||||
|
||||
/**
|
||||
* 是否自动生成 permalink
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
permalink?: boolean
|
||||
/**
|
||||
* 是否自动生成 createTime
|
||||
*
|
||||
* 默认读取 文件创建时间,`createTitme` 比 vuepress 默认的 `date` 时间更精准到秒
|
||||
*/
|
||||
createTime?: boolean
|
||||
/**
|
||||
* 是否自动生成 author
|
||||
*
|
||||
* 默认读取 `profile.name` 或 `package.json` 的 `author`
|
||||
*/
|
||||
author?: boolean
|
||||
/**
|
||||
* 是否自动生成 title
|
||||
*
|
||||
* 默认读取文件名作为标题
|
||||
*/
|
||||
title?: boolean
|
||||
|
||||
/**
|
||||
* {
|
||||
* key(value, file, data) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user