chore: improve theme code comments

This commit is contained in:
pengzhanbo 2026-03-08 16:59:50 +08:00
parent 77856e36c5
commit 896c7e22df
10 changed files with 413 additions and 20 deletions

View File

@ -12,10 +12,34 @@ import { removeLeadingSlash } from 'vuepress/shared'
import { normalizeLink } from '../utils/index.js'
import { useData } from './data.js'
/**
* Reference type for collections data.
* Maps locale paths to arrays of collection items.
*
*
*
*/
export type CollectionsRef = Ref<Record<string, ThemeCollectionItem[]>>
/**
* Reference type for a single collection item.
*
*
*/
export type CollectionItemRef<T extends ThemeBaseCollection> = Ref<T | undefined>
/**
* Global reference to all collections data.
*
*
*/
export const collectionsRef: CollectionsRef = ref(collectionsRaw)
/**
* Global reference to the current collection item.
*
*
*/
export const collectionItemRef: CollectionItemRef<ThemeBaseCollection> = ref()
const forceCollection = ref<string | true>()
@ -26,13 +50,49 @@ if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) {
}
}
/**
* Use collections data.
* Returns the global collections reference.
*
*
*
*
* @returns Collections data reference /
*/
export const useCollections = (): CollectionsRef => collectionsRef
/**
* Use current collection item.
* Returns the current collection based on the page context.
*
*
*
*
* @template T - Collection type /
* @returns Current collection item reference /
*/
export const useCollection = <T extends ThemeBaseCollection = ThemeDocCollection>(): CollectionItemRef<T> => collectionItemRef as CollectionItemRef<T>
/**
* Force update the current collection.
* Used to programmatically switch the active collection.
*
*
*
*
* @param dir - Collection directory or true for first posts collection / true
*/
export function forceUpdateCollection(dir?: string | true): void {
forceCollection.value = dir
}
/**
* Setup collection tracking.
* Automatically determines the current collection based on route and page path.
*
*
*
*/
export function setupCollection(): void {
const routeLocale = useRouteLocale()
const { page } = useData()

View File

@ -2,17 +2,42 @@ import type { Ref } from 'vue'
import { onUnmounted, readonly, ref, watch } from 'vue'
import { inBrowser } from '../utils/index.js'
/**
* Options for useFlyout composable.
*
* useFlyout
*/
interface UseFlyoutOptions {
/** Element to track focus for / 要跟踪焦点的元素 */
el: Ref<HTMLElement | undefined>
/** Callback when element gains focus / 元素获得焦点时的回调 */
onFocus?: () => void
/** Callback when element loses focus / 元素失去焦点时的回调 */
onBlur?: () => void
}
/**
* Currently focused element reference.
* Shared across all flyout instances.
*
*
*
*/
export const focusedElement: Ref<HTMLElement | undefined> = ref()
let active = false
let listeners = 0
/**
* Use flyout focus tracking.
* Tracks focus state for dropdown menus and flyout components.
*
*
*
*
* @param options - Flyout options /
* @returns Readonly reference to focus state /
*/
export function useFlyout(options: UseFlyoutOptions): Readonly<Ref<boolean>> {
const focus = ref(false)
@ -47,16 +72,37 @@ export function useFlyout(options: UseFlyoutOptions): Readonly<Ref<boolean>> {
return readonly(focus)
}
/**
* Activate global focus tracking.
* Adds focusin event listener to document.
*
*
* focusin
*/
function activateFocusTracking() {
document.addEventListener('focusin', handleFocusIn)
active = true
focusedElement.value = document.activeElement as HTMLElement
}
/**
* Deactivate global focus tracking.
* Removes focusin event listener from document.
*
*
* focusin
*/
function deactivateFocusTracking() {
document.removeEventListener('focusin', handleFocusIn)
}
/**
* Handle focusin event.
* Updates the focused element reference.
*
* focusin
*
*/
function handleFocusIn() {
focusedElement.value = document.activeElement as HTMLElement
}

View File

@ -2,14 +2,29 @@ import type { Ref } from 'vue'
import { icons } from '@internal/iconify'
import { ref } from 'vue'
/**
* Raw icons data structure from internal data.
*
*
*/
interface IconsRawData {
/** Collection names / 集合名称 */
co: string[]
/** Background icons by collection index / 按集合索引的背景图标 */
bg: Record<number, string[]>
/** Mask icons by collection index / 按集合索引的遮罩图标 */
mask: Record<number, string[]>
}
/**
* Processed icons data structure.
*
*
*/
interface IconsData {
/** List of background icons / 背景图标列表 */
bg: string[]
/** List of mask icons / 遮罩图标列表 */
mask: string[]
}
@ -17,6 +32,15 @@ type IconsDataRef = Ref<IconsData>
const iconsData: IconsDataRef = ref(resolveIconsData(icons))
/**
* Use icons data.
* Returns the processed icons data reference.
*
*
*
*
* @returns Icons data reference /
*/
export const useIconsData = (): IconsDataRef => iconsData
if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) {
@ -25,12 +49,31 @@ if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) {
}
}
// 旧版本内置图标别名,映射回 simple-icons 集合中的名称
/**
* Fallback mappings for old icon aliases.
* Maps legacy icon names to their current equivalents.
*
*
*
*/
export const socialFallbacks: Record<string, string> = {
twitter: 'x',
weibo: 'sinaweibo',
}
/**
* Resolve raw icons data to processed format.
* Converts indexed data to flat icon lists.
*
*
*
*
* @param data - Raw icons data /
* @param data.co - Collection names /
* @param data.bg - Background icons by collection index /
* @param data.mask - Mask icons by collection index /
* @returns Processed icons data /
*/
export function resolveIconsData({ co, bg, mask }: IconsRawData): IconsData {
return {
bg: processIcons(co, bg),
@ -38,6 +81,16 @@ export function resolveIconsData({ co, bg, mask }: IconsRawData): IconsData {
}
}
/**
* Normalize icon to CSS class name.
* Returns the appropriate class name based on icon type.
*
* CSS
*
*
* @param icon - Icon name in format "collection:name" / "collection:name"
* @returns CSS class name or empty string if not found / CSS
*/
export function normalizeIconClassname(icon: string): string {
const [collect, name] = icon.split(':')
const iconName = `vpi-${collect}-${name}`
@ -48,6 +101,17 @@ export function normalizeIconClassname(icon: string): string {
return ''
}
/**
* Process indexed icons into flat list.
* Converts collection-indexed data to "collection:icon" format.
*
*
* "collection:icon"
*
* @param collects - Array of collection names /
* @param raw - Indexed icon data /
* @returns Flat array of icon identifiers /
*/
function processIcons(collects: string[], raw: Record<number, string[]>): string[] {
const data: string[] = []
for (const [key, list] of Object.entries(raw)) {

View File

@ -12,18 +12,51 @@ import { useRouteLocale } from 'vuepress/client'
import { normalizeLink, normalizePrefix, resolveNavLink } from '../utils/index.js'
import { useData } from './data.js'
/**
* Sidebar data type - maps locale paths to sidebar configurations.
*
* -
*/
export type SidebarData = Record<string, ThemeSidebar>
/**
* Reference type for sidebar data.
*
*
*/
export type SidebarDataRef = Ref<SidebarData>
/**
* Reference type for auto-generated directory sidebar.
*
*
*/
export type AutoDirSidebarRef = Ref<ThemeSidebarItem[] | {
link: string
items: ThemeSidebarItem[]
}>
/**
* Reference type for auto-generated home data.
*
*
*/
export type AutoHomeDataRef = Ref<Record<string, string>>
const { __auto__, __home__, ...items } = sidebarRaw
/**
* Global sidebar data reference.
*
*
*/
export const sidebarData: SidebarDataRef = ref(items)
/**
* Auto-generated directory sidebar reference.
*
*
*/
export const autoDirSidebar: AutoDirSidebarRef = ref(__auto__)
const autoHomeData: AutoHomeDataRef = ref(__home__)
@ -38,6 +71,13 @@ if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) {
const sidebar: Ref<ResolvedSidebarItem[]> = ref([])
/**
* Setup sidebar tracking.
* Automatically updates sidebar based on route and frontmatter changes.
*
*
* frontmatter
*/
export function setupSidebar(): void {
const { page, frontmatter } = useData()
@ -67,19 +107,30 @@ export function setupSidebar(): void {
}
/**
* Use sidebar data
* Use sidebar data.
* Returns the resolved sidebar items for the current page.
*
*
*
*
*
* @returns Resolved sidebar items reference /
*/
export function useSidebarData(): Ref<ResolvedSidebarItem[]> {
return sidebar
}
/**
* Get the `Sidebar` from sidebar option. This method will ensure to get correct
* sidebar config from `MultiSideBarConfig` with various path combinations such
* as matching `guide/` and `/guide/`. If no matching config was found, it will
* return empty array.
* Get the sidebar configuration from sidebar options.
* Ensures correct sidebar config from MultiSideBarConfig with various path combinations.
* Returns empty array if no matching config is found.
*
*
* MultiSideBarConfig
*
*
* @param routePath - Current route path /
* @param routeLocal - Current route locale /
* @returns Resolved sidebar items /
*/
export function getSidebar(routePath: string, routeLocal: string): ResolvedSidebarItem[] {
const _sidebar = sidebarData.value[routeLocal]
@ -123,6 +174,17 @@ export function getSidebar(routePath: string, routeLocal: string): ResolvedSideb
return []
}
/**
* Resolve sidebar items from raw configuration.
* Converts string items and nested structures to resolved format.
*
*
*
*
* @param sidebarItems - Raw sidebar items /
* @param _prefix - URL prefix for nested items / URL
* @returns Resolved sidebar items /
*/
function resolveSidebarItems(
sidebarItems: (string | ThemeSidebarItem)[],
_prefix = '',
@ -163,7 +225,14 @@ function resolveSidebarItems(
}
/**
* Get or generate sidebar group from the given sidebar items.
* Get or generate sidebar groups from the given sidebar items.
* Groups consecutive items without children into a single group.
*
*
*
*
* @param sidebar - Flat array of sidebar items /
* @returns Grouped sidebar items /
*/
export function getSidebarGroups(sidebar: ResolvedSidebarItem[]): ResolvedSidebarItem[] {
const groups: ResolvedSidebarItem[] = []
@ -188,6 +257,16 @@ export function getSidebarGroups(sidebar: ResolvedSidebarItem[]): ResolvedSideba
return groups
}
/**
* Get the first link from sidebar items.
* Recursively searches through nested items to find the first link.
*
*
*
*
* @param sidebar - Sidebar items to search /
* @returns First link found or empty string /
*/
export function getSidebarFirstLink(sidebar: ResolvedSidebarItem[]): string {
for (const item of sidebar) {
if (item.link)

View File

@ -1,17 +1,39 @@
/**
* vuepress/core
* vuepress/core
* Temporary enhancement for VuePress app.
* This enhancement will be removed in the next version of vuepress/core.
*
* VuePress
* vuepress/core
*/
import type { App } from 'vuepress'
import { fs, hash } from 'vuepress/utils'
/**
* Cache structure for writeTemp operations.
* Tracks content hash and writing promises for optimization.
*
* writeTemp
*
*/
interface WriteTempCache {
hash?: string // content hash
current?: Promise<void> // the current writing promise
next?: () => Promise<void> // the next writing promise
/** Content hash for change detection / 用于变更检测的内容哈希 */
hash?: string
/** Current writing promise / 当前写入承诺 */
current?: Promise<void>
/** Next writing promise to chain / 要链接的下一个写入承诺 */
next?: () => Promise<void>
}
/**
* Enhance the VuePress app with optimized writeTemp method.
* Implements caching and promise chaining for better performance.
*
* 使 writeTemp VuePress
*
*
* @param app - VuePress application instance / VuePress
*/
export function enhanceApp(app: App): void {
// rewrite writeTemp to cache the writing promise
const cache = new Map<string, WriteTempCache>()

View File

@ -2,6 +2,17 @@ import type { Matcher } from 'picomatch'
import { toArray, uniq } from '@pengzhanbo/utils'
import picomatch from 'picomatch'
/**
* Resolve include and exclude patterns into pattern and ignore arrays.
* Converts various pattern formats into a standardized format for matching.
*
* include exclude pattern ignore
*
*
* @param include - Patterns to include, can be string or array /
* @param exclude - Patterns to exclude, can be string or array /
* @returns Object containing pattern and ignore arrays / pattern ignore
*/
export function resolveMatcherPattern(include?: string | string[], exclude?: string | string[]): {
pattern: string[]
ignore: string[]
@ -26,6 +37,18 @@ export function resolveMatcherPattern(include?: string | string[], exclude?: str
return { pattern, ignore }
}
/**
* Create a file matcher function using picomatch.
* Returns a function that tests if a file path matches the given patterns.
*
* 使 picomatch
*
*
* @param include - Patterns to include /
* @param exclude - Patterns to exclude /
* @param cwd - Current working directory for relative path matching /
* @returns Matcher function that tests file paths /
*/
export function createMatcher(include?: string | string[], exclude?: string | string[], cwd?: string): Matcher {
exclude = ['**/node_modules/**', '**/.vuepress/**', ...toArray(exclude)]
const { pattern, ignore } = resolveMatcherPattern(include, exclude)

View File

@ -4,17 +4,30 @@ import { interopDefault } from './interopDefault'
let _pinyin: typeof import('pinyin-pro').pinyin | null = null
/**
* Check if pinyin-pro package is installed
* Check if pinyin-pro package is installed.
* Used for Chinese character to pinyin conversion.
*
* pinyin-pro
* pinyin-pro
*
*/
export const hasPinyin = isPackageExists('pinyin-pro')
const hasPinyinData = isPackageExists('@pinyin-pro/data')
/**
* Get pinyin function
* Get the pinyin conversion function.
* Dynamically imports pinyin-pro and its data if available.
* Caches the result for subsequent calls.
*
*
*
* pinyin-pro
* 使
*
* @returns Pinyin function or null if not installed / null
* @example
* const pinyin = await getPinyin()
* if (pinyin) {
* const result = pinyin('中文') // 'zhōng wén'
* }
*/
export async function getPinyin() {
if (hasPinyin && !_pinyin) {

View File

@ -1,12 +1,36 @@
import type { App } from 'vuepress'
/**
* Options for resolving content to be written to a temporary file.
*
*
*/
export interface ResolveContentOptions {
/** Variable name for the exported content / 导出内容的变量名 */
name: string
/** Content to be serialized / 要序列化的内容 */
content: any
/** Content to prepend before the export / 在导出之前添加的内容 */
before?: string
/** Content to append after the export / 在导出之后添加的内容 */
after?: string
}
/**
* Resolve content string for writing to temporary files.
* Generates JavaScript module content with HMR support in development mode.
*
*
* HMR JavaScript
*
* @param app - VuePress application instance / VuePress
* @param options - Content resolution options /
* @param options.name - Variable name for the exported content /
* @param options.content - Content to be serialized /
* @param options.before - Content to prepend before the export /
* @param options.after - Content to append after the export /
* @returns Resolved content string /
*/
export function resolveContent(app: App, { name, content, before, after }: ResolveContentOptions): string {
content = `${before ? `${before}\n` : ''}export const ${name} = ${JSON.stringify(content)}${after ? `\n${after}` : ''}`

View File

@ -1,6 +1,9 @@
/**
* /
* Log app.lang
* Simple built-in Chinese/English translation utility.
* Used for log output based on app.lang settings.
*
* /
* app.lang
*/
import { isEmptyObject } from '@pengzhanbo/utils'
@ -11,6 +14,15 @@ type TranslateData = Record<string, string>
let lang: TranslateLang = 'en'
/**
* Set the translation language based on the current locale.
* Supports Chinese variants (zh-CN, zh, zh-Hans, zh-Hant) and defaults to English.
*
*
* zh-CNzhzh-Hanszh-Hant
*
* @param current - Current locale string /
*/
export function setTranslateLang(current: string): void {
if (['zh-CN', 'zh', 'zh-Hans', 'zh-Hant'].includes(current)) {
lang = 'zh'
@ -20,6 +32,24 @@ export function setTranslateLang(current: string): void {
}
}
/**
* Create a translation function with locale support.
* Returns a function that translates keys to localized strings with optional interpolation.
*
*
*
*
* @template Data - Data type for interpolation /
* @template Locale - Locale type for translations /
* @param locales - Locale data for each language /
* @returns Translation function /
* @example
* const t = createTranslate({
* zh: { hello: '你好,{{name}}' },
* en: { hello: 'Hello, {{name}}!' }
* })
* t('hello', { name: 'World' }) // '你好World' or 'Hello, World!'
*/
export function createTranslate<
Data extends TranslateData = TranslateData,
Locale extends TranslateLocale = TranslateLocale,

View File

@ -1,11 +1,33 @@
/**
*
* Write to temporary file only when content changes.
* Optimizes file I/O by caching content hashes.
*
*
* I/O
*/
import type { App } from 'vuepress'
import { hash } from './hash.js'
/**
* Cache for content hashes to detect changes.
* Maps file paths to their content hashes.
*
*
*
*/
export const contentHash: Map<string, string> = new Map()
/**
* Write content to a temporary file if it has changed.
* Uses hash comparison to avoid unnecessary writes.
*
*
* 使
*
* @param app - VuePress application instance / VuePress
* @param filepath - Relative path to the temporary file /
* @param content - Content to write /
*/
export async function writeTemp(
app: App,
filepath: string,
@ -18,6 +40,16 @@ export async function writeTemp(
}
}
/**
* Set or clear the content hash for a file.
* Used to manually manage the hash cache.
*
*
*
*
* @param filepath - File path to manage /
* @param content - Content to hash, or empty to clear /
*/
export function setContentHash(filepath: string, content: string): void {
if (content) {
const currentHash = hash(content)