1
0
mirror of https://github.com/142vip/408CSFamily.git synced 2026-11-02 05:14:16 +08:00

refactor(vuepress): 优化站点配置并统一侧边栏 permalink 解析

- siteDevPort 抽至 site.ts,接入 createVipViteBuildPlugin 消除构建 chunk 告警
- 移除 sitePathsPlugin,文档路由完全依赖 frontmatter permalink
- 侧边栏解析迁至 sidebar.resolve.ts,各学科 sidebar 使用 DocsSidebarConfig 类型
- 启用水印,简化 theme 中与包默认重复的配置项
This commit is contained in:
chufan
2026-09-22 15:19:30 +08:00
parent 6712f1bd65
commit 420372fb44
11 changed files with 117 additions and 188 deletions

View File

@@ -1,21 +1,6 @@
/** 站点配置统一导出入口 */
export {
copyrightHtmlStr,
footerHtmlStr,
} from './footer'
export { sitePathsPlugin } from './plugins/site-paths'
export { seoHead, siteHead } from './seo'
export {
navbarConfig,
sidebarConfig,
} from './sidebar'
export {
homeUrl,
pkg,
siteBase,
siteDescription,
siteLogo,
siteLogoUrl,
siteOrigin,
siteTitle,
} from './site'
export * from './footer'
export * from './seo'
export * from './sidebar'
export * from './sidebar.resolve'
export * from './site'

View File

@@ -1,82 +0,0 @@
import type { AutoLinkOptions, SidebarGroupOptions, SidebarItemOptions } from 'vuepress-theme-hope'
import { VipNodeJS } from '@142vip/utils'
/** 学科目录,与 `docs/` 下子目录及 sidebar 配置键一致 */
export type SidebarSubject = 'ds' | 'os' | 'ccp' | 'cn'
/** 各学科 `*.sidebar.ts` 的链接项(避免在 docs 中直接依赖 hope 类型) */
export interface SubjectSidebarLink {
text: string
link: string
}
/** 各学科 `*.sidebar.ts` 的分组项 */
export interface SubjectSidebarGroup {
text: string
prefix?: string
collapsible?: boolean
expanded?: boolean
children: SubjectSidebarLink[]
}
export type SubjectSidebarConfig = SubjectSidebarGroup[]
function readPermalink(mdFilePath: string): string | null {
if (!VipNodeJS.existPath(mdFilePath)) {
return null
}
for (const line of VipNodeJS.readFileToStrByUTF8(mdFilePath).split('\n')) {
if (line.startsWith('permalink:')) {
return line.slice('permalink:'.length).trim()
}
}
return null
}
function resolveSidebarLink(
item: SubjectSidebarLink,
subject: SidebarSubject,
groupPrefix: string,
): AutoLinkOptions {
if (!item.link.endsWith('.md')) {
return item
}
const mdRelativePath = groupPrefix
? `${groupPrefix}/${item.link}`
: item.link
const permalink = readPermalink(
VipNodeJS.pathJoin(VipNodeJS.getProcessCwd(), 'docs', subject, mdRelativePath),
)
if (permalink == null) {
return item
}
return { ...item, link: permalink }
}
function resolveSidebarGroup(
group: SubjectSidebarGroup,
subject: SidebarSubject,
): SidebarGroupOptions {
const groupPrefix = group.prefix ?? ''
return {
...group,
children: group.children.map(child => resolveSidebarLink(child, subject, groupPrefix)),
}
}
/**
* Hope 手动 sidebar 的 `.md` 链接按「prefix + 文件名」推断路由,不会读取 `permalink`。
* 官方推荐在 `link` 中直接写 permalink;此处仅在配置阶段自动补齐,避免维护成本。
*/
export function resolveSidebarPermalinks(
sidebar: SubjectSidebarConfig,
subject: SidebarSubject,
): SidebarItemOptions[] {
return sidebar.map(group => resolveSidebarGroup(group, subject))
}

View File

@@ -1,52 +0,0 @@
import type { App, PageOptions, Plugin } from '@vuepress/core'
import { VipNodeJS } from '@142vip/utils'
import { ensureLeadingSlash, inferRoutePath } from '@vuepress/shared'
const DOCS_PREFIX = 'docs/'
/**
* 源目录为项目根时,将 `docs/` 下的 Markdown 映射为与原 `docs` 源目录一致的路由。
* 例如:`docs/ds/README.md` → `/ds/`,避免 URL 出现 `/docs/` 前缀。
*/
function getDocsRoutePath(filePath: string, sourceDir: string): string | null {
const normalizedSource = `${VipNodeJS.pathResolve(sourceDir)}/`
const normalizedFile = VipNodeJS.pathResolve(filePath)
if (!normalizedFile.startsWith(normalizedSource)) {
return null
}
const filePathRelative = normalizedFile
.slice(normalizedSource.length)
.replaceAll('\\', '/')
if (!filePathRelative.startsWith(DOCS_PREFIX)) {
return null
}
const routePath = filePathRelative.slice(DOCS_PREFIX.length)
return inferRoutePath(ensureLeadingSlash(routePath))
}
export function sitePathsPlugin(): Plugin {
return {
name: 'vip-site-paths',
extendsPageOptions(options: PageOptions, app: App) {
if (options.filePath == null) {
return
}
const inferredPath = getDocsRoutePath(options.filePath, app.dir.source())
if (inferredPath == null) {
return
}
if (options.frontmatter?.permalink != null) {
return
}
options.frontmatter = {
...options.frontmatter,
permalink: inferredPath,
}
},
}
}

View File

@@ -0,0 +1,72 @@
import type {
DocsSidebarConfig,
DocsSidebarGroup,
DocsSidebarItem,
} from '@142vip/vuepress'
import type { AutoLinkOptions, SidebarItemOptions } from 'vuepress-theme-hope'
import { VipNodeJS } from '@142vip/utils'
function readPermalink(mdFilePath: string): string | null {
if (!VipNodeJS.existPath(mdFilePath)) {
return null
}
for (const line of VipNodeJS.readFileToStrByUTF8(mdFilePath).split('\n')) {
if (line.startsWith('permalink:')) {
return line.slice('permalink:'.length).trim()
}
}
return null
}
function resolveMdLink(
link: string,
docsBasePath: string,
groupPrefix: string,
): string {
const mdRelativePath = groupPrefix
? `${groupPrefix}/${link}`
: link
const permalink = readPermalink(
VipNodeJS.pathJoin(VipNodeJS.getProcessCwd(), 'docs', docsBasePath, mdRelativePath),
)
return permalink ?? link
}
/**
* Hope 手动 sidebar 的 `.md` 链接按「prefix + 文件名」推断路由,**不会**读取 frontmatter `permalink`。
* 本仓库页面 permalink 与文件名不一致时,在配置阶段按文件解析并替换 `link`。
*
* 侧栏结构类型见 `@142vip/vuepress` 的 `DocsSidebar*`;本函数为站点侧解析工具。
*
* @param sidebar 模块侧边栏原始配置(可含 `.md` 链接)
* @param docsBasePath `docs/` 下的相对目录,如 `ds`、`os`
*/
export function resolveSidebarPermalinks(
sidebar: DocsSidebarConfig,
docsBasePath: string,
): SidebarItemOptions[] {
const walk = (items: DocsSidebarItem[], groupPrefix = ''): SidebarItemOptions[] =>
items.map((item) => {
if (Array.isArray((item as DocsSidebarGroup).children)) {
const group = item as DocsSidebarGroup
return {
...group,
children: walk(group.children, group.prefix ?? ''),
}
}
if (!item.link?.endsWith('.md')) {
return item as AutoLinkOptions
}
return {
...item,
link: resolveMdLink(item.link, docsBasePath, groupPrefix),
}
})
return walk(sidebar)
}

View File

@@ -3,7 +3,7 @@ import { cppSidebar } from '../../docs/ccp/cpp.sidebar'
import { cnSidebar } from '../../docs/cn/cn.sidebar'
import { dsSidebar } from '../../docs/ds/ds.sidebar'
import { osSidebar } from '../../docs/os/os.sidebar'
import { resolveSidebarPermalinks } from './plugins/resolve-sidebar-links'
import { resolveSidebarPermalinks } from './sidebar.resolve'
/** 顶部导航栏 */
export const navbarConfig = defineVipNavbarConfig([
@@ -33,7 +33,11 @@ export const navbarConfig = defineVipNavbarConfig([
},
])
/** 各学科侧边栏(路径与 `docs/` 下目录对应) */
/**
* 各学科侧边栏
* - 路径键与 Hope 路由前缀一致
* - `.md` 链接通过 `resolveSidebarPermalinks` 自动对齐 frontmatter `permalink`
*/
export const sidebarConfig = defineVipSidebarConfig({
'/ds': resolveSidebarPermalinks(dsSidebar, 'ds'),
'/os': resolveSidebarPermalinks(osSidebar, 'os'),

View File

@@ -22,6 +22,9 @@ export const siteBase = vipDocSite.getBase(pkg.name)
/** 站点首页绝对地址 */
export const homeUrl = new URL(siteBase || '/', `${siteOrigin}/`).href
/** 本地开发端口 */
export const siteDevPort = 4200
/** 站点 Logo(深浅色导航栏通用,`.vuepress/public/logo.png`) */
export const siteLogo = '/logo.png'

View File

@@ -1,9 +1,9 @@
import type { SubjectSidebarConfig } from '../../.vuepress/config/plugins/resolve-sidebar-links'
import type { DocsSidebarConfig } from '@142vip/vuepress'
/**
* 计算机组成原理
*/
export const cppSidebar: SubjectSidebarConfig = [
export const cppSidebar: DocsSidebarConfig = [
{
text: '计算机引论',
prefix: '计算机引论',

View File

@@ -1,9 +1,9 @@
import type { SubjectSidebarConfig } from '../../.vuepress/config/plugins/resolve-sidebar-links'
import type { DocsSidebarConfig } from '@142vip/vuepress'
/**
* 计算机网络
*/
export const cnSidebar: SubjectSidebarConfig = [
export const cnSidebar: DocsSidebarConfig = [
{
text: '体系结构',
prefix: '体系结构',

View File

@@ -1,9 +1,9 @@
import type { SubjectSidebarConfig } from '../../.vuepress/config/plugins/resolve-sidebar-links'
import type { DocsSidebarConfig } from '@142vip/vuepress'
/**
* 数据结构
*/
export const dsSidebar: SubjectSidebarConfig = [
export const dsSidebar: DocsSidebarConfig = [
{
text: '基础入门',
prefix: '基础入门',

View File

@@ -1,9 +1,9 @@
import type { SubjectSidebarConfig } from '../../.vuepress/config/plugins/resolve-sidebar-links'
import type { DocsSidebarConfig } from '@142vip/vuepress'
/**
* 操作系统
*/
export const osSidebar: SubjectSidebarConfig = [
export const osSidebar: DocsSidebarConfig = [
{
text: '系统概述',
prefix: '系统概述',

View File

@@ -1,9 +1,3 @@
import { OPEN_SOURCE_ADDRESS, OPEN_SOURCE_AUTHOR } from '@142vip/open-source'
import {
defineVipVuepressConfig,
getVipHopeTheme,
handleImportCodePath,
} from '@142vip/vuepress'
import {
copyrightHtmlStr,
footerHtmlStr,
@@ -12,25 +6,38 @@ import {
sidebarConfig,
siteBase,
siteDescription,
siteDevPort,
siteHead,
siteLogo,
sitePathsPlugin,
siteOrigin,
siteTitle,
} from './.vuepress/config'
} from '.vuepress/config'
import {
OPEN_SOURCE_ADDRESS,
OPEN_SOURCE_AUTHOR,
} from '@142vip/open-source'
import {
createVipViteBuildPlugin,
defineVipVuepressConfig,
getVipHopeTheme,
handleImportCodePath,
} from '@142vip/vuepress'
/**
* VuePress 站点配置
*
* 注意:
* - 环境变量 `NEED_PROXY=true` 时,base 为 `/{pkg.name}/`,否则为 `/`
* - `locales` / `lang` / `bundler` 等由 `defineVipVuepressConfig` 默认注入
* - 导航、SEO、页脚等拆分见 `.vuepress/config/`
* 对齐 `@142vip/vuepress`(见 core-x `packages/vuepress`):
* - `locales` / `lang` / `bundler` 由 `defineVipVuepressConfig` 默认注入
* - `author` / `docsDir` / `docsBranch` / `contributors` 由 `getVipHopeTheme` 默认注入
* - 大文档站 chunk 告警用包内 `createVipViteBuildPlugin`
* - 文档路由依赖各 md 的 frontmatter `permalink`
* - 侧边栏 `.md` 链接经 `resolveSidebarPermalinks` 对齐 permalink
*/
export default defineVipVuepressConfig({
base: siteBase,
title: siteTitle,
description: siteDescription,
port: 4200,
port: siteDevPort,
pagePatterns: [
'docs/**/*.md',
'!node_modules',
@@ -39,7 +46,7 @@ export default defineVipVuepressConfig({
],
head: siteHead,
plugins: [
sitePathsPlugin(),
createVipViteBuildPlugin(),
],
markdown: {
importCode: {
@@ -65,23 +72,16 @@ export default defineVipVuepressConfig({
end: ['Language', 'Outlook', 'Repo', 'Search'],
},
logo: siteLogo,
hostname: OPEN_SOURCE_ADDRESS.HOME_PAGE_DOMAIN_408,
hostname: siteOrigin,
// 页脚
footer: footerHtmlStr,
// 版权
copyright: copyrightHtmlStr,
// 仓库 142vip/408CSFamily
repo: `${OPEN_SOURCE_ADDRESS.GITHUB_ORGANIZATION_NAME}/${pkg.name}`,
// 作者信息
author: OPEN_SOURCE_AUTHOR,
// 主题布局选项
docsRepo: OPEN_SOURCE_ADDRESS.GITHUB_REPO_408,
contributors: true,
// 插件
plugins: {
// 水印
watermark: {
enabled: false,
enabled: true,
watermarkOptions: {
content: OPEN_SOURCE_AUTHOR.name,
},
@@ -89,6 +89,5 @@ export default defineVipVuepressConfig({
},
}),
}, {
// 浏览器控制台打印版本与构建时间
appBuildLog: { version: pkg.version },
})