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:
@@ -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'
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
72
.vuepress/config/sidebar.resolve.ts
Normal file
72
.vuepress/config/sidebar.resolve.ts
Normal 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)
|
||||
}
|
||||
@@ -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'),
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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: '计算机引论',
|
||||
|
||||
@@ -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: '体系结构',
|
||||
|
||||
@@ -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: '基础入门',
|
||||
|
||||
@@ -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: '系统概述',
|
||||
|
||||
@@ -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 },
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user