Design Token 跨端语义契约校验器:基于 JSON Schema 验证
·
Design Token 跨端语义契约校验器:基于 JSON Schema 验证

在大型跨端设计系统(Multi-platform Design System / 覆盖 Web, iOS SwiftUI, Android Compose, Flutter)的工程化协作中,最令人头疼的莫过于**“单源真理契约漂移与非法数据污染(Single Source of Truth Drift & Corrupted Payloads)”**:
- 一位初级设计师在 Figma Token 插件中,不小心手抖把一个间距 Token 写成了
"16pxx"(多打了一个x); - 某个开发者在维护主题 JSON 时,把一个颜色写成了非标的
"blue-bright"(既不是标准的 Hex/OKLCH,也没有在色彩字典中注册); - 这个包含了脏数据的
tokens.json一旦被合并发版,灾难瞬间多端引爆:- Web 端的 PostCSS 编译报错中断;
- iOS 端的 Swift 编译器因为无法将非法字符串解析为
Color而直接报出语法编译错误! - Android 端的 Gradle 构建流水线全面红屏瘫痪!
在数据工程与接口契约设计中,JSON Schema(Draft 2020-12 规范)结合高性能编译引擎 Ajv 是确保跨端数据交换具备 $100%$ 严格类型安全、格式校验与语义自洽的最强工业级防火墙。
本文将深入设计一套专为 Design Token(符合 W3C DTCG 国际标准)打造的 JSON Schema 契约元规范,并手写一个毫秒级拦截一切非法 Token 的 CI 自动化校验引擎。
Design Token 跨端契约校验的三层防护拓扑
[设计师提交变更: tokens/core-tokens.json]
│
▼ (步骤 1: Ajv 载入 Design Token JSON Schema 契约规则)
┌───────────────────────┴────────────────────────────────────────────────────┐
├── 规则 A (类型断言): $type 必须为 "color" | "dimension" | "duration" | "font"
├── 规则 B (格式正则): 颜色值严格匹配 ^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$ 或 oklch()
├── 规则 C (数值约束): 间距必须为带 px/rem 单位的正数,严禁负数与非标后缀
└── 规则 D (别名闭包): 引用别名 {color.brand.primary} 必须存在于字典定义中!
│
▼ (步骤 2: 校验引擎毫秒级扫描)
[🔥 发现 0 错误 ➔ 允许进入 Web/iOS/Android 代码生成器 | 发现错误 ➔ 阻断 PR 并高亮行号!]
设计符合 W3C DTCG 标准的 Token JSON Schema 元规范
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://company.design/schemas/design-tokens.schema.json",
"title": "DesignTokensContractSchema",
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9_-]+$": {
"type": "object",
"properties": {
"value": {
"type": ["string", "number"]
},
"type": {
"type": "string",
"enum": ["color", "dimension", "duration", "fontWeight", "fontFamily", "cubicBezier"]
},
"description": {
"type": "string"
}
},
"required": ["value", "type"],
"allOf": [
{
"if": { "properties": { "type": { "const": "color" } } },
"then": {
"properties": {
"value": {
"type": "string",
"pattern": "^(#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})|oklch\\([0-9.%\\s/]+\\)|\\{[a-zA-Z0-9_.-]+\\})$"
}
}
}
},
{
"if": { "properties": { "type": { "const": "dimension" } } },
"then": {
"properties": {
"value": {
"type": "string",
"pattern": "^([0-9]+(\\.[0-9]+)?(px|rem|em)|0|\\{[a-zA-Z0-9_.-]+\\})$"
}
}
}
}
]
}
}
}
编写基于 Ajv 的生产级 Token 契约自动化校验引擎
// scripts/token-contract-validator.ts
import Ajv, { DefinedError } from 'ajv/dist/2020';
import addFormats from 'ajv-formats';
import * as fs from 'fs';
export interface TokenValidationError {
tokenPath: string;
errorMessage: string;
receivedValue: any;
}
export class TokenContractValidator {
private ajv: Ajv;
private validateFunction: any;
constructor(schemaPath: string) {
this.ajv = new Ajv({
allErrors: true, // 收集全部错误,一次性报出
strict: true,
verbose: true,
});
addFormats(this.ajv);
const schemaContent = JSON.parse(fs.readFileSync(schemaPath, 'utf8'));
this.validateFunction = this.ajv.compile(schemaContent);
}
// 1. 语法与格式结构校验
public validateTokenFile(jsonFilePath: string): { isValid: boolean; errors: TokenValidationError[] } {
const rawContent = fs.readFileSync(jsonFilePath, 'utf8');
const tokens = JSON.parse(rawContent);
const isValid = this.validateFunction(tokens);
const errors: TokenValidationError[] = [];
if (!isValid && this.validateFunction.errors) {
for (const err of this.validateFunction.errors as DefinedError[]) {
errors.push({
tokenPath: err.instancePath || '/',
errorMessage: err.message || '未知契约校验失败',
receivedValue: err.data,
});
}
}
// 2. 语义别名拓扑闭包校验 (检查引用的别名是否存在)
const aliasErrors = this.validateAliasReferences(tokens);
errors.push(...aliasErrors);
return {
isValid: errors.length === 0,
errors,
};
}
// 检查形如 {color.brand.primary} 的别名是否真实存在
private validateAliasReferences(tokenTree: Record<string, any>): TokenValidationError[] {
const definedKeys = new Set(Object.keys(tokenTree));
const errors: TokenValidationError[] = [];
for (const [key, token] of Object.entries(tokenTree)) {
if (typeof token.value === 'string' && token.value.startsWith('{') && token.value.endsWith('}')) {
const targetRef = token.value.slice(1, -1);
if (!definedKeys.has(targetRef)) {
errors.push({
tokenPath: `/${key}/value`,
errorMessage: `🚨 悬挂死引用:指向的别名 [${targetRef}] 不存在于 Token 字典中!`,
receivedValue: token.value,
});
}
}
}
return errors;
}
}
拦截非法 Token 的 CI 自动化执行脚本
// scripts/run-token-ci-check.ts
import { TokenContractValidator } from './token-contract-validator';
const validator = new TokenContractValidator('./schemas/design-tokens.schema.json');
const result = validator.validateTokenFile('./tokens/core.json');
if (!result.isValid) {
console.error('\n🚨 ============================================================');
console.error(`❌ [Design Token 契约校验失败] 发现 ${result.errors.length} 处严重非法数据!`);
console.error('============================================================');
result.errors.forEach((err, idx) => {
console.error(`\n[违规项 #${idx + 1}]:`);
console.error(` - Token 路径: ${err.tokenPath}`);
console.error(` - 错误原因: ${err.errorMessage}`);
console.error(` - 实际输入值: ${JSON.stringify(err.receivedValue)}`);
});
console.error('\n👉 阻断提醒:请修复上述数据格式后重新提交,防止破坏多端编译!\n');
process.exit(1);
} else {
console.log('✅ [Design Token 契约验证通过] 所有 Token 100% 满分符合跨端单源真理规范!');
}
总结
跨端设计系统的稳定性,建立在对数据契约的绝对敬畏之上。引入严格的 JSON Schema 契约元规范,结合 Ajv 在 CI/CD 源头对颜色格式、物理单位与别名引用进行无死角毫秒级校验,我们彻底消灭了跨端类型撕裂与脏数据引发的下游崩溃,让单源真理资产在 Web、iOS、Android 与 Flutter 之间实现真正无懈可击的安全流转。
更多推荐



所有评论(0)