设计决策
架构
- 单个 crate。 目前核心和 CLI 位于同一个 crate(
crates/annealer)中。只有在真正需要时才会拆分为 workspace,以避免过早抽象。CLI 依赖的clap位于默认的clifeature 之后,因此使用--no-default-features即可将库构建为 WebAssembly。 - 配置文件是 YAML,而不是代码。 HTML、Vue 以及未来的配置文件都是数据。每个配置文件都有
schemaVersion,因为该格式与 Rust 函数签名一样,都是公共 API。 - 严格校验。 解析配置文件时会拒绝未知键,因此拼写错误会立即暴露。另提供 JSON Schema(draft-07)以支持编辑器。draft-07 无法表达的两条规则(恰好一个 fallback 分组、分组名唯一)仅由加载器检查。
- malva 采用宽松的版本约束(
0.16),与其他项目中使用的库依赖策略一致。 - 只改变顺序。 annealer 询问 malva 如何对属性名称排序,然后按原样移动声明;不会使用 malva 输出的内容。空白、引号和其他格式交由 Prettier 或 Oxfmt 等格式化工具处理。
- npm 包内嵌 WebAssembly 二进制(base64),而不是单独提供
.wasm文件。代价是压缩前体积增加约 33%;换来的是format()为同步函数,并且无需配置打包工具或加载器。
实现过程中的决策
- HTML 配置文件的顺序。 最初的表格没有为未匹配的属性以及
alt/title留出位置。最终商定的顺序为 id → class → 补充(alt、title、placeholder)→ 语义(type、href、src、srcset)→ 键值(name、value、content)→ 尺寸(width、height)→ 其他(状态:disabled、checked等)→ ARIA → data → 事件。Vue 配置文件在其其他属性层级内部使用相同的顺序。 - "静态先于绑定"仅适用于
class。 对于其他名称,聚合簇保持源顺序,因为 Vue 合并style时后出现的声明优先,而对于其他重复项只保留一个值。 spreadSafe分组。 编译期指令从不与v-bind展开对象合并,因此可以跨越展开移动。其他所有属性都保持在各自的片段内。- 多行标签中每行一个属性。 否则重新排序可能会让两个属性挤在同一行。这与
vue/max-attributes-per-line的多行默认值一致,可以通过layout.oneAttributePerLine关闭。 - Vue 第 8 层被拆分为先其他指令、后其他属性,与
vue/attributes-order(OTHER_DIRECTIVES→OTHER_ATTR)一致。 void: always与vue/html-self-closing的默认值(html.void: never)不同。保留该 ESLint 规则的项目需要让其中一方对齐。- HTML 配置文件中同样开启
contentNewline。 块级内容开头和结尾的空白不会被渲染,而ignore列表涵盖了可能会显示空白的元素。 - 带厂商前缀的声明排在最前(
vendorPrefix: start),这样标准属性最后声明并生效。参见样式表。
已解决的问题
v-model与普通的value:从不聚合。键v-model与value不同。- 配置文件的严格程度:在解析时拒绝未知键。
- malva 版本:宽松范围
0.16。