codometer-configure

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Writing a codometer configuration

编写 codometer 配置

codometer.config.{ts,mts,cts,js,mjs,cjs,json,jsonc}
is read from the directory being measured, or from any directory above it — the search walks upward and takes the first file found. The nearest configuration wins outright, and is never merged with one further up: a merged limit that never applied to this folder would look exactly like one that did.
format
is the one field every configuration must set — there is no code-level fallback for it — and every other field is optional.
ts
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;
Output paths resolve relative to the process's own working directory, not to the configuration file, so a configuration kept in a
configuration/
folder still writes to wherever the run was invoked from. A configuration file is always read as a plain object — there is no config-as-function escape hatch, so one file cannot describe every project in a workspace on its own; a repository with many projects gives each its own
codometer.config.ts
and spreads a shared object across them, the way this repository's own
configuration/codometer.config.ts
does.
codometer.config.{ts,mts,cts,js,mjs,cjs,json,jsonc}
会从被测目录或其任意上级目录读取;查找会逐级向上,并采用找到的第一个文件。最近的配置直接胜出,绝不会与更上层的配置合并:一个原本不适用于当前文件夹的合并后
limit
,看起来会与确实适用于此文件夹的
limit
完全一样。
format
是每份配置都必须设置的字段——代码层面没有 fallback——其他所有字段均为可选。
ts
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/
文件夹中的配置仍会写入运行命令时所在的位置。配置文件始终作为普通对象读取——没有将配置写成函数的变通机制——所以单个文件无法独立描述 workspace 中的每个项目;包含多个项目的仓库应为每个项目提供各自的
codometer.config.ts
,并像本仓库自己的
configuration/codometer.config.ts
那样,在这些项目中展开同一个共享对象。

Exclusions

排除项

.gitignore
is already in force through discovery, so
exclude
and
excludeFrom
are for files that are kept but that nobody wrote by hand — lockfiles, vendored bundles, generated documentation:
OptionSyntaxUse it for
exclude
GlobsA handful of paths named inline
excludeFrom
Paths to gitignore-syntax filesA list a repository would rather keep in a file
Reusing an existing ignore file is a trap worth knowing about: most are written for one other tool's concerns. A
.prettierignore
that excludes all markdown because a formatter doesn't touch it will erase every markdown metric if codometer is pointed at the same file. A dedicated
.codometerignore
is usually the right answer instead of reaching for a file another tool owns.
在文件发现过程中,
.gitignore
已经生效,因此
exclude
和
excludeFrom
适用于那些会被保留、但并非人工编写的文件,例如 lockfile、vendored bundle 和生成的文档:
选项语法用途
exclude
Globs直接列出少量路径
excludeFrom
采用 gitignore 语法的文件路径存放仓库更希望保留在独立文件中的排除列表
复用现有 ignore 文件是一个值得注意的陷阱:大多数此类文件都是为另一个工具的特定需求编写的。某个
.prettierignore
可能因为 formatter 不会处理 Markdown 而排除所有 Markdown;如果将 codometer 指向同一个文件,它会清除所有 Markdown 指标。通常,专用
.codometerignore
才是正确选择,而不是使用另一个工具所拥有的文件。

Inputs

输入

An input is a named set of files matched by glob, with the analyses run over it. The measured directory is always an input of its own, named
codebase
;
inputs
declares the others — most often build output that every
.gitignore
excludes, and that therefore needs an input rather than an exclusion to reach at all.
FieldRequiredDefaultMeaning
name
yes—Unique name for this input
include
yes—Globs that add files; at least one must add rather than only remove
exclude
nononeGlobs that remove files
analyses
yes—
"language"
,
"size"
, or both
compression
no
gzip
gzip
,
brotli
, or
none
, for
size
analysis
directory
no
.
Where this input's globs start, relative to the process's working directory
A
!
prefix in
include
removes files; every negation across the array forms one set applied to the whole input, so reordering the array can never change what it matches.
exclude
may not carry a
!
— it already removes, so there is nothing left to negate there. Ignore files are not consulted for a declared input, which is exactly what lets one match compiled output at all.
Naming an input
codebase
replaces the built-in whole-tree scan rather than adding beside it, and only its
compression
and
analyses
are read — its
include
/
exclude
are not consulted, because the whole-tree scan is discovered by walking ignore files rather than by matching globs. To measure a subset of the tree, declare an input under some other name instead.
input 是由 glob 匹配的一组有名称的文件,并针对其运行各项分析。被测目录始终将自身作为一个 input,名称为
codebase
;
inputs
用于声明其他 input——最常见的是构建输出,因为所有
.gitignore
都会排除这些文件,所以必须通过 input 而非排除项才能访问它们。
字段必填默认值含义
name
是—此 input 的唯一名称
include
是—添加文件的 Globs;至少有一个必须用于添加文件,不能全部只用于移除文件
exclude
否无移除文件的 Globs
analyses
是—
"language"
、
"size"
,或两者兼有
compression
否
gzip
用于
size
分析,可选
gzip
、
brotli
或
none
directory
否
.
此 input 的 globs 的起始目录,相对于进程工作目录
include
中以
!
为前缀的项用于移除文件;整个数组中的所有否定项会组成一个集合,并应用于整个 input,因此调换数组顺序绝不会改变匹配结果。
exclude
不得携带
!
——它本来就负责移除文件,因此没有什么还可供取反。对于声明的 input,系统不会查询 ignore 文件,而这正是它能够匹配编译输出的原因。
将某个 input 命名为
codebase
,会替换内置的整树扫描,而不是在其旁边新增一个;此时只有它的
compression
和
analyses
会被读取,
include
/
exclude
不会生效,因为整树扫描是通过遍历 ignore 文件发现的,而不是通过匹配 globs 实现的。若要测量树中的某个子集,请改用其他名称声明 input。

Limits

限制

A limit bounds how high one measured metric may go. Any metric can carry one — a compressed size, a line count, a declared counter's count — and a metric with no limit is measured and reported exactly as before, gated by nothing.
ts
defaultInput: "codebase",
limits: [
  { metric: "compiled.size", value: "8 KB" },
  { label: "Interfaces", metric: "typescript.interfaces", value: 500 },
  { metric: "linesOfCode", severity: "warn", value: 100_000 },
],
FieldRequiredDefaultMeaning
metric
yes—Dotted path of the metric this bounds
value
yes—Ceiling, as a number or a string carrying a unit
severity
no
fail
fail
stops the run on a breach;
warn
only reports it
label
nothe pathWhat the report calls this limit
A metric is addressed as its input's name followed by its path within that input. Setting
defaultInput
lets an unprefixed path resolve to that input's metric, but only where no input of a competing name exists — write the input name in full wherever an input and a metric group could share one, because an ambiguous path is refused outright, never guessed at. The same refusal applies to a path naming no metric at all, or one from an analysis its input never ran.
A string value's unit is decimal and its trailing
b
is required:
"8 KB"
is 8000 bytes,
"1 MB"
is 1000000, and
"8 K"
is refused rather than read as anything. An input that matches no files only fails the run if a limit is written against it — declaring the limit is what asserts the files should be there.
Never raise a limit's
value
to make a breach pass.
A configured limit is a promise about the codebase's shape, and raising it on the change that broke that promise erases the evidence the limit existed to keep. If a breach is a real regression, reduce what is being measured instead — trim the metric down, split the file, delete the dead code. If the limit itself was simply wrong for what this metric should hold going forward, that is also a decision worth making deliberately and explaining, not a number quietly bumped in the same change that broke it. For everything else a breach could mean, reach for the
codometer-triage
skill.
limit 用于限定某个已测指标的最高值。任何指标都可以设置 limit,例如压缩后的文件大小、行数或已声明计数器的计数;没有 limit 的指标仍会像以前一样被测量和报告,不受任何 gate 约束。
ts
defaultInput: "codebase",
limits: [
  { metric: "compiled.size", value: "8 KB" },
  { label: "Interfaces", metric: "typescript.interfaces", value: 500 },
  { metric: "linesOfCode", severity: "warn", value: 100_000 },
],
字段必填默认值含义
metric
是—此 limit 所限定指标的点分路径
value
是—上限,可以是数字,也可以是带单位的字符串
severity
否
fail
fail
会在超出限制时停止运行;
warn
仅报告超出限制
label
否指标路径报告中用于称呼此 limit 的名称
指标的寻址方式是在前面加上 input 的名称,后接该指标在此 input 内的路径。设置
defaultInput
后,未加前缀的路径可以解析到该 input 的指标,但前提是不存在名称冲突的其他 input——如果 input 名称和指标组可能重名,应始终完整写出 input 名称,因为歧义路径会被直接拒绝,绝不会靠猜测处理。同样的拒绝规则也适用于未指向任何指标的路径,或指向该 input 从未运行过的分析类型的路径。
字符串值的单位采用十进制,且末尾的
b
必填:
"8 KB"
表示 8000 字节,
"1 MB"
表示 1000000,而
"8 K"
会被拒绝,不会被解释为其他数值。只有当某个 input 上存在针对它的 limit 时,未匹配到任何文件的 input 才会导致运行失败——声明该 limit,就等于断言这些文件应当存在。
绝不要通过提高 limit 的
value
来让违规通过。
已配置的 limit 是对代码库形态作出的承诺;在破坏该承诺的变更中提高它,会抹掉 limit 原本要保留的证据。如果违规确实代表回归,应改为减少被测量的内容——缩小指标规模、拆分文件或删除死代码。如果 limit 本身确实不适合该指标今后应保持的范围,这也应是一项经过审慎决定并加以解释的变更,而不是在破坏承诺的同一次变更中悄悄调高数字。对于其他可能意味着违规的情况,请使用
codometer-triage
skill。

Custom statistics

自定义统计

The top-level
custom
array is what measures a counter — declaring it there is what computes it, regardless of whether, or where, any output renders it:
ts
custom: [
  { label: "Service Files", patterns: ["**/*.service.ts"] },
  { label: "Unit Tests", patterns: ["**/*.unit.test.ts"] },
  { color: "16a34a", label: "Migrations", patterns: ["**/migrations/*.sql"] },
],
Each entry renders as one badge everywhere it is selected, in configured order.
color
is a shields.io hexadecimal triplet; an entry that omits it takes the next color from a built-in palette per group, so a counter's color stays stable between runs. A counter selects what it counts with exactly one of three fields —
patterns
,
symbols
, or
comment
— and an entry naming none of them is rejected rather than reported as a permanent zero.
An
outputs
entry's own
custom
array is a separate, later concern: it selects, by label, which of the top-level counters that destination renders — it never declares a counter of its own, so a JSON report and a markdown report may still select entirely different labels:
ts
outputs: [
  { custom: ["Service Files"], path: "codometer-report.json", type: "json" },
  { custom: ["Unit Tests"], path: "README.md", type: "markdown" },
],
A label an output selects that the top-level
custom
never declared is refused when the configuration loads — selection cannot conjure a counter that was never measured. A counter
custom
declares but no output selects is still measured and still gates a limit; it simply renders nowhere.
顶层
custom
数组负责测量计数器;在其中进行声明才会计算该计数器,无论任何 output 是否以及在哪里呈现它:
ts
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
数组属于另一个更靠后的环节:它按 label 选择目标 output 要呈现哪些顶层计数器,而不会自行声明计数器。因此,JSON 报告和 Markdown 报告可以选择完全不同的 labels:
ts
outputs: [
  { custom: ["Service Files"], path: "codometer-report.json", type: "json" },
  { custom: ["Unit Tests"], path: "README.md", type: "markdown" },
],
如果 output 选择的 label 未在顶层
custom
中声明,配置加载时会被拒绝——选择操作无法凭空创建一个从未被测量的计数器。如果某个计数器已由
custom
声明,但没有任何 output 选择它,它仍会被测量,也仍可对 limit 产生 gate 作用,只是不会呈现在任何地方。

Counting declarations

统计声明

symbols
counts declarations in TypeScript and JavaScript sources instead of files:
ts
custom: [
  { group: "typescript", label: "Static Methods", symbols: { kinds: ["method"], modifiers: ["static"] } },
],
kinds
is one or more of
class
,
enum
,
function
,
getter
,
interface
,
method
,
property
,
setter
;
modifiers
narrows to declarations carrying every named modifier from
abstract
,
async
,
export
,
override
,
private
,
protected
,
public
,
readonly
,
static
. Both are read literally from the syntax — a class member is a
method
, everything else callable is a
function
including an arrow, and a class field holding an arrow function is a
property
carrying none of the field's own modifiers.
patterns
on a symbol counter narrows which files are searched rather than being what is counted.
group
places the badge after a named group's built-ins —
conventions
by default, or one of the built-in language groups — and a name outside that set fails the configuration rather than rendering nowhere.
symbols
用于统计 TypeScript 和 JavaScript 源代码中的声明,而不是文件:
ts
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
,也可以是内置语言组之一。如果使用该范围之外的组名,配置会失败,而不是将徽章呈现在无处可见的位置。

Selecting a comment budget

选择注释预算

comment
selects a comment budget for a counter to measure, rather than counting files or declarations. There is no
comments
/
documentation
mechanism of its own any more: a comment budget is an ordinary custom statistic, and a
limits[]
entry against its
custom.<label>
metric is what turns the count into a gate — the same consolidation every other counter goes through.
ts
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 }],
FieldRequiredDefaultMeaning
language
noevery language with commentsNarrows to one of
css
,
hcl
,
python
,
shell
,
sql
,
toml
,
typescript
,
yaml
kind
noa plain comment blockNarrows to a documented declaration's JSDoc-style comment for this declaration kind
maximumCharacters
no—Characters a block may hold, markers and newlines and all
maximumLines
no—Lines a block may span
maximumWords
no—Words of prose a block may hold, once markers are stripped
severity
no
fail
fail
stops the run on a breach;
warn
only reports it
Its count is how many blocks broke the selector's own maxima, and its
instances
names each breaching block's
file
and 1-indexed
line
, plus how much it measured. A block that stayed within budget contributes to neither.
A
comment
selector carries no inheritance.
There is no top-level default a per-language entry merges over, so "every language at 128 words except shell at 256" is written as one selector per language, not as a shared budget with an override — this repository's own
configuration/codometer.config.ts
derives that enumeration from
CODOMETER_COMMENT_LANGUAGES
and types it so a language added to the schema is a compile error there until this repository's list covers it. A language codometer starts measuring comments in is not gated until that enumeration is updated to name it — nothing about adding a language to the schema gates it automatically.
The three maxima are separate fields rather than one
maximum
steered by a
unit
, because they are not alternatives.
A block can sit inside a line budget and outside a word one. A field left out is not measured, and nothing is defaulted to a number — a budget nobody wrote is one nobody chose.
The budget is per block, never file-wide. A block is the run of comment lines a reader takes as one thought: a blank line ends one, a comment trailing a value is never part of the block above it, and a
#!
shebang is never a comment at all. There is no file-wide counterpart to a
comment
selector — a file holding forty well-sized comments is not the same problem as one holding a single essay, but codometer has no way to measure the second kind today.
Prefer a word budget over a character one where a linter already holds lines to a column limit: the character count would only restate what formatting already enforces, while words budget what the comment actually says. JSDoc is ungated by default — a
comment
selector with a
kind
reaches it, but declaring one is a deliberate choice, not something that happens by naming a language.
comment
为计数器选择一项需要测量的注释预算,而不是用来统计文件或声明。现在已经不存在独立的
comments
/
documentation
机制:注释预算是一项普通的自定义统计,只有针对其
custom.<label>
指标的
limits[]
条目才会将计数转换为 gate——这与所有其他计数器采用的整合方式相同。
ts
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
,以及测得的超出量。未超出预算的注释块不会贡献计数,也不会出现在
instances
中。
comment
选择器不具有继承关系。
不存在可供按语言条目合并覆盖的顶层默认值,因此“所有语言均为 128 个词,但 shell 为 256 个词”需要为每种语言分别编写一个选择器,而不是使用共享预算加覆盖的方式。本仓库自己的
configuration/codometer.config.ts
从
CODOMETER_COMMENT_LANGUAGES
推导该枚举并为其添加类型,因此当 schema 中新增一种语言时,如果仓库的列表尚未覆盖它,就会产生编译错误。codometer 开始测量某种语言的注释后,在该枚举更新并明确列出它之前,这种语言不会受到 gate 约束——仅仅向 schema 中添加语言并不会自动为其启用 gate。
三个最大值是彼此独立的字段,而不是由
unit
控制的单一
maximum
,因为它们并不是互斥选项。
一个注释块可能符合行数预算,却超出词数预算。未填写的字段不会被测量,而且任何字段都不会默认成一个数字——没有人写下的预算,就代表没有人选择过的预算。
预算始终按注释块计算,绝不按文件整体计算。 注释块是读者视为一个完整想法的一段连续注释行:空行会结束一个注释块,值后面的尾随注释绝不属于上方注释块,而
#!
shebang 始终不属于注释。
comment
选择器没有文件级对应项——包含四十个大小合适的注释的文件,与包含一篇长篇大论的文件并非同一类问题,但 codometer 目前无法测量后一种情况。
如果 linter 已经将行长度限制在某一列宽,应优先使用词数预算,而不是字符数预算:字符数只会重复格式化工具已经强制执行的内容,而词数预算才能限制注释实际表达的信息量。JSDoc 默认不受 gate 约束——带
kind
的
comment
选择器可以覆盖它,但声明这种限制是一项有意选择,而不是仅指定语言就会自动发生的行为。

What a breach means, and what it does not

违规意味着什么,以及不意味着什么

A breach names its file and line. Condense the block, or move the detail into documentation that has room for it — never raise the budget to make the run pass, for the same reason a limit's
value
is never raised.
Two accuracy caveats worth knowing before trusting a count:
  • Some languages read a real parser, and some a line scanner. CSS (postcss), Python (
    tokenize
    ), YAML (its CST), and TypeScript/JavaScript (the compiler's scanner) all know a comment marker inside a string literal from a real comment. Shell, TOML, SQL, and HCL use a line scanner and cannot — a
    #
    or
    --
    inside a string is read as a comment, exactly as those languages' existing comment counters already read it. HCL is the one language measured with three syntaxes:
    #
    ,
    //
    , and
    /* */
    .
  • Python's comments need a reachable interpreter, the same way every other Python metric does. An interpreter codometer cannot run leaves them unmeasured rather than miscounted.
For what to do about a breach, reach for the
codometer-triage
skill.
违规会指出对应的文件和行。应当压缩注释块,或将细节迁移到有足够空间的文档中——绝不要提高预算来让运行通过,原因与绝不提高 limit 的
value
相同。
在信任计数之前,需要了解两项准确性限制:
  • 有些语言使用真正的解析器,有些则使用行扫描器。 CSS(postcss)、Python(
    tokenize
    )、YAML(其 CST)以及 TypeScript/JavaScript(编译器的 scanner)都能识别字符串字面量中的注释标记与真正的注释之间的区别。Shell、TOML、SQL 和 HCL 使用行扫描器,无法做到这一点——字符串中的
    #
    或
    --
    会被当作注释读取,与这些语言现有的注释计数器所采用的读取方式完全一致。HCL 是唯一一个按三种语法进行测量的语言:
    #
    、
    //
    和
    /* */
    。
  • Python 注释需要可访问的 interpreter,这一点与所有其他 Python 指标相同。如果 codometer 无法运行 interpreter,它会留下未测量的结果,而不是给出错误计数。
对于如何处理违规,请使用
codometer-triage
skill。

Output destinations

输出目标

outputs
is an array of typed destinations — declare as many as you like, but at most one entry per type; a configuration naming two
json
outputs, or two
markdown
ones, is rejected at load time naming the duplicated type, because nothing on the command line could address the second.
ts
outputs: [
  { indentation: 2, path: "output/codometer.json", type: "json" },
  {
    description: "Measured on every push.",
    path: "README.md",
    type: "markdown",
  },
],
A JSON entry needs
path
;
indentation
defaults to
2
. A markdown entry needs a
path
, a
write
function, or both — a destination naming neither is rejected when the configuration loads.
outputs
是由带类型目标组成的数组——可以声明任意多个,但每种类型最多只能有一个条目;如果一份配置声明了两个
json
outputs 或两个
markdown
outputs,它会在加载时被拒绝并指出重复的类型,因为命令行无法定位第二个目标。
ts
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
函数,或两者同时设置;如果目标两者均未设置,配置加载时会被拒绝。

Writing markdown

编写 Markdown

write
is the whole of the customizable behavior — it both decides what the report says and where it lands, replacing what used to be two separate
render
and
write
callbacks:
ts
{
  path: "README.md",
  type: "markdown",
  write: ({ anchors, renderBadges, statistics }) =>
    anchors.syncAnchoredBlock({
      content: `Lines of code: ${statistics.linesOfCode}\n\n${renderBadges()}`,
    }),
}
ArgumentMeaning
description
The configured description, for a writer that wants to place it itself
renderBadges()
The built-in badge rendering of these same statistics — call it to build the default content, or to add to it
statistics
The measured statistics
check
True when nothing may be written and the file is only being inspected
path
The configured path, resolved against the process's working directory
anchors
The marker mechanics —
syncAnchoredBlock
,
wrapInAnchors
,
startMarker
/
endMarker
— so choosing a different file does not mean reimplementing the splice
Leaving
write
unset keeps the built-in rendering and writing. A
write
function returns
false
to report its destination as stale, which is what fails a
--check reports
run; anything else counts as up to date.
write
包含全部可定制行为——它既决定报告内容,也决定报告写入位置,并取代过去彼此独立的
render
和
write
回调:
ts
{
  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
运行失败的方式;返回其他任何值都视为目标为最新状态。

Authoring says inputs, the report says targets

配置端称 inputs,报告端称 targets

inputs
is what a configuration declares — this redesign renamed
targets
to
inputs
and
defaultTarget
to
defaultInput
on the authoring side — but the measured JSON report keeps its old vocabulary:
CodometerReport.targets
, and a metric is still addressed as
codebase.typescript.interfaces
or
Compiled JavaScript.size
, the input's name standing in for what the report calls a target. That split is deliberate rather than drift, kept because
CodometerReport.targets
predates this redesign and nothing downstream reading a report needed to change; the
Measured Targets
badge group carries its own code comment explaining why it keeps that name too. Write
inputs
in a configuration; read
targets
in a report, a
configuration --limits
listing, or a badge group heading.
inputs
是配置所声明的内容——此次重新设计将配置端的
targets
更名为
inputs
,将
defaultTarget
更名为
defaultInput
——但测量生成的 JSON 报告仍保留旧有词汇:
CodometerReport.targets
,指标仍使用
codebase.typescript.interfaces
或
Compiled JavaScript.size
等形式寻址,此时 input 的名称代替了报告中所谓的 target。这种拆分是刻意设计,而非命名漂移;之所以保留,是因为
CodometerReport.targets
早于此次重新设计,并且下游报告读取逻辑无需改变;
Measured Targets
徽章组也有自己的代码注释解释为何保留该名称。配置中应写入
inputs
;在报告、
configuration --limits
列表或徽章组标题中则应读取
targets
。