项目结构与静态构建
Neoverse-Docs 的目录职责、路由布局、内容源、编译管线与静态导出
本章 AI 摘要
项目把内容、站点组件、解析逻辑、生成脚本和样式明确分层。Fumadocs 将 content/docs/{locale}/ 编译为类型安全内容源,Next.js 根据页面树生成全部 locale 与 slug,生产环境通过 output: 'export' 输出 out/ 静态站点。搜索、文档源码、Mermaid 资源与站点级常量也在构建期或集中配置中完成。各架构层的依赖方向构成严格单向 DAG,由轻量脚本在构建前自动检查。
一、目录职责
Neoverse-Doc/
├── content/docs/
│ ├── zh/ # 中文内容树
│ └── en/ # 英文内容树
├── public/
│ └── mermaid/ # 生成的 Mermaid SVG
├── scripts/
│ ├── content-pipeline.ts # 内容管线:IR 派生、校验与 Mermaid 生成 / 对账
│ ├── check-architecture.ts
│ └── verify-build-output.ts
├── src/
│ ├── app/ # 路由、数据读取与页面组合
│ ├── content/ # Schema、MDX 插件、IR、搜索索引、SEO 与 Manifest
│ ├── features/ # Docs-shell、Search、Reading、Tasks、Mermaid、Transition、Community
│ ├── runtime/ # Navigation、Motion 与 Interaction 外部 Store
│ ├── adapters/fumadocs/ # Source、Layout 配置与第三方 DOM 适配
│ ├── ui/ # Token、Surface 与全局样式
│ ├── components/ # 未迁移的共享组件与 MDX 注册表
│ ├── dictionaries/ # 项目 UI 字典
│ └── lib/ # i18n 与无业务语义的纯工具函数
├── source.config.ts # 内容源、Schema 与 MDX Pipeline 组装
├── next.config.ts # 静态导出与 Next.js 配置
└── package.json # 依赖与项目命令内容与实现分离
content/docs/ 是内容的唯一主要入口。页面导航由各级 meta.json 显式声明,单篇元数据由 Frontmatter 声明;React 组件和解析插件不分散到内容目录中。
src/components/mdx/ 是统一增强层。每篇文档都通过同一个组件注册表获得代码块、Mermaid、任务项、Tabs、文档卡片和文件层级,不需要逐页导入。
成熟业务能力收敛到 src/features/,跨 Feature 的导航、动效和交互状态由 src/runtime/ 提供;Fumadocs 的 Loader、Layout、TOC 与复杂 DOM 选择器统一经过 src/adapters/fumadocs/。scripts/ 只负责开发或构建前需要批量完成的工作,生成产物分别进入 public/mermaid/、src/features/mermaid/generated/、.source/ 与 out/。
依赖方向与架构检查
顶层目录的依赖方向由 scripts/check-architecture.ts 自动守护(bun run check:architecture,并作为 prebuild 的第一步执行)。脚本扫描 src/ 全部 TypeScript 导入(含 import(...) 动态形式),按下表判定层级违规:
| 源层 | 允许依赖 |
|---|---|
app | 全部下层(ui 仅经 globals.css 的 CSS @import 消费) |
components | features runtime content lib dictionaries |
features | runtime content adapters lib dictionaries |
runtime | adapters |
content | adapters lib dictionaries |
adapters | lib |
lib | 无(纯工具叶子层) |
dictionaries | lib |
矩阵是严格单向 DAG:任意两层不得互相引用。脚本启动时先用拓扑排序校验矩阵自身无环,成环即失败;扫描结束后对零引用的允许边告警,提示把矩阵剪除到与真实依赖一致(app → ui 这条 CSS 消费的边登记在 CSS_CONSUMED_EDGES 豁免)。ui 与 styles 必须保持纯 CSS,出现任何 .ts / .tsx 文件都会直接失败。
四条附加规则:
- 桶规则(Barrel):任何跨 Feature 边界的导入——无论来自其他层还是其他 Feature——必须命中目标 Feature 的
index.ts公共入口,直接引用features/A/内部文件会被拒绝;Feature 内部引用不受限。公共入口只暴露真正需要跨边界使用的能力,不整目录转发。 - Feature 边界:feature→feature 依赖默认禁止;确属正确业务关系的直接依赖在检查器
FEATURE_ALLOWLIST中登记理由后保留(当前仅community → transition一条:留言板返回导航复用转场感知的BackLink)。真实依赖边必须无环——即使全部获准。多 Feature 共需的实现按归属下沉runtime/content/lib等公共层,不复制代码、不建shared/垃圾桶目录。 - 例外清单:
EXCEPTIONS机制保留但当前清零——历史 4 条桥接例外已通过正确边界(features/docs-shell迁移与baseOptions依赖注入)全部消除。新增例外必须附充分理由。 - 适配器纯净:
adapters只做第三方接缝(Fumadocs 配置、source、DOM 访问器),产品内容(导航标题、字典文案)由 app 层调用方注入,适配器不得反向组合上层模块。
新增顶层目录必须在 ALLOWED 矩阵登记,否则按“未知层”失败。完整决策记录见仓库内 docs/adr/0001-architecture-boundary-check.md、docs/adr/0005-strict-layer-dag.md 与 docs/adr/0006-feature-boundary.md。
二、路由与布局
src/app/[lang]/
├── (home)/
│ ├── (index)/page.tsx # 首页
│ └── guestbook/page.tsx # 留言墙
├── docs/
│ ├── layout.tsx # DocsLayout
│ └── [...slug]/page.tsx # 文档正文
└── docs-source/
└── [...slug]/route.ts # Markdown 源码端点路由组让首页与文档页共享 locale,却使用不同布局:
- 首页使用顶部导航和内容门户,不加载文档侧栏;
- 文档页使用 Fumadocs
DocsLayout,包含侧栏、正文与本页目录; - 留言墙复用首页布局,但使用独立页面;
docs-source为每篇内容生成可静态访问的原始 Markdown。
根路径是静态语言分流入口:浏览器根据 navigator.languages 选择中文或英文,未知语言回退到英文;无脚本环境仍可使用页面中的两个显式入口。自动判断只发生在 /,不会改写读者主动访问的 /zh 或 /en。项目不使用 Middleware 在请求时判断 locale,因为生产部署没有长期运行的 Next.js 服务器。
三、内容源与页面树
source.config.ts 只组装内容源、src/content/schema/docs.ts 与 src/content/plugins/mdx-options.ts。fumadocs-mdx 把内容编译到 .source/,再由 src/adapters/fumadocs/source.ts 的 loader() 组合为页面树。
src/lib/i18n.ts 同时被内容 loader 和 Fumadocs UI 使用,是 locale 列表与默认语言的唯一来源。目录解析器以 zh、en 等一级目录分桶,因此同一个 slug 可以在不同 locale 下拥有独立内容。内容回退被显式关闭,缺失英文译文时不会在英文 URL 下重复输出中文页面。
页面树还负责:
meta.json中的章节标题、图标、分组与顺序;- 上一篇 / 下一篇页面关系;
- 搜索索引需要的页面 slug 与 locale;
generateStaticParams()所需的全部静态路由参数。
四、文档页面装配
src/app/[lang]/docs/[...slug]/page.tsx 保持为 Server Component,并集中装配:
- Frontmatter 中的标题、描述、作者和贡献者;
- Markdown 源码与 GitHub 文件操作;
- 可选任务进度卡片;
- 共享 MDX 组件注册表;
- 草稿门控;
- Fumadocs 目录和分页;
- canonical、真实译文的
hreflang、分享元数据与结构化数据; - 正文外的 Giscus 社区模块。
社区模块不嵌入 Fumadocs 正文容器,避免第三方 iframe 的高度变化影响正文卡片、目录与分页布局。
五、编译与输出管线
src/content/ir.ts 从编译后的 Source 单遍派生 Content IR(含 Mermaid 图表检测),是唯一的规范化内容数据面;src/content/generated/manifest.ts 是其消费视图,稳定 ID 来自 frontmatter 必填 id 加 docs: 前缀,同 ID 多语言页面共享身份。Manifest 保留草稿标记,由 sitemap 等消费者自行过滤,不存在第二份手写内容事实来源。生产构建还会生成 robots.txt、只包含已发布页面的 sitemap.xml,以及统一的 PNG 社交分享图。
不同命令承担不同验证范围:
| 命令 | 管线作用 |
|---|---|
bun dev | 先执行内容准备管线(IR 派生、校验与 Mermaid 增量生成),再启动开发服务器 |
bun typecheck | 生成路由类型、编译 Fumadocs 内容并执行 TypeScript 检查 |
bun run check:architecture | 扫描导入图,校验架构层依赖边界(亦作为 prebuild 第一步) |
bun run generate:content | 内容准备:更新静态 SVG 与资源映射(唯一会启动 Puppeteer 的命令) |
bun run check:content | 内容校验与 Mermaid 资产哈希对账,零 Puppeteer |
bun run build | 执行架构检查、内容校验与 Next.js 生产构建(不渲染 Mermaid) |
bun run start | 预览已经存在的 out/ |
六、静态导出的约束与收益
静态导出带来几项明确约束:核心功能不能依赖请求时 Server Action、Middleware、数据库或用户会话;所有页面参数必须在构建时可枚举;图片和第三方能力也需要兼容无服务器托管。
这些约束换来了更简单的部署与故障边界:
out/可以部署到任意静态托管平台;- 阅读正文不依赖后端可用性;
- 页面、搜索和图表在发布前已经生成;
- 轻量状态保存在浏览器,不需要账号系统;
- 第三方评论失败不会影响文档主体。
开发环境会暂时关闭 output: 'export',从而保留 Next.js 对未知路径的正常 404 行为;生产构建仍然启用完整静态导出。
七、站点级常量与双协议
src/lib/site-config.ts 是仓库信息、作者、协议与 Giscus 配置的单一来源:
| 常量 | 用途 |
|---|---|
REPO_URL | GitHub 仓库地址,用于源码链接、Issue 入口与 Giscus repo 配置 |
PROJECT_START_YEAR | Footer 中显示的项目起始年份 |
AUTHOR_GITHUB_ID | 主作者 GitHub ID,用于作者头像与链接 |
CODE_LICENSE_URL | 代码 MIT 协议地址 |
DOCS_LICENSE_URL | 文档 CC BY-NC-SA 4.0 协议地址 |
GISCUS_CONFIG | Giscus repo / repoId / category / categoryId |
GISCUS_THEME_PATHS | 自定义主题 CSS 的公共路径(/giscus-light.css、/giscus-dark.css) |
GISCUS_THEME_URLS | 生产环境主题 URL,使用 jsDelivr CDN 镜像保证跨域可访问 |
项目采用双协议:代码使用 MIT,文档内容使用 CC BY-NC-SA 4.0。协议地址集中在配置中,不在各页面重复硬编码。
Giscus 主题在生产环境使用 jsDelivr CDN 镜像(https://cdn.jsdelivr.net/gh/${repo}@main/public),因为静态托管平台不一定提供 CSS 文件的跨域响应头,而 Giscus iframe 需要跨域加载主题。
八、MDX Preview 与编辑器对齐
VS Code 的 MDX Preview 扩展无法自动识别项目自定义组件,只内置了 Docusaurus、Starlight、Nextra 与 Next.js 的 shim。项目通过三处配置让编辑器预览与构建管线对齐:
| 文件 | 作用 |
|---|---|
.mdx-previewrc.json | 注册 remarkPlugins: ["remark-math"] 与 rehypePlugins: ["rehype-katex"],并把自定义组件名映射到 shim |
src/components/mdx/mdx-preview-shims.tsx | 重新导出 DocCard、DocGrid、FeatureCard、LearningPath、ResourceLink、File、Files、Folder |
tsconfig.json 的 mdx.plugins | 配置 remark-math,让 vscode-mdx 语言服务能解析 LaTeX 表达式 |
这样作者在 VS Code 中预览 MDX 时,能看到与构建产物一致的数学公式渲染和自定义组件样式。新增站点 MDX 组件时必须同步维护这三处,否则预览会将其识别为未知组件。
九、部署偏斜守卫
src/components/deployment-skew-guard.tsx 监听 error 与 unhandledrejection 事件,检测部署偏斜错误:
ChunkLoadError、dynamically imported module、RSC payload等模块加载失败;/_next/static/路径下的 SCRIPT / LINK 资源加载失败。
检测到偏斜后,组件通过 __reload 参数强制刷新页面绕过缓存,并在 sessionStorage 中设置 30 秒冷却,避免刷新失败时进入无限重载循环。这是静态站点更新后旧页面引用失效的最后兜底。
十、路由加载场景
src/styles/loading.css 定义了 Next.js App Router 路由切换时的加载视觉:
- 视口宽数据流横幅,含 Orbitron 字体品牌标识、信号点、跑马灯文字与进度轨道;
route-loading-shell--handoff与--release类配合 App Router 的克隆与释放机制,避免两条独立跑马灯重影;- 移动端调整 band 高度与文字尺寸。
加载场景只在路由切换的短暂窗口出现,不影响首屏渲染。
十一、关键文件
| 文件 | 职责 |
|---|---|
source.config.ts | 内容源、Schema 与 MDX Pipeline 组装 |
src/content/schema/docs.ts | Frontmatter Schema |
src/content/plugins/ | Remark / Rehype、代码标题与图标配置 |
src/content/generated/manifest.ts | 从 Source 派生的类型化 Content Manifest |
next.config.ts | 生产静态导出、图片与开发响应配置 |
src/adapters/fumadocs/ | Fumadocs Source、Layout、TOC 与 DOM 适配 |
src/features/ | 七个成熟功能边界(各自公共入口 + 跨 Feature 许可清单) |
src/runtime/ | Navigation、Motion 与 Interaction 协调层 |
src/lib/i18n.ts | locale 单一来源 |
src/lib/site-config.ts | 仓库、作者、协议与 Giscus 配置 |
src/app/[lang]/docs/layout.tsx | locale 页面树与 DocsLayout |
src/app/[lang]/docs/[...slug]/page.tsx | 文档页面装配 |
src/app/[lang]/docs-source/[...slug]/route.ts | Markdown 静态源码端点 |
src/components/deployment-skew-guard.tsx | 部署偏斜检测与恢复 |
scripts/content-pipeline.ts | 内容管线:Content IR 消费、内容校验与 Mermaid 生成 / 对账 |
scripts/check-architecture.ts | 架构层依赖边界检查 |
scripts/verify-build-output.ts | 构建产物完整性校验 |