Files
gitea-github-theme/ARCHITECTURE_CN.md
T
2026-06-06 22:42:49 +08:00

31 KiB
Raw Blame History

架构设计:Gitea GitHub Theme

项目概述

Gitea GitHub Theme 是一个构建时 CSS 生成框架,为 Gitea 自托管代码平台产出静态 .css 主题文件。项目以设计令牌和颜色定义为输入,通过分层管道转换,最终输出 30+ 个压缩后的 CSS 文件,覆盖 7 个主题系列(default、colorblind、tritanopia、pink、gitea、catppuccin、high-contrast)。

核心原则: 开发时享受完整的类型安全和 CSS-in-JS 开发体验,浏览器端交付纯静态 CSS,零运行时开销。


架构分层

┌──────────────────────────────────────────────────────────────┐
│                    主题配置层                                  │
│  theme.config.ts: 声明主题系列、颜色类型、显示名称、无障碍元信息 │
└──────────────────────────┬───────────────────────────────────┘
                           │
┌──────────────────────────▼───────────────────────────────────┐
│                    颜色定义层                                  │
│  ┌──────────┐  ┌──────────────┐  ┌───────────────────┐      │
│  │ primer/  │  │  themes/     │  │ themes/catppuccin  │      │
│  │ (自动生成│  │  github.ts   │  │ .ts               │      │
│  │ 的 Primer│  │  gitea*.ts   │  │                    │      │
│  │ 设计令牌)│  │              │  │                    │      │
│  └────┬─────┘  └──────┬───────┘  └─────────┬─────────┘      │
│       │               │                    │                 │
│  primer2GitHubColor   defineTheme   catppuccin2ThemeColor    │
│       │               │                    │                 │
│       ▼               ▼                    ▼                 │
│  GitHubColor      Theme             ThemeColor               │
│                   (colorType:                                │
│                    "github")                                 │
└──────────────────────────┬───────────────────────────────────┘
                           │
┌──────────────────────────▼───────────────────────────────────┐
│                    颜色转换层                                  │
│  src/palette/                                                │
│  ┌────────────┐  ┌────────────┐  ┌──────────────────┐      │
│  │ github.ts  │  │ theme.ts   │  │ gitea.ts         │      │
│  │ GitHubColor│  │ ThemeColor │  │ GiteaColor       │      │
│  │    →       │  │    →       │  │    →             │      │
│  │ ThemeColor │  │ ThemeVars  │  │ ThemeVars        │      │
│  └────────────┘  └────────────┘  └──────────────────┘      │
│                                                              │
│  统一输出: ThemeVars (CSS 变量的扁平键值映射)                   │
└──────────────────────────┬───────────────────────────────────┘
                           │
┌──────────────────────────▼───────────────────────────────────┐
│                    核心框架层                                  │
│  src/core/                                                   │
│  ┌──────────────┐  ┌──────────┐  ┌───────────────────┐      │
│  │ theme.ts     │  │ css.ts   │  │ chroma.ts         │      │
│  │ createTheme()│  │ css()    │  │ codemirror.ts     │      │
│  │ defineTheme()│  │cssCombine│  │ meta.ts           │      │
│  └──────┬───────┘  │cssStyle()│  └───────────────────┘      │
│         │          └────┬─────┘                              │
│         │               │                                    │
│  vanilla-extract 适配器 (src/vanilla-extract.ts)              │
│  ┌──────────────────────────────────────────────────┐       │
│  │  setAdapter() → 拦截 appendCss,                  │       │
│  │  registerClassName, registerComposition          │       │
│  │  → 在构建时将全部 CSS 序列化为字符串              │       │
│  └──────────────────────────────────────────────────┘       │
└──────────────────────────┬───────────────────────────────────┘
                           │
┌──────────────────────────▼───────────────────────────────────┐
│                    样式覆写层                                  │
│  styles/                                                     │
│  ┌──────────┐  ┌──────────────┐  ┌──────────────────┐      │
│  │ public/  │  │ components/  │  │ templates/       │      │
│  │ (基础    │  │ (页面级别    │  │ (模板专属样式)    │      │
│  │  元素)   │  │  样式)       │  │                  │      │
│  └──────────┘  └──────────────┘  └──────────────────┘      │
│  common/ (可复用 CSS 片段: 按钮、标签、动画等)                │
│                                                              │
│  每个文件使用 css`...` 模板字面量                              │
│  → 在导出边界用 cssCombine() 拼接 → 单个 CSS 字符串            │
└──────────────────────────┬───────────────────────────────────┘
                           │
┌──────────────────────────▼───────────────────────────────────┐
│                    构建管道                                    │
│  src/vite-plugin/index.ts (Vite 插件)                        │
│                                                              │
│  config() 钩子:                                              │
│    1. 动态导入 theme.config.ts                                │
│    2. 解析所有主题条目 + 生成 auto 自动主题                    │
│                                                              │
│  generateBundle() 钩子:                                      │
│    1. 导入 styles/ → CSS 字符串 (public + components)        │
│    2. 对每个主题条目:                                         │
│       a. compileCSS(createThemeMetaInfo + createTheme)       │
│       b. lightningcss.transform() → 压缩                     │
│       c. 追加样式覆写 (仅非 auto 主题)                        │
│       d. emitFile 输出 .css 资源                             │
│    3. 对 auto 主题: 生成 @import 声明                         │
└──────────────────────────┬───────────────────────────────────┘
                           │
                           ▼
                    dist/theme-github-*.css

核心设计模式

1. Vanilla-Extract 适配器劫持

解决的问题: Vanilla-extract 是一个零运行时 CSS-in-JS 库,在构建时通过其 Vite/esbuild 插件生成静态 CSS 文件。但 Gitea 主题需要单文件 CSS 输出,并且需要对主题生成过程进行编程控制(每个主题一个输出文件、自动生成 auto 主题等)。

方案: 绕过 vanilla-extract 的默认插件,直接使用其底层适配器 API。

在 src/vanilla-extract.ts 中:

export function compileCSS(compileFn: () => void): string {
  // 1. 安装自定义适配器,将 CSS 对象捕获到数组
  setAdapter({
    appendCss: css => capturedCssObjs.push(css),
    registerClassName: className => localClassNames.add(className),
    registerComposition: composition => composedClassLists.push(composition),
    // ... 其他回调实现为空操作
  });

  // 2. 设置文件作用域用于调试
  setFileScope(getCallerInfo(1));

  // 3. 执行调用方的 vanilla-extract API 调用
  compileFn();

  // 4. 拆除适配器
  endFileScope();
  removeAdapter();

  // 5. 将捕获的 CSS 对象序列化为字符串
  return transformCss({
    localClassNames: Array.from(localClassNames),
    composedClassLists,
    cssObjs: capturedCssObjs,
  }).join("\n");
}

工作原理:

  1. compileCSS 包装一个同步函数,该函数内部调用 vanilla-extract API(createGlobalTheme、globalStyle、globalKeyframes 等)
  2. 自定义适配器拦截所有 CSS 生成调用,将它们捕获到内存数组
  3. 函数执行完毕后,transformCss() 将捕获的对象序列化为 CSS 字符串
  4. 适配器被拆除,不留下任何全局副作用

安全性保证: 每次 compileCSS 调用完全自包含:安装 → 捕获 → 序列化 → 拆除。调用之间无状态泄漏。

关键洞察: Vanilla-extract 的 createGlobalThemeContract + createGlobalTheme 完全适配此模式——它们仅在 createGlobalTheme 调用期间产生 CSS,而该调用恰好发生在我们的捕获范围内。

2. 三条颜色输入管道

解决的问题: 不同的颜色主题来源有不同的数据结构。Primer 设计令牌(来自 GitHub)有数百个嵌套键。Catppuccin 有自己的调色板结构。Gitea 的原生格式使用扁平结构。需要同时支持这三种。

方案: 三条转换路径,全部汇聚到 ThemeVars:

Primer 令牌 ──→ GitHubColor ──→ ThemeColor ──→ ThemeVars
Catppuccin flavor ────────────→ ThemeColor ──→ ThemeVars
GiteaColor ──────────────────────────────────→ ThemeVars

每条路径有不同的复杂度要求:

路径 适用场景 复杂度 示例
github 完整 Primer 令牌合规 高(数百个映射键) Default、色盲、高对比度主题
theme 15 个基础色定义自定义主题 低(自动派生 200+ CSS 变量) Catppuccin、社区主题
gitea Gitea 原生颜色格式 中(直接映射) Gitea 兼容主题

设计理由: theme 路径是"入门路径"——贡献者只需定义 15 个语义色(red、orange、yellow、……、white)加上 primary 和 secondary。框架自动计算:

  • 7 级亮度变体(dark-1 到 dark-7、light-1 到 light-7)
  • 9 级透明度变体(alpha-10 到 alpha-90)
  • Badge 颜色及其 hover 状态
  • 消息颜色(error/success/warning/info)
  • ANSI 控制台颜色
  • 方向感知(暗/亮主题下的缩放方向反转)

实现在 src/palette/theme.ts 的 theme2ThemeVars() 函数中。

3. CSS 变量命名约定

解决的问题: Gitea 期望特定命名模式的 CSS 变量(--color-primary-dark-1、--color-red-badge-bg 等)。我们在 TypeScript 中需要类型安全的访问方式(themeVars.color.primary.dark.num1)。

方案: 使用 vanilla-extract 的 createGlobalThemeContract 配合自定义 varMapper。

在 src/types/vars.ts 中:

function varMapper(prefix: string | null = null) {
  return (value: string | null, path: string[]) => {
    if (value === null) {
      // 从路径自动生成名称: ["color","primary","dark","num1"] → "color-primary-dark-1"
      path = path.filter(item => item !== "self"); // 移除 "self" 键
      path = path.map(item => item.replace(/^num/, "")); // 去掉 "num" 前缀
      value = path.join("-");
    }
    if (prefix) value = `${prefix}-${value}`;
    return value;
  };
}

由此创建了双向映射:

TypeScript 访问 CSS 变量
themeVars.color.primary.self --color-primary
themeVars.color.primary.dark.num1 --color-primary-dark-1
themeVars.github.button.primary.bgColor.rest --github-button-primary-bgColor-rest
syntaxVars.keyword --color-syntax-keyword
customThemeVars.cloneMenuWidth --custom-clone-menu-width

4. 构建时 CSS 语法校验

解决的问题: 模板字面量中的 CSS 可能包含语法错误,这些错误只有到浏览器运行时才会显现。

方案: css() 模板标签在构建时使用 lightningcss 校验 CSS 语法。

在 src/core/css.ts 中:

export function css(strings: TemplateStringsArray, ...values: CSSInterpolation[]): CSSString {
  const result = strings.reduce((acc, s, i) => acc + s + String(values[i] ?? ""), "");
  // 构建时校验: lightningcss 解析并报告语法错误
  transform({ filename: getCallerInfo(1), code: Buffer.from(result) });
  return result as CSSString;
}

如果开发者编写了无效 CSS,构建会立即失败,并附带指向问题模板字面量的 文件:行号 信息。这能捕捉到原本只能通过浏览器 DevTools 才能发现的错误。

权衡: 每次 css 模板调用都会执行一次 transform()。对于约 60 个样式文件,这会为初始构建增加约 200ms(一次性开销,后续由 Bun 的模块缓存加速)。


Vite 构建管道

构建由自定义 Vite 插件在 src/vite-plugin/index.ts 中编排:

阶段一:配置解析(config 钩子)

  1. 通过 Bun 原生 TypeScript + package exports 自引用(@lutinglt/gitea-github-theme/theme.config.ts)动态导入 theme.config.ts
  2. 遍历每个主题系列,为每个主题变体生成条目(dark、light、soft-dark、frappe、macchiato 等)
  3. 当系列同时具有 dark 和 light 变体时,自动生成 auto 主题条目
  4. 创建占位入口文件(Vite 需要至少一个输入)

阶段二:CSS 生成(generateBundle 钩子)

对每个 非 auto 主题:

  1. 调用 compileCSS(),传入一个函数,该函数内部:
    • 创建 Gitea 主题元信息(createThemeMetaInfo)——设置 --theme-color-scheme、--theme-display-name、--theme-colorblind-type
    • 创建主题 CSS 变量(createTheme)——生成所有 --color-* 和 --github-* 变量、全局样式、关键帧动画、代码高亮
  2. 捕获的 CSS 字符串通过 lightningcss.transform() 进行压缩
  3. 追加公共样式覆写(styles/)

对每个 auto 主题:

  1. 生成 @import 声明,配合 prefers-color-scheme 媒体查询,引用对应的暗/亮主题文件
  2. 包含主题元信息,供 Gitea 识别为自动颜色主题

CSS 输出结构

theme-github-dark.css:
  ├── @keyframes (overlayAppear, overlayAppearDown, overlayAppearUp)
  ├── gitea-theme-meta-info { --theme-color-scheme: dark; ... }
  ├── :root { --theme-version: "1.26.2.260605"; --is-dark-theme: true; ... }
  ├── :root { --color-primary: #4493f8; --color-primary-contrast: ... }
  ├── ... (200+ 个 CSS 变量声明)
  ├── .chroma { ... } (代码语法高亮)
  ├── .code-editor-container { ... } (CodeMirror 高亮)
  └── <公共样式覆写> (~60KB 单行压缩)

theme-github-auto.css:
  ├── @import "./theme-github-dark.css" (prefers-color-scheme: dark);
  ├── @import "./theme-github-light.css" (prefers-color-scheme: light);
  └── gitea-theme-meta-info { --theme-color-scheme: auto; ... }

颜色系统深入

颜色缩放计算

src/functions/color.ts 中的 scaleColorLight() 实现了 Sass color.scale() 算法:

变暗:  newL = currentL × (1 + scale/100)    [scale < 0]
变亮:  newL = currentL + (1 - currentL) × (scale/100)  [scale > 0]

这是比例缩放,并非线性偏移。其意义在于:

  • color.scale(#000000, $lightness: 50%) → 50% 灰色(而非"比黑色暗 50%",那仍然是黑色)
  • color.scale(#ffffff, $lightness: -50%) → 50% 灰色(而非"比白色亮 50%",那仍然是白色)
  • 比例方法确保在亮度两端都能产生视觉上有意义的变化

ThemeColor → ThemeVars 派生

src/palette/theme.ts 中的 theme2ThemeVars() 接收 ThemeColor(15 个基础色 + 元数据),派生完整的 ThemeVars(200+ 个 CSS 变量值):

  1. 主色阶(Primary scale):7 个暗色变体、7 个亮色变体、9 个透明度变体、hover/active 状态——全部从一个 hex 颜色生成
  2. 副色阶(Secondary scale):13 个暗色变体、4 个亮色变体、9 个透明度变体、按钮 hover/active 状态
  3. 命名颜色(15 个基础色中的 13 个):每个生成 self/light/dark-1/dark-2/badge-self/badge-bg/badge-hover-bg
  4. 语义消息色:bg/border/text,配合 rgba 透明度和饱和度调整
  5. ANSI 颜色:从命名色映射的 16 色调色板
  6. 方向感知:isDarkTheme 标志反转缩放方向——暗色主题中"dark"变体走向更亮的方向(为保持对比度)

样式覆写策略

选择器优先级层级

样式覆写使用 Gitea 现有的 DOM 选择器。覆写策略遵循优先级阶梯:

  1. 颜色变量——所有颜色引用主题中的 CSS 变量(绝不硬编码 hex 值)
  2. 元素级覆写——基础元素如 .ui.button、#navbar、input
  3. 组件级覆写——页面特定区域:heatmap、issues、actions、仓库页面
  4. !important 逃离——仅在 Gitea 通过内联 style 属性设置颜色时使用(如热力图贡献方块)

cssCombine() 显式组合

样式通过 cssCombine() 调用树组合在一起:

styles/index.ts
  ├── public/index.ts    (基础元素、按钮、输入框、下拉菜单、菜单、表格)
  ├── components/index.ts (导航栏、热力图、议题、仓库文件、Actions 等)
  └── templates/index.ts  (模板特定样式)

每个叶子模块导出 default cssCombine(...),将 CSS 片段串联在一起。这种显式组合使依赖关系图可见,避免了 CSS @import 或 @use 的隐式排序问题。

cssStyle() 可复用片段

src/core/css.ts 中的 cssStyle() 允许提取可复用的 CSS 属性集,同时保留 vanilla-extract 的类型检查:

// 在 styles/common/button.ts 中:
export const primaryButtonStyle = cssStyle({
  color: themeVars.github.button.primary.fgColor.rest,
  backgroundColor: themeVars.github.button.primary.bgColor.rest,
  borderColor: themeVars.github.button.primary.borderColor.rest,
  boxShadow: themeVars.github.shadow.resting.small,
});

// 在 styles/public/button.ts 中:
const baseButton = css`
  .ui.primary.button {
    ${primaryButtonStyle}
  }
`;

这在最终输出静态 CSS 字符串的系统中保留了类型安全(vanilla-extract 校验属性名和值类型)。


目录结构

├── theme.config.ts         # 主题系列声明(框架入口点)
├── src/
│   ├── index.ts            # 公共 API 导出
│   ├── core/               # 核心框架
│   │   ├── theme.ts        # createTheme、defineTheme(颜色 → CSS 变量)
│   │   ├── css.ts          # css()、cssCombine()、cssStyle()
│   │   ├── chroma.ts       # Chroma(服务端)语法高亮
│   │   ├── codemirror.ts   # CodeMirror(客户端)语法高亮
│   │   ├── meta.ts         # Gitea 主题元信息(color scheme, display name)
│   │   └── types.d.ts      # MapLeafNodes、CSSVarFunction、Tokens
│   ├── palette/            # 颜色转换与变换
│   │   ├── theme.ts        # ThemeColor → ThemeVars(简化 API)
│   │   ├── github.ts       # GitHubColor → ThemeColor(Primer 路径)
│   │   ├── gitea.ts        # GiteaColor → ThemeVars(原生路径)
│   │   ├── catppuccin.ts   # CatppuccinFlavor → ThemeColor
│   │   ├── primer.ts       # Primer 令牌 → GitHubColor / Chroma / CodeMirror
│   │   ├── display.ts      # Display color scale → GitHubColor 覆写
│   │   └── catppuccinSyntax.ts  # Catppuccin 语法高亮
│   ├── functions/          # 工具函数
│   │   ├── color.ts         # scaleColorLight(Sass color.scale)+ rgba 转换
│   │   └── var.ts          # extractVarName(CSS 变量名提取)
│   ├── types/              # 类型定义与 CSS 变量契约
│   │   ├── color/          # 颜色结构定义(primary, secondary, named, 等)
│   │   └── vars.ts         # createGlobalThemeContract + varMapper
│   ├── vite-plugin/        # 构建工具
│   │   ├── index.ts        # 主 Vite 插件
│   │   ├── themeConfig.ts  # defineThemeConfig、TypeScript 类型
│   │   └── utils.ts        # buildFullThemeName、buildFullDisplayName
│   ├── vanilla-extract.ts  # 适配器劫持(compileCSS、captureStyle)
│   ├── selectors.ts        # 共享 CSS 选择器(root、chroma、codeMirror、emoji)
│   └── utils.ts            # deepOverride、getCallerInfo
├── styles/                 # CSS 样式覆写
│   ├── common/             # 可复用 CSS 片段(按钮、标签、动画)
│   ├── public/             # 基础元素(按钮、输入框、下拉菜单、菜单、表格)
│   ├── components/         # 页面特定样式(导航栏、热力图、仓库、议题、Actions)
│   └── templates/          # 模板特定样式
├── themes/                 # 颜色主题定义
│   ├── github.ts           # GitHub 主题颜色(来自 Primer 令牌)
│   ├── giteaDark.ts        # Gitea 暗色主题颜色(原生格式)
│   ├── giteaLight.ts       # Gitea 亮色主题颜色(原生格式)
│   └── catppuccin.ts       # Catppuccin 主题颜色
├── primer/                 # 自动生成的 Primer 设计令牌(来自 @primer/primitives)
├── scripts/                # 构建与工具脚本
│   ├── buildPrimerTSToken.ts  # 从 @primer/primitives 生成 primer/ TypeScript 令牌
│   ├── syncTheme.ts        # 同步构建产物到远程 Gitea 实例
│   ├── updateGitea.ts      # 更新 Gitea 参考文件
│   ├── translate.ts        # 生成本地化/翻译文件
│   └── version.ts          # 版本管理
├── dist/                   # 构建产物(30+ CSS 文件)
└── templates/              # 可选的 Gitea Go 模板覆写

关键设计决策与权衡

决策一:Vanilla-Extract 仅用于 CSS 编写,不用于运行时

选择: 使用 vanilla-extract 的 createGlobalTheme/createGlobalThemeContract 进行类型安全的 CSS 变量生成,但通过适配器劫持在构建时序列化为静态 CSS 字符串。

放弃: 使用 vanilla-extract 内置 Vite 插件(生成按模块拆分的 CSS 文件,不适合单文件主题输出)。

权衡: 适配器 API 无官方文档,可能在 vanilla-extract 版本间变化。通过锁定 @vanilla-extract/css 版本并使用快照测试检测 CSS 输出变化来缓解风险。

决策二:单文件 CSS 输出

选择: 每个主题是包含所有变量和所有样式覆写的单个 CSS 文件。

放弃: 通过 @import 链拆分为多个 CSS 文件(Gitea 主题系统每个主题只加载一个 CSS 文件)。

权衡: 样式覆写在所有主题文件中重复嵌入(约 60KB × 30 个主题 ≈ 1.8MB 冗余)。这是 Gitea 的约束而非设计选择。替代方案(独立 common.css)需要 Gitea 支持多文件主题加载。

决策三:LightningCSS 同时用于语法校验和输出压缩

选择: 使用 LightningCSS 进行构建时 CSS 语法校验(在 css() 模板标签中)和输出压缩(在 Vite 插件中)。

放弃: PostCSS(更慢)、esbuild CSS(功能不够完整)、完全不校验。

权衡: LightningCSS 较宽容——它会从许多语法错误中恢复而非抛出异常。这意味着 css() 中的"校验"能捕获严重错误,但可能静默接受某些边缘情况。构建产物快照测试部分补偿了这个不足。

决策四:仅支持 Bun 运行时

选择: 要求 Bun ≥ 1.3.14 用于开发和构建。

放弃: 跨运行时支持(Node + Bun)。

权衡: 限制了贡献者环境。收益:原生 TypeScript 执行(无 ts-node/esbuild 开销)、一致的堆栈追踪格式(用于调试工具)、更快的构建速度(Bun 原生打包器)。

决策五:包自引用用于内部导入

选择: 即使在同一个包内也使用 @lutinglt/gitea-github-theme/core 风格的导入,通过 package.json 中的 exports 映射实现。

放弃: 全程使用相对路径导入。

权衡: 需要 tsconfig.json 中的 paths 映射支持 IDE。收益:无论作为依赖使用还是本地开发,导入路径完全一致;src/、styles/、themes/ 三个目录之间没有路径混淆。


添加新主题

快速路径(15 个颜色定义)

  1. 创建 themes/my-theme.ts:
import { defineTheme } from "@lutinglt/gitea-github-theme/core";
import type { ThemeColor, Syntax } from "@lutinglt/gitea-github-theme/core";

const themeColor: ThemeColor = {
  isDarkTheme: true,
  primary: "#8b5cf6", // 强调色(紫色)
  primaryContrast: "#ffffff",
  secondary: "#27272a", // 边框色
  base: {
    red: "#ef4444",
    orange: "#f97316",
    yellow: "#eab308",
    olive: "#84cc16",
    green: "#22c55e",
    teal: "#14b8a6",
    cyan: "#06b6d4",
    blue: "#3b82f6",
    violet: "#8b5cf6",
    purple: "#a855f7",
    pink: "#ec4899",
    brown: "#a16207",
    black: "#18181b",
    grey: "#71717a",
    gold: "#eab308",
    white: "#fafafa",
  },
  console: {
    /* Actions 日志颜色 */
  },
  diff: {
    /* 代码差异颜色 */
  },
  other: {
    /* body、text、input、shadow 等 */
  },
  github: {
    /* GitHub 专有变量 */
  },
};

const syntaxColor: Syntax = {
  /* 代码高亮色 */
};

export default defineTheme({ colorType: "theme", themeColor, syntaxColor });
  1. 在 theme.config.ts 中注册:
{
  themeSeriesName: "my-theme",
  themes: {
    dark: { theme: myDarkTheme },
    // 可选: 添加 light 变体后自动生成 auto 主题
    light: { theme: myLightTheme },
  },
}
  1. 运行 bun bundle——主题 CSS 将出现在 dist/ 目录中。

完整路径(Primer 令牌)

如需像素级精准的 GitHub 颜色匹配,使用 github 路径直接映射 Primer 设计令牌。完整映射参考见 themes/github.ts 和 src/palette/github.ts。


测试策略

层级 测试类型 位置 验证内容
工具函数 单元测试 src/utils.test.ts deepOverride 行为、getCallerInfo 堆栈解析
计算函数 单元测试 src/functions/*.test.ts scaleColorLight 数值计算、extractVarName 解析
核心框架 单元测试 src/core/css.test.ts css() 校验、cssCombine() 组合、cssStyle() 序列化
调色板 快照测试 src/palette/*.test.ts 完整颜色转换链路(Primer → ThemeColor → ThemeVars、Catppuccin → ThemeColor → ThemeVars 等)
调色板 结构测试 src/palette/github.test.ts 颜色值合法性(所有叶子节点都是有效 CSS 颜色字符串)、必需键存在
构建产物 集成测试 src/build-output.test.ts 预期文件存在、CSS 内容有效性(变量存在、无调试残留、hex 小写、color-scheme 匹配)、文件体积上限

相关文档