关于 codometer-configure
用于编写 Codometer 配置文件的 Skill,介绍输入集、排除项、输出格式、指标限值和自定义统计项的设置,包括声明计数与注释预算。
主要功能
- 01配置测量输入与排除项
- 02设置报告格式和目标路径
- 03定义指标限值与严重级别
- 04声明自定义计数项
- 05测量声明数量和注释预算
适用场景
- 01测量生成的构建输出
- 02在运行中设置代码库指标门槛
- 03跟踪指定声明或注释
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ organizzolini/ codebase --skill codometer-configure安装后在 Claude Code 中输入/codometer-configure确认已加载。
详细介绍
编写 codometer 配置
运行时通过 --config <path> 指定的路径读取 codometer.config.{ts,mts,cts,js,mjs,cjs,json,jsonc},且此选项为必填项——不会搜索配置文件,也没有默认文件。在此代码库中,工作区配置文件是 configuration/codometer.config.ts;项目自己的配置文件是其 codometer.config.ts。指定的文件绝不会与其他文件合并:合并后实际并未应用于此文件夹的限制,看起来会和已应用的限制完全一样。每个配置都必须设置 format 字段——代码中没有默认回退值——其他字段均为可选。
import { type CodometerConfiguration } from "@codometer/configuration";
const codometerConfiguration: CodometerConfiguration = {
format: "markdown",
exclude: ["notepads/**"],
excludeFrom: ["configuration/.codometerignore"],
defaultInput: "codebase",
limits: [{ metric: "typescript.interfaces", severity: "warn", value: 500 }],
inputs: [
{
analyses: ["size"],
include: ["dist/**/*.js", "!dist/**/*.map.js"],
name: "compiled",
},
],
outputs: [{ path: "README.md", type: "markdown" }],
};
export default codometerConfiguration;
输出路径相对于进程自身的工作目录解析,而不是相对于配置文件,因此,即使配置文件保存在 configuration/ 文件夹中,输出也仍会写入运行命令时所在的目录。配置文件始终作为普通对象读取——不支持将配置写成函数,因此单个文件无法独自描述工作区中的每个项目;对于包含多个项目的代码库,每个项目各自配置 codometer.config.ts,并在其中展开共享对象,就像此代码库自己的 configuration/codometer.config.ts 一样。
排除项
通过文件发现机制,.gitignore 已经生效,因此 exclude 和 excludeFrom 用于排除那些保留在代码库中、但并非手动编写的文件——锁文件、供应商打包文件、生成的文档:
| 选项 | 语法 | 用途 |
|---|---|---|
exclude | Glob | 少量内联指定的路径 |
excludeFrom | Gitignore 语法文件的路径 | 仓库更愿意保存在文件中的列表 |
重用现有忽略文件可能会带来问题,值得留意:大多数忽略文件都是为另一款工具的需求编写的。如果某个格式化工具不会处理 Markdown,.prettierignore 可能会排除所有 Markdown;若 codometer 使用同一个文件,所有 Markdown 指标都会被排除。通常更适合专门使用 .codometerignore,而不是借用其他工具的文件。
输入
输入是一组带名称、通过 glob 匹配的文件,分析会针对这组文件运行。被测量的目录始终是一个独立输入,名称为 codebase;inputs 用于声明其他输入——通常是被每个 .gitignore 排除的构建输出,因此必须通过输入而非排除项来声明,才能匹配到这些文件。
| 字段 | 必填 | 默认值 | 含义 |
|---|---|---|---|
name | 是 | — | 此输入的唯一名称 |
include | 是 | — | 用于添加文件的 Glob;至少有一个 Glob 必须添加文件,而不能只移除文件 |
exclude | 否 | 无 | 用于移除文件的 Glob |
analyses | 是 | — | "language"、"size" 或两者 |
compression | 否 | gzip | size 分析使用的 gzip、brotli 或 none |
directory | 否 | . | 此输入的 Glob 的起始位置,相对于进程的工作目录 |
include 中的 ! 前缀用于移除文件;数组中的所有否定项会合并为一个集合,应用于整个输入,因此无论数组如何重新排序,匹配结果都不会改变。exclude 不得带有 !——它本来就会移除文件,因此无须再进行否定。声明的输入不会参考忽略文件,这正是它能够匹配编译输出的原因。
将输入命名为 codebase 会替换内置的全树扫描,而不是在其旁边新增一个输入;系统只会读取其 compression 和 analyses,不会参考其 include/exclude,因为全树扫描是通过遍历忽略文件发现的,而非通过 Glob 匹配。若要测量目录树的子集,请使用其他名称声明输入。
限值
限值用于约束某项已测量指标的上限。任何指标都可以设置限值——压缩后的大小、行数、已声明计数器的计数都可以。没有限值的指标会像之前一样精确测量并报告,不受任何限制。
defaultInput: "codebase",
limits: [
{ metric: "compiled.size", value: "8 KB" },
{ label: "Interfaces", metric: "typescript.interfaces", value: 500 },
{ metric: "linesOfCode", severity: "warn", value: 100_000 },
],
| 字段 | 必填 | 默认值 | 含义 |
|---|---|---|---|
metric | 是 | — | 此限值约束的指标点分路径 |
value | 是 | — | 上限,可为数字或带单位的字符串 |
severity | 否 | fail | 超限时,fail 会停止运行;warn 只会报告 |
label | 否 | 路径 | 报告中对此限值的称呼 |
指标的寻址方式是输入名称加上该指标在输入中的路径。设置 defaultInput 后,未加前缀的路径会解析为该输入的指标,但仅当不存在同名输入时才会如此——如果某个输入和指标组可能重名,请始终写出完整的输入名称,因为遇到有歧义的路径会直接拒绝,绝不猜测。路径未命名任何指标,或命名的指标来自其输入未运行的分析时,也会同样被拒绝。
字符串值的单位采用十进制,且必须带有末尾的 b:"8 KB" 表示 8000 字节,"1 MB" 表示 1000000,而 "8 K" 会被拒绝,不会被解释为任何值。只有当限值针对某个未匹配到任何文件的输入时,才会导致运行失败——声明限值就表示断言这些文件应该存在。
**绝不要提高限值的 value 来让超限通过。**配置的限值是对代码库形态的承诺;在破坏这一承诺的更改中提高限值,会抹去限值原本要保留的证据。如果超限确实代表回归,应减少被测量的内容——精简指标、拆分文件、删除无用代码。如果限值本身不适合该指标今后的预期,这同样是值得审慎作出的决定,应当加以说明,而不是在导致超限的同一更改中悄悄调高数值。对于超限可能代表的其他情况,请使用 codometer-triage 技能。
自定义统计
顶层的 custom 数组用于测量计数器——无论输出是否渲染它,也无论在哪里渲染,在此处声明计数器就会计算它:
custom: [
{ label: "Service Files", patterns: ["**/*.service.ts"] },
{ label: "Unit Tests", patterns: ["**/*.unit.test.ts"] },
{ color: "16a34a", label: "Migrations", patterns: ["**/migrations/*.sql"] },
],
每个条目都会在所有选定它的地方渲染为一个徽章,顺序与配置中的顺序一致。color 是 shields.io 十六进制三元组;省略此项的条目会按组从内置调色板中依次取色,因此计数器的颜色在不同运行之间保持稳定。计数器通过 patterns、symbols 或 comment 三个字段之一来选择计数对象;未指定其中任何字段的条目会被拒绝,而不会作为永久为零的计数器报告。
outputs 条目中的 custom 数组是单独的后续配置:它按标签选择要由该目标渲染的顶层计数器——它本身绝不会声明计数器,因此 JSON 报告和 markdown 报告仍可以选择完全不同的标签:
outputs: [
{ custom: ["Service Files"], path: "codometer-report.json", type: "json" },
{ custom: ["Unit Tests"], path: "README.md", type: "markdown" },
],
如果某个输出选取的标签未在顶层 custom 中声明,配置加载时就会拒绝它——选择无法凭空生成从未测量过的计数器。custom 中声明但未被任何输出选取的计数器仍会被测量,也仍会用于限值判断;它只是不会在任何地方渲染。
统计声明
symbols 统计 TypeScript 和 JavaScript 源码中的声明,而不是文件:
custom: [
{ group: "typescript", label: "Static Methods", symbols: { kinds: ["method"], modifiers: ["static"] } },
],
kinds 是 class、enum、function、getter、interface、method、property、setter 中的一个或多个;modifiers 则筛选带有 abstract、async、export、override、private、protected、public、readonly、static 中所有指定修饰符的声明。两者都按语法原样读取——类成员属于 method,其他所有可调用对象(包括箭头函数)都属于 function;保存箭头函数的类字段属于 property,且不携带该字段自身的任何修饰符。
对符号计数器而言,patterns 用于筛选要搜索的文件,而不是指定要统计的内容。group 会将徽章放在指定组的内置徽章之后——默认是 conventions,也可以是某个内置语言组;组名若不在该范围内,配置会报错,而不会导致徽章不显示。
选择注释预算
comment 为计数器选择要衡量的注释预算,而不是统计文件或声明。现在已不再有独立的 comments/documentation 机制:注释预算是一种普通的自定义统计,而针对其 custom.<label> 指标的 limits[] 条目才会将计数变成门槛——这与其他计数器经历的整合过程相同。
custom: [
{ comment: { language: "yaml", maximumWords: 128 }, label: "YAML Comment Budget" },
{ comment: { kind: "class", maximumLines: 24 }, label: "Class Comment Budget" },
],
limits: [{ metric: "custom.YAML Comment Budget", value: 0 }],
| 字段 | 必填 | 默认值 | 含义 |
|---|---|---|---|
language | 否 | 所有含注释的语言 | 限定为 css、hcl、python、shell、sql、toml、typescript、yaml 之一 |
kind | 否 | 普通注释块 | 限定为此声明类型中有文档说明的声明所使用的 JSDoc 风格注释 |
maximumCharacters | 否 | — | 注释块可包含的字符数,包括标记和换行符 |
maximumLines | 否 | — | 注释块可跨越的行数 |
maximumWords | 否 | — | 去除标记后,注释块可包含的文字词数 |
severity | 否 | fail | fail 会在超限时停止运行;warn 只会报告超限 |
计数值是违反该选择器自身上限的注释块数量;其 instances 会列出每个超限注释块的 file 和从 1 开始计数的 line,以及测得的超限量。符合预算的注释块不会计入任何一项。
comment 选择器不继承任何配置。 没有供各语言条目合并继承的顶层默认值,因此“所有语言为 128 个词,但 shell 为 256 个词”需要为每种语言分别编写一个选择器,而不是设置共享预算并进行覆盖——本仓库自己的 configuration/codometer.config.ts 会从 CODOMETER_COMMENT_LANGUAGES 派生该枚举,并通过类型约束确保:如果在模式中新增语言,却未在本仓库的列表中涵盖它,就会产生编译错误。如果未更新该枚举以列出某种语言,codometer 就不会开始衡量该语言的注释,也不会对此设置门槛——在模式中添加语言不会自动为其设置门槛。
三个上限是独立字段,而不是由 unit 决定的单个 maximum,因为它们并非互斥选项。 一个注释块可能符合行数预算,却超出词数预算。未填写的字段不会进行衡量,而且任何字段都不会默认设置为某个数值——没有人写下的预算,就是无人选择的预算。
预算针对每个注释块,而非整个文件。 注释块是读者会当作一个完整想法来读的一组注释行:空行会结束一个注释块,跟在某个值后面的注释不会并入其上方的注释块,而 #! shebang 根本不算注释。comment 选择器没有对应的文件级配置——一个文件包含 40 个长度适中的注释,与一个文件包含一篇长篇注释并非同一个问题,但 codometer 目前无法衡量后一种情况。
如果 linter 已经将行数限制在某个列数以内,优先采用词数预算而非字符数预算:字符数只会重复格式化工具已有的限制,而词数预算衡量的是真正写在注释里的内容。JSDoc 默认不受门槛限制——comment 选择器配上 kind 可以覆盖它,但声明这样的选择器是有意为之,不会仅仅因为指定了语言就自动生效。
breach 的含义,以及它不代表什么
breach 会指出其文件和行号。请精简代码块,或将详细内容移到有足够篇幅的文档中——绝不要为了让运行通过而提高预算,理由与绝不提高限制的 value 相同。
在相信统计结果前,有两点准确性注意事项值得了解:
- 有些语言使用真正的解析器,有些则使用逐行扫描器。 CSS
(postcss)、Python(
tokenize)、YAML(其 CST)和 TypeScript/JavaScript(编译器的扫描器)都能区分字符串字面量中的注释标记和真正的注释。Shell、TOML、SQL 和 HCL 使用逐行扫描器,无法做到这一点——字符串中的#或--会被当作注释,正如这些语言现有的注释计数器所做的那样。HCL 是唯一使用三种语法测量的语言:#、//和/* */。 - Python 注释需要可用的解释器,与其他所有 Python 指标一样。没有可运行的解释器时,codometer 会将它们留作未测量,而不是错误计数。
如需了解如何处理 breach,请使用 codometer-triage 技能。
输出目标
outputs 是一个类型化目标数组——可以声明任意数量的目标,但每种类型最多只能有一个条目;配置中若指定了两个 json 输出或两个 markdown 输出,加载时会因类型重复而被拒绝,因为命令行上无法指定第二个输出。
outputs: [
{ indentation: 2, path: "output/codometer.json", type: "json" },
{
description: "Measured on every push.",
path: "README.md",
type: "markdown",
},
],
JSON 条目需要 path;indentation 默认为 2。markdown 条目需要 path、write 函数,或两者都提供——未指定其中任何一项的目标会在加载配置时被拒绝。
编写 markdown
write 包含全部可自定义行为——它既决定报告内容,也决定报告写入的位置,取代了过去分开的 render 和 write 两个回调:
{
path: "README.md",
type: "markdown",
write: ({ anchors, renderBadges, statistics }) =>
anchors.syncAnchoredBlock({
content: `Lines of code: ${statistics.linesOfCode}\n\n${renderBadges()}`,
}),
}
| 参数 | 含义 |
|---|---|
description | 配置的描述,供希望自行放置描述的 writer 使用 |
renderBadges() | 内置的徽章渲染功能,可用于生成默认内容或在默认内容上添加内容,这些徽章展示的是相同的统计数据 |
statistics | 测得的统计数据 |
check | 如果不允许写入且文件仅供检查,则为 True |
path | 配置的路径,相对于进程的工作目录解析 |
anchors | 标记机制——syncAnchoredBlock、wrapInAnchors、startMarker/endMarker——因此选择不同的文件时无需重新实现内容拼接 |
不设置 write 时,会保留内置的渲染和写入行为。write
函数返回 false 表示其目标已过期,这会导致 --check reports 运行失败;返回其他任何值都表示目标为最新状态。
编写配置时称为输入,报告中称为目标
inputs 是配置中声明的内容——这次重新设计将编写配置时使用的 targets 重命名为
inputs,并将 defaultTarget 重命名为 defaultInput——但测量后的 JSON 报告仍保留原来的术语:CodometerReport.targets,指标仍通过 codebase.typescript.interfaces 或
Compiled JavaScript.size 来引用,输入的名称对应报告中的目标。此区分是有意为之,并非术语不一致;保留它是因为 CodometerReport.targets 早于此次重新设计,且没有下游读取报告的内容需要更改;Measured Targets 徽章组也有自己的代码注释,解释了为何保留此名称。在配置中使用 inputs;在报告、configuration --limits 列表或徽章组标题中使用 targets。