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 之间实现真正无懈可击的安全流转。

Logo

一站式 AI 云服务平台

更多推荐