Перейти к содержимому

Слой загрузки load.ts

Загрузка JSON + валидация схемы Ajv + уплощение.

load.ts выполняет только структурную валидацию (JSON Schema). Семантическая валидация (соответствие типов значениям, целостность ссылок, обнаружение циклов) происходит в resolve.ts.

interface LoadedTokenFile {
fileName: string
namespace: string // Префикс пространства имён (color / radius / zIndex; пустая строка означает, что ключи верхнего уровня — это пространства имён)
isDark: boolean
defaultType?: TokenType // Файловый $type
description?: string
flat: Map<string, RawToken> // path -> RawToken (после уплощения, с префиксом пространства имён)
}

Читает JSON-файл и выполняет JSON.parse. Внутренняя вспомогательная функция.

Перечисляет файлы .json в каталоге, исключая schema.json, сортирует и приводит к абсолютным путям. Сортировка гарантирует стабильный порядок загрузки (он влияет на сообщения об ошибках и воспроизводимость артефактов).

Вывод пространства имён из имени файла (запасная логика для случая, когда $namespace не объявлен): отбрасывается суффикс .json, строка разбивается по -, первый сегмент остаётся как есть, а остальные пишутся с заглавной первой буквой, после чего сегменты соединяются.

Имя файла Результат
color.json color
z-index.json zIndex
color-dark.json colorDark

Рекурсивно сворачивает вложенный JSON в плоское Map<path, RawToken>:

  • Пропускаются все ключи, начинающиеся с $ (мета-свойства)
  • Сращивание путей: prefix ? prefix + '.' + key : key
  • Узел, не являющийся ни листом, ни объектом → ошибка Путь токена X не является ни токеном-листом, ни группой

Ключевая логика наследования типов: $type, объявленный группой (включая корень файла), распространяется вниз по дереву, но запекается только в листья-литералы:

// Токены-литералы наследуют тип группы; ref-токены сохраняют undefined,
// оставляя выбор типа за resolve — он возьмёт его у цели ссылки
if (node.type === undefined && node.ref === undefined && groupType !== undefined) {
out.set(path, { ...node, type: groupType })
}

ref-листья намеренно не «запекаются» — решение о типе откладывается до этапа resolve (первый шаг трёхуровневого приоритета типов; см. Система типов).

Предикат типа: узел с ключом value или ref считается листовым токеном; иначе — группой.

Загружает каталог токенов:

  1. Читает tokens/schema.json и компилирует его с помощью Ajv (allErrors: true, strict: false)
  2. Читает и валидирует каждый JSON-файл; при неудаче выбрасывается Ошибка валидации файла токенов: <path> + отформатированный список ошибок
  3. Пространство имён: raw.$namespace (если это строка) имеет приоритет, иначе — namespaceFromFileName
  4. flatten(raw, namespace, flat) выполняет уплощение
  5. Сборка { fileName, namespace, isDark: raw.$dark === true, defaultType: raw.$type, description: raw.$description, flat }

Тем же образом загружает specs/*.json; при неудаче валидации выбрасывается Ошибка валидации контракта компонента.

Тем же образом загружает cases/*.json; при неудаче валидации выбрасывается Ошибка валидации золотых сценариев.

Форматирует объекты ошибок Ajv в список с отступами (instancePath + message).

Читает version из package.json; используется как часть заголовочного комментария каждого артефакта («источник: @lh-ui/core tokens @ v0.0.1»).

Слой Где Что делает
Структурная валидация load.ts JSON Schema: обязательные поля, значения перечислений, регулярные выражения для имён, additionalProperties: false
Семантическая валидация resolve.ts Соответствие типов значениям (assertTypedValue), существование ссылок, обнаружение циклов в ссылках
Проверка согласованности scripts/verify-specs.mts Перекрёстная согласованность specs и cases

Слои выделены потому, что JSON Schema не способна выразить межфайловые и межузловые ограничения вроде «цель ref должна существовать» или «тип должен соответствовать значению».

Copyright © 2024-2026 FengMo WenXue