Repository Wiki
LyraVoid/Mizuki

目录组织与代码组织约定

Mizuki 是一个基于 Astro + Svelte + Tailwind CSS 的博客主题仓库(package.json 中 type: "module"、version: "9.0")。本页系统说明该仓库的目录划分、组件目录「三件套」约定、命名规则、scripts/ 构建流水线、tests/ 测试组织,以及由 CLAUDE.md 定义的代码行为约定,帮助贡献者快速定位代码位置并以统一方式扩展。

目的与范围

本页覆盖以下内容:

  • 仓库根目录的顶层文件职责与配置文件分布
  • src/ 下 assets/、components/、类型声明与内容集合配置的组织规则
  • 组件目录的标准结构(index.ts + types.ts + 组件本体)
  • 仓库中观察到的命名约定(PascalCase 与 kebab-case 的并存规则)
  • scripts/ 工程脚本目录与 package.json 驱动的构建流水线
  • tests/ 测试目录的组织与注册方式
  • CLAUDE.md 中定义的行为约定(思考先行、最简实现、外科手术式修改、安全基线)
  • 工具链约束(Biome 负责格式化与静态检查、pnpm 强制使用)

以下内容由兄弟页面承接,本页不展开:

  • 各组件的具体 Props、内部实现与渲染行为(参见对应组件的文档页)
  • src/content.config.ts 中内容集合(content collections)的数据模型细节
  • scripts/sync-content.js、scripts/update-anime.mjs 等脚本的内部实现逻辑
  • Astro / Svelte / Tailwind 的框架级配置项含义(astro.config.mjs 等的逐项解读)

概述

Mizuki 的代码组织遵循几个清晰的原则:

  1. 按职责分层的顶层目录:框架配置(astro.config.mjs、svelte.config.js 等)平铺在仓库根目录,源代码集中于 src/,工程脚本集中于 scripts/,测试集中于 tests/。
  2. 组件即目录(component-as-folder):每个原子组件拥有独立目录,目录内固定包含组件本体、index.ts 桶导出与 types.ts 类型定义三件套。
  3. 静态与交互分离:组件同时存在 .astro(服务端渲染/静态)与 .svelte(客户端交互岛屿)两种形态,按交互需求选择。
  4. 命令式流水线:构建、数据更新、产物校验全部通过 package.json scripts 显式编排,且测试文件以白名单方式逐一列举而非 glob 匹配。
  5. 行为约定文档化:CLAUDE.md 将 LLM/协作者的行为准则写入仓库,约束改动范围与实现复杂度。

架构总览

下图展示仓库的整体目录组织与关键依赖关系(依据仓库文件清单与 package.json 脚本引用整理):

Loading diagram...

图中关键关系说明:

  • package.json 是流水线的唯一入口:所有工程命令(内容同步、数据更新、构建校验、测试)都由根目录 package.json 的 scripts 字段显式编排,不依赖隐藏的任务运行器。
  • src/components/ 通过「目录 + 桶导出」向外暴露组件:消费方从 index.ts 导入,而不是直接深入组件文件路径,从而隔离内部文件结构的变更。
  • scripts/ 与 src/ 职责正交:src/ 是被 Astro 构建消费的源代码,scripts/ 是 Node 脚本(内容同步、番剧/Bangumi/Bilibili 数据更新、产物校验),二者互不嵌套。
  • 测试与源码分离:tests/ 位于仓库根目录,测试命令在 package.json 中逐文件列举,新增测试必须显式注册。

仓库顶层目录结构

Mizuki 仓库根目录的文件按职责分为四类(依据仓库文件清单):

路径类别职责
astro.config.mjs、svelte.config.js、postcss.config.mjs框架配置Astro / Svelte / PostCSS 的构建配置
biome.json代码质量配置Biome 格式化与静态检查规则
tsconfig.jsonTypeScript 配置编译与类型检查选项(type-check 脚本使用 tsc --noEmit)
pagefind.yml搜索配置Pagefind 站内搜索索引配置
pnpm-workspace.yaml包管理配置pnpm workspace 定义
package.json工程清单scripts 命令、依赖声明、packageManager: "pnpm@11.5.3"
CLAUDE.md行为约定LLM/协作者行为准则(详见下文「代码行为约定」)
README.md、README.en.md、README.ja.md、README.tw.md文档多语言项目说明
LICENSE、LICENSE.MIT、THIRD_PARTY_NOTICES.md法律许可证与第三方声明
_frontmatter.json内容元数据前置数据配置

设计意图:框架配置平铺在根目录而非 config/ 子目录,符合 Astro 项目的主流惯例,降低新贡献者的定位成本;法律文件与文档集中在根目录便于维护。

src/ 内部结构

src/ 顶层只保留少量全局文件,其余按子目录划分:

路径职责
src/content.config.tsAstro 内容集合(content collections)的 schema 与配置入口
src/global.d.ts、src/env.d.ts全局 TypeScript 类型声明(含 import.meta.env 等环境类型)
src/FooterConfig.html页脚 HTML 片段,独立于组件体系之外的全局注入点
src/assets/静态资源:字体(fonts/)、头像与音乐封面(images/、music/cover/)、横幅图(public/assets/)
src/components/UI 组件,按层级分层(atoms/ 等)

src/assets/ 中的资源按用途进一步细分(如 fonts/ 存放 loli.woff2、ZenMaruGothic-Medium.woff2 等字体;music/cover/ 存放音乐封面图),且同时保留 .ttf 与 .woff2 两种格式 —— 与 package.json 中 prepare-fonts / check-font-loading 脚本配套,构建时校验字体可用性。

组件三件套约定

src/components/atoms/ 下每个组件占据一个独立目录,目录内固定结构如下(以 Badge 为例):

text
1src/components/atoms/Badge/ 2├── Badge.svelte # 组件本体(交互岛屿,Svelte 实现) 3├── index.ts # 桶导出(barrel export) 4└── types.ts # 该组件的 Props/类型定义

Badge、Button、Chip、custom-scrollbar、filter-tabs、Icon、Image 孌套目录均严格遵循这一结构:index.ts 负责对外导出,types.ts 集中放置类型契约,组件本体为 .astro 或 .svelte。

这套约定的设计意图:

  1. 消费方解耦:外部代码从 atoms/Badge 导入即可,无需感知目录内文件是 .astro 还是 .svelte。当某个组件从静态 .astro 迁移为交互式 .svelte 时,导入路径不变,仅桶导出内容变化,改动被限制在组件目录内部。
  2. 类型与实现分离:types.ts 单独成文件,使得类型契约可以被其它模块独立引用(例如组件容器读取多个原子组件的 Props 类型做联合推导),也避免在 .astro 中维护复杂类型。
  3. 双形态并存:Icon 目录下同时存在 Icon.astro 与 LocalIcon.svelte,表明同一功能域内静态渲染与客户端交互可并存——静态优先(Astro islands 架构),仅在需要状态时才引入 Svelte 岛屿。

命名规则

仓库中存在两种命名风格并存的目录:

  • PascalCase 目录:Badge、Button、Chip、Icon、Image —— 组件目录与其组件文件同名(Badge/Badge.svelte)。
  • kebab-case 目录:custom-scrollbar(内部为 CustomScrollbar.astro)、filter-tabs(内部为 FilterTabs.astro) —— 目录 kebab-case、文件 PascalCase。

引入新组件时的实践建议:优先沿用 PascalCase 目录名(与目录内组件文件同名的样式,仓库中数量占优),若组件名含多个单词则目录可用 kebab-case,但内部文件保持 PascalCase。

构建与工程脚本流水线

package.json 的 scripts 是工程命令的唯一编排处。完整命令表如下:

命令类型作用
sync-content数据同步运行 scripts/sync-content.js,同步外部内容仓库
init-content初始化运行 scripts/init-content-repo.js,初始化内容仓库
export-config导出以 --experimental-transform-types 运行 scripts/export-config.mjs
predev / prebuild钩子自动运行 sync-content,后接 || true 保证内容同步失败不阻断流程
dev / start开发astro dev 启动开发服务器
check校验astro check 类型检查
update-anime / update-bangumi / update-bilibili数据更新拉取番剧 / Bangumi / Bilibili 数据
build构建完整流水线(见下文分解)
submitSEOscripts/indexnow-submit.js 提交 IndexNow
preview预览astro preview
type-check校验tsc --noEmit 纯类型检查
test测试node --experimental-strip-types --test 逐文件运行(见下文)
prepare-fonts / check-fonts / prepare-images资产准备字体与默认图片的预处理与校验
new-post内容创作scripts/new-post.js 新建文章
format / lint代码质量biome format --write ./src / biome check --write ./src
preinstall守卫npx only-allow pnpm 强制使用 pnpm

其中 build 命令是理解流水线的关键:

json
"build": "node scripts/update-anime.mjs && astro build && node scripts/check-global-style-loading.mjs && pagefind --site dist && node scripts/check-font-loading.mjs"

Source: package.json

这一行串联了五个阶段:数据更新 → Astro 构建 → 全局样式加载校验 → Pagefind 索引 → 字体加载校验。设计意图是把「产出正确性」的校验直接挂进构建链——任何一环失败(样式未加载、字体缺失)都会导致整体构建失败,避免发布损坏产物。

注意 predev / prebuild 中的容错写法:

json
"predev": "node scripts/sync-content.js || true", "prebuild": "node scripts/sync-content.js || true"

Source: package.json

|| true 使内容同步失败不阻断 dev/build —— 因为 Astro 构建在无内容时仍可产出主题骨架,内容仓库属于可缺省的外部依赖。

测试组织

测试文件位于仓库根目录 tests/,通过 test 命令显式列举运行:

json
"test": "node --experimental-strip-types --test tests/markdown-enhancements.test.mjs tests/layout-regressions.test.mjs tests/image-loading.test.mjs tests/music-player-loading.test.mjs && node tests/crypto.test.mjs"

Source: package.json

组织要点:

  • 使用 Node 内置 node:test 运行器(无 Vitest/Jest 等外部测试框架),--experimental-strip-types 允许在 .mjs 测试中直接使用 TypeScript 语法。
  • 白名单式注册:测试文件逐个列出而非 glob 匹配,新增测试文件必须手动加入 test 命令,避免误吞无关文件。
  • tests/crypto.test.mjs 单独用一条命令运行(&& 串联在前一条之后),暗示其运行方式与 node:test 批量运行存在差异。
  • 测试命名按「功能域 + regressions」划分(markdown-enhancements、layout-regressions、image-loading、music-player-loading),表明测试以回归防护为主。

代码行为约定(CLAUDE.md)

CLAUDE.md 是仓库中面向 LLM 与人类协作者的行为准则文档,共五条,直接约束代码组织与修改方式:

  1. 先思考再编码:不假设、不隐藏困惑、显式陈述假设;存在多种解释时全部列出而非默默选择其一;有更简单方案时主动提出。
  2. 最简优先:只写解决问题的最小代码,不做投机性抽象,不添加未被要求的功能或「灵活性」,不为不可能的场景写错误处理。文档中给出自检标准:「如果写了 200 行而 50 行够用,就重写」。
  3. 外科手术式修改:只改必须改的,只清理自己制造的垃圾;不「顺手改进」相邻代码、注释或格式;不重构没坏的东西;即使风格不同也匹配既有风格。判断标准是「每一行改动都应能追溯到用户请求」。
  4. 目标驱动执行:把任务转化为可验证目标(如「写测试 → 让它通过」),多步任务先列出每步的验证方式。
  5. 安全基线:所有密钥、token、密码、私钥、数据库连接串必须通过环境变量或 .env 管理,.env 必须加入 .gitignore;若确需在代码中包含敏感信息,必须先征得同意;发现已提交的敏感数据须立即上报以便轮换密钥并清理 Git 历史。

文档末尾给出效果度量:「diff 中不必要的改动更少、因过度复杂导致的重写更少、澄清问题发生在实现之前而非错误之后」。这解释了本仓库代码组织的一个深层意图——目录与命名约定的稳定性本身就是协作契约,任何偏离约定的「顺手改进」都被第 3 条显式禁止。

package.json 中对 src/ 限定的 format/lint 命令与该约定互相印证:

json
"format": "biome format --write ./src", "lint": "biome check --write ./src"

Source: package.json

格式化与静态检查只作用于 ./src,即源代码目录被定义为需要统一风格的边界;scripts/、tests/ 等工程代码不在 Biome 管辖范围内,保持了「框架源码严格规范、工程脚本宽松灵活」的分层治理。

工具链与包管理约定

  • 包管理器锁定:package.json 声明 "packageManager": "pnpm@11.5.3",并通过 preinstall 钩子 npx only-allow pnpm 强制所有贡献者使用 pnpm,防止 lockfile 漂移。
  • Biome 而非 ESLint/Prettier:格式化与 lint 由单一工具 Biome 承担(@biomejs/biome ^2.5.5 devDependency),规则配置在根目录 biome.json。
  • TypeScript 严格分离:check(astro check)与 type-check(tsc --noEmit)是两个独立命令,前者校验 .astro 内的类型,后者做纯 TS 源码检查。
  • 模块体系:"type": "module" 且工程脚本使用 .mjs 扩展名(update-anime.mjs、export-config.mjs),在 CommonJS 仍可能被 Node 解析的场景下显式声明 ESM。

失败模式与边界情况

依据 package.json 与 CLAUDE.md 可归纳出以下已显式处理的失败模式:

场景处理方式位置
内容仓库同步失败predev / prebuild 中 || true 吞掉非零退出码,dev/build 不被阻断package.json L9-L10
构建产物缺样式check-global-style-loading.mjs 校验失败使 build 整体失败package.json L17
字体加载失败check-font-loading.mjs 在 Pagefind 索引后校验,失败即中断发布package.json L17
使用非 pnpm 安装preinstall 钩子 only-allow pnpm 直接拒绝安装package.json L29
密钥泄漏CLAUDE.md 安全基线要求立即上报、轮换并清理 Git 历史CLAUDE.md §5
过度改动扩散CLAUDE.md 外科手术式修改条款限制改动半径CLAUDE.md §3

未显式处理、需贡献者自行注意的边界:

  • 测试白名单的遗漏风险:新增 tests/*.test.mjs 后若忘记加入 test 命令,该测试永远不会运行——CI 不会因此报错。这是白名单式注册的固有代价(换取对运行集合的完全控制)。
  • 命名风格漂移:PascalCase 与 kebab-case 组件目录并存,无自动化工具(Biome 规则未见涉及目录命名)约束新增目录的风格,依赖 review 把关。
  • scripts/ 与 tests/ 无 lint 管辖:Biome 只作用于 ./src,工程脚本与测试文件的格式不受约束。

扩展点

基于上述约定,向仓库添加新能力的标准路径:

  1. 新增原子组件:在 src/components/atoms/<Name>/ 下建立三件套(<Name>.astro 或 <Name>.svelte、index.ts、types.ts),消费方一律从 index.ts 导入。需要交互状态时用 .svelte,纯静态展示用 .astro。
  2. 新增测试:在 tests/ 下创建 <domain>.test.mjs,并必须手动加入 package.json 的 test 命令参数列表。
  3. 新增数据更新流程:在 scripts/ 下添加脚本,然后在 package.json scripts 中注册命令(如已有 update-bangumi 的模式)。
  4. 新增静态资源:按 src/assets/ 既有子目录分类(字体入 fonts/、图片入 images/、横幅入 public/assets/),涉及字体的改动需确认 check-font-loading 仍通过。
  5. 新增构建校验:在 build 命令的 && 链上追加 node scripts/<check>.mjs,保持「校验失败即构建失败」的语义。

相关链接

Sources

(2 files)