Files
HMDesign/scripts/theme-contract.mjs
T

26 lines
2.3 KiB
JavaScript

import { readFileSync } from "node:fs"
export const repositoryRoot = new URL("../", import.meta.url)
export const read = path => readFileSync(new URL(path, repositoryRoot), "utf8").replace(/\r\n/g, "\n")
const source = read("packages/ui/src/theme/contract.ts")
export const definitions = [...source.matchAll(/"([^"]+)": \{ property: "([^"]+)", defaultValue: [^\n]+?, type: "(color|radius)" \}/g)]
.map(([, key, property, type]) => ({ key, property, type }))
if (!definitions.length || new Set(definitions.map(def => def.key)).size !== definitions.length || new Set(definitions.map(def => def.property)).size !== definitions.length) throw Error("Missing or duplicate contract entries")
const groupsSource = source.slice(source.indexOf("export const runtimeGradientGroups"))
export const gradientGroups = [...groupsSource.matchAll(/\[([^\[\]]+)\]/g)].map(([, body]) => [...body.matchAll(/"([^"]+)"/g)].map(([, key]) => key))
if (!gradientGroups.length || gradientGroups.some(group => !group.length || group.some(key => !definitions.some(def => def.key === key)))) throw Error("Unrecognized production gradient groups")
// Build-time check derived from the production allowlist. Runtime uses isRuntimeTheme.
export function validateDocument(theme, fullDefault = false) {
if (!theme || theme.schemaVersion !== 1 || !theme.tokens || typeof theme.tokens !== "object" || Array.isArray(theme.tokens)) throw Error("Expected v1 RuntimeTheme document")
for (const [key, value] of Object.entries(theme.tokens)) {
const definition = definitions.find(def => def.key === key)
if (!definition || typeof value !== "string" || !(definition.type === "color" ? /^#[\da-f]{6}$/i : /^(?:[2-9]|1[0-2])px$/).test(value)) throw Error(`Invalid Theme token: ${key}`)
}
for (const group of gradientGroups) {
const count = group.filter(key => Object.hasOwn(theme.tokens, key)).length
if (count && count !== group.length) throw Error(`Incomplete gradient group: ${group[0]}`)
}
if (fullDefault && definitions.some(def => def.type === "color" && !Object.hasOwn(theme.tokens, def.key))) throw Error("default.json must contain every public color role/palette/gradient value")
if (Object.keys(theme.tokens).some(key => definitions.find(def => def.key === key).type !== "color")) throw Error("Theme artifacts are color-only; geometry belongs to package CSS")
return theme
}