首页
/ Reactive Resume 中 Semantic CSS 完整命名规范设计:一个未发布特性的词汇统一与编译隔离策略

Reactive Resume 中 Semantic CSS 完整命名规范设计:一个未发布特性的词汇统一与编译隔离策略

2026-09-05 11:42:28作者:咎岭娴Homer

本文解读 Reactive Resume 仓库中 Semantic CSS 完整重命名设计文档 的核心内容:在 Custom Styles 特性合入主干之前,将旧缩写命名彻底替换为 Semantic CSS 单一词汇体系,并统一 @version 1; 版本指令、--resume-* 系统变量与 -resume-* 渲染器属性三套命名空间。读完本文,你将理解这套命名契约(Naming Contract)如何落到 TypeScript 符号、常量前缀、缓存键和文档标记上,以及仓库中编译器、注册表与测试如何印证这些约定。

背景:为什么要在合并前做“完整重命名”

设计文档的目标非常明确:以 Semantic CSS 作为该特性唯一的公开名称,并在特性合并前移除此前的缩写(a former acronym)及其所有派生前缀,使作者、贡献者、诊断信息和文档共用一套词汇(见 设计文档 的 Goal 一节)。

这条约束之所以可行,关键前提是文档中反复强调的一点:该特性尚未发布(“because the feature has not shipped”)。因此编译器不需要保留任何废弃别名、迁移路径或兼容性警告,直接只接受新语法即可。这也是整篇设计文档与通常的“API 重命名”不同的地方——它没有兼容层问题,只有“一次做对”的纪律问题。

从源码结构看,这一词汇统一已经贯彻到仓库各层:公共包导出、API 初始数据、PDF 渲染、E2E 测试夹具中都使用 SemanticCss 形式。例如:

  • 公共类型导出集中在 stylesheet 包入口,其中暴露 SemanticCssCompilerDiagnosticCodeSemanticCssDiagnostic 等诊断相关类型;
  • Web 端编辑器 editor.tsx 引入 SemanticCssDiagnosticSemanticCssColorTokenSemanticCssEditorMetadata 等编辑器协议类型;
  • PDF 渲染与 API 层也分别引用了统一语言命名空间,见 resolve.tsinitial-data.ts

命名契约(Naming Contract):每个语境一种规范形式

设计文档用一张表格定义了“哪种语境下该用哪种写法”,这是整篇规范的核心:

语境 规范形式
产品名与语言名 Semantic CSS
TypeScript 符号形式 SemanticCss*
常量前缀 SEMANTIC_CSS_*
Slug 与缓存形式 semantic-css-*
版本指令 @version 1;
系统变量 --resume-*
渲染器属性 -resume-*
空源常量 EMPTY_SEMANTIC_CSS_SOURCE
文档标记 SEMANTIC-CSS-*

重命名适用于该分支中所有被 Git 跟踪的源码、测试、夹具、生成标记、指南、计划与规格文档,但 不重写 Git 历史。同时文档划定了“不动区”:既有中性命名保持不变,包括 stylesheetmode: "semantic"languageVersion、语义节点名称、API 路由、数据库列名,以及 /applying-custom-styles 文档 URL。

逐条对照仓库,可以看到契约的落地方式:

  • 常量前缀 SEMANTIC_CSS_*:长度属性清单、长度单位、边框取值、CSS 宽泛关键字等 v1 常量均以该前缀命名,如 SEMANTIC_CSS_LENGTH_UNITS_V1SEMANTIC_CSS_BORDER_STYLE_VALUES_V1registry/properties.ts);支持版本常量 SUPPORTED_SEMANTIC_CSS_VERSIONS 同样遵循此约定(version.ts)。
  • 空源常量 EMPTY_SEMANTIC_CSS_SOURCE:定义在 packages/schema/src/resume/stylesheet.ts,值为 "@version 1;\n"。它被两处消费:API 层为新建简历初始化样式表源(initial-data.ts),以及 PDF 渲染层在缺少用户样式时回退到空源(resolve.ts)。
  • 文档标记 SEMANTIC-CSS-*:用于自动生成参考文档中的生成块标记,如 SEMANTIC-CSS-SEMANTIC-ELEMENTSSEMANTIC-CSS-PROPERTIESSEMANTIC-CSS-SYSTEM-VARIABLESSEMANTIC-CSS-TEMPLATE-PARTSSEMANTIC-CSS-DIAGNOSTICSSEMANTIC-CSS-LIMITS,规划与测试用例见 计划文档generate-reference.test.ts
  • Slug 与缓存形式 semantic-css-*:编译缓存标识采用该形式,详见下文“内部代码”一节。

值得注意的是文档对“中性命名”的保护:Stylesheet* 前缀的标识符(如 StylesheetCompilationCachecompileStylesheet)被明确允许保留,避免在已经由 stylesheet 模块限定的标识符上重复 SemanticCss,这既控制了命名噪音,也让编译器核心 API 保持简洁。

语言语法:@version 1;--resume-*-resume-* 三个命名空间

设计文档规定新样式表与格式化输出以版本指令开头:

@version 1;

解析后的 Builder 值使用 --resume-* 系统变量命名空间:

:root {
	--accent: var(--resume-primary-color);
}

渲染器专用属性使用 -resume-* 命名空间:

section {
	-resume-min-presence-ahead: 24pt;
}

编译器只接受新语法,没有任何废弃别名或兼容警告。三组命名各有明确的实现位置:

版本指令的编译验证

编译器对 @version 指令做了完整的行为约束,version.test.ts 中的测试逐一固定了这些行为:

输入场景 诊断码 严重级别
规范 v1 源 @version 1; 无诊断
缺少版本指令 MISSING_VERSION_DIRECTIVE warning
指令与持久化元数据不一致(如 @version 2;languageVersion: 1 VERSION_MISMATCH error
重复指令(包括嵌套在 at-rule 块内的重复) DUPLICATE_VERSION_DIRECTIVE error
畸形指令(@version one; INVALID_VERSION error
非正数指令(@version 0; INVALID_VERSION error
不支持的持久化语言版本(languageVersion: 2 UNSUPPORTED_VERSION error

从实现看,version.tsSUPPORTED_SEMANTIC_CSS_VERSIONS = Object.freeze([1] as const) 声明当前仅支持版本 1,并通过 COMPILERS 表按版本号分派编译器;v1 编译器产出的 StyleProgram 携带 languageVersion: 1 与冻结规则数组。这个“版本分派表 + 冻结常量”的结构正是契约中“无兼容路径”的实现方式:未来若支持版本 2,需要显式注册新编译器,而不是修改现有解析逻辑。

--resume-* 系统变量注册表

--resume-* 变量并非自由命名空间——存在一个显式注册表 SYSTEM_VARIABLE_REGISTRY_V1,共 21 个变量,每个都带说明文本(registry/system-variables.ts),例如 --resume-primary-color(Builder 主色)、--resume-page-width / --resume-page-height(解析后的页面宽高)、--resume-sidebar-width(侧栏宽度)、--resume-picture-shadow-width(头像投影宽度)等。

createSystemVariables 负责把 Builder 的基础设置快照与解析后的页面尺寸物化为具体变量值:颜色直接取 design.colors 三值,字体尺寸格式化为 pt,行高为倍数,边距/间距带 pt 单位,侧栏宽度用 %,头像旋转用 deg。这解释了文档示例中 var(--resume-primary-color) 的来源——它是把编辑器 Design 面板的持久化状态桥接到样式表的机制。

-resume-* 渲染器属性

-resume-* 是 v1 语法中仅有的几组私有前缀属性,专门表达浏览器 CSS 无法覆盖的 PDF 渲染语义。从 properties.ts 的属性注册表可以看到完整清单:

  • -resume-min-presence-ahead(结构类,长度属性):控制分页时元素前后保留的最小空间,即文档示例中的“section 需要前方至少 24pt”场景;
  • -resume-shadow-width(图像类):与 -resume-shadow-color 配对,作用于 picture 节点的投影宽度;
  • -resume-fixed(结构类):取值 true | false | 0 | 1,见 propertyValueHints

注册表还统一了取值与单位的约束体系:size 页面属性仅接受 A4letter-resume-min-presence-aheadsize 使用不含 % 的数值单位集(propertyUnits);所有长度类属性(含上述两个 -resume-* 长度属性)共享 ptpxinmmcm%vwvhemrem 单位白名单。

产品与文档:面向用户的统一词汇

设计文档要求:所有可见的编辑器标签、帮助文本、错误提示、诊断信息、面向运维的日志、断言可见文案的测试,以及 Applying Custom Styles 指南,一律使用 Semantic CSS 一词;指南与示例只教授 @version--resume-*-resume-*

用户可见侧的对应物是 Applying Custom Styles 指南。该指南与契约保持一致:

  • 开篇即声明“Custom Styles let you make focused changes… They use Semantic CSS, a CSS-like language designed for resume PDFs”,并明确 Semantic CSS 作用于 PDF 输出而非浏览器界面,不能加载字体、图片、脚本;
  • “Make your first change” 一节给出的最小完整示例仍以 @version 1; 开头:
@version 1;

section[type="experience"] > section-heading {
	color: #0f766e;
	text-transform: uppercase;
}
  • 指南保留了旧版 Custom Styles 表单到 Semantic CSS 草稿的转换流程(打开 Design → Custom Styles、审阅转换草稿、确认预览一致后才点击 Activate Semantic CSS),并强调两个系统不会同时生效、保留回滚能力。

指南本身保持手工编写。设计文档规定 pnpm docs:gen 继续只重新生成 Resume schema 参考与 OpenAPI 规格,而指南中用标记框选出的 Semantic CSS 示例仍由“示例编译测试”持续验证可编译。这与仓库中的文档工具链吻合:generate-reference.ts 通过 JSON Schema 生成 json-resume-schema 指南skills 参考,配合根 package.json 中的 docs:gen 脚本(pnpm --filter server docs:gen && pnpm --filter @reactive-resume/tooling docs:gen)。也就是说,SEMANTIC-CSS-* 生成块标记保证了自动生成的参考内容不会与手工指南正文互相覆盖。

内部代码:标识符命名与编译缓存失效

设计文档对内部分两句话约束:

  1. 公共包导出与内部标识符在需要语言名时使用 SemanticCssSEMANTIC_CSS;已由 stylesheet 模块限定的标识符可保留中性的 Stylesheet* 名。
  2. 编译器构建/缓存标识必须变更,使旧语法下产生的缓存输出无法被复用;且不添加任何数据迁移。

第二条在本仓库中有直接证据:cache.ts 中定义了

const SEMANTIC_CSS_COMPILER_BUILD_ID = "semantic-css-v1-values-2";

export function stylesheetCacheKey(languageVersion: number, source: string, registry: string): string {
	return JSON.stringify([languageVersion, source, SEMANTIC_CSS_COMPILER_BUILD_ID, registry]);
}

缓存键由四段组成:语言版本、样式表源文本、编译器构建标识、注册表版本。这里体现了两个值得注意的设计点:

  • 构建标识充当“隐式失效开关”semantic-css-v1-values-2 形式的 slug 同时满足命名契约中“缓存形式 semantic-css-*”的要求;当语法或值语义发生不兼容变化(例如本次词汇体系切换)时,只需提升该常量,所有旧缓存条目因键不同而自然失效,无需清理逻辑或数据迁移——这正是设计文档 “No data migration is added” 的实现依据。
  • 缓存本身有资源上限StylesheetCompilationCache 是 LRU 结构,最多 128 条、总序列化体积上限 16 MiB,单条超限直接不缓存,超限按最旧条目逐出。这保证了编译缓存不会在长会话中无限膨胀。

与契约中“SEMANTIC_CSS 常量前缀 + 中性 Stylesheet* 名共存”的约定相呼应:缓存类与编译函数沿用 Stylesheet*StylesheetCompilationCachecompileStylesheet),而构建标识常量使用 SEMANTIC_CSS_COMPILER_BUILD_ID

验收标准:以可搜索性定义“完成”

设计文档给出的完成判据本身就是一套可执行的验证清单,值得作为特性级重命名的通用方法参考:

  1. 不区分大小写的跟踪文件搜索找不到旧四字母缩写的任何出现——把“词汇统一”转化为 grep 可验证的断言;
  2. 跟踪文件搜索找不到旧指令、变量或渲染器属性前缀;
  3. 编译器测试证明 @version 1; 必填、旧指令被拒绝为不支持——对应上文 version.test.tsMISSING_VERSION_DIRECTIVEUNSUPPORTED_VERSION 用例;
  4. 注册表与渲染测试覆盖重命名后的系统变量与渲染器属性——对应 system-variables.test.tsproperties.test.ts
  5. 公共指南中标记的示例可编译;
  6. 焦点包测试、类型检查、文档生成、Knip、Biome 与既有 E2E 工作流全部通过——E2E 侧有对应的夹具 tests/e2e/fixtures/semantic-css.ts 与用例 semantic-css.test.ts
  7. 不需要本地 Chrome 运行,浏览器验证仍由 CI 负责。

这套验收清单的设计取向是“以搜索代替记忆”:重命名是否完成不依赖评审者肉眼确认,而依赖仓库级文本搜索与既有测试流水线,任何残留的旧词汇都会让第 1、2 条直接失败。

小结

这份 设计文档 篇幅不长,却把“未发布特性的命名治理”做成了工程约束:一张命名契约表覆盖从产品文案到缓存键的全部语境;三套语法命名空间(@version 1;--resume-*-resume-*)由编译器测试和注册表白名单双重锁定;公共 API 用 SemanticCss/SEMANTIC_CSS,模块内实现允许中性 Stylesheet*;缓存键内嵌 semantic-css-v1-values-2 构建标识实现零迁移的缓存失效;最后用七条可自动执行的验收判据收尾。对仓库贡献者而言,它同时是词汇字典与检查清单——在动任何 Semantic CSS 相关代码前,先对照这张契约表命名,再跑一遍验证项,是文档期望的协作方式。

登录后查看全文
热门项目推荐
相关项目推荐