Initial commit: HMDesignSystem baseline
This commit is contained in:
commit
b6a0a61103
486 files changed
+68432
No files matched your search
@@ -0,0 +1,38 @@
|
||||
import { writeFileSync } from "node:fs"
|
||||
import { definitions, read, repositoryRoot, validateDocument } from "./theme-contract.mjs"
|
||||
|
||||
// themes/default.json is the authored COLOR source. Templates own aliases/geometry.
|
||||
const theme = validateDocument(JSON.parse(read("themes/default.json")), true)
|
||||
const templates = ["primitives", "default"].map(name => ({ name, source: read(`scripts/theme-templates/${name}.css`) }))
|
||||
const graph = new Map(templates.flatMap(({ source }) => [...source.replace(/\/\*[\s\S]*?\*\//g, "").matchAll(/(--[\w-]+)\s*:\s*([^;]+);/g)].map(([, key, value]) => [key, value.trim()])))
|
||||
const publicProperties = new Map(definitions.filter(def => def.type === "color").map(def => [def.property, theme.tokens[def.key].toLowerCase()]))
|
||||
function resolve(value, stack = []) {
|
||||
const placeholder = /^\{\{([^|}]+)(?:\|([^}]+))?\}\}$/.exec(value)
|
||||
if (placeholder) return theme.tokens[placeholder[1]].toLowerCase()
|
||||
const alias = /^var\((--[\w-]+)\)$/.exec(value)
|
||||
if (!alias) return value
|
||||
const property = alias[1]
|
||||
if (stack.includes(property)) throw Error(`Circular fallback alias: ${property}`)
|
||||
if (publicProperties.has(property)) return publicProperties.get(property)
|
||||
if (!graph.has(property)) throw Error(`Missing fallback alias: ${property}`)
|
||||
return resolve(graph.get(property), [...stack, property])
|
||||
}
|
||||
function render(source) {
|
||||
return source.replace(/\{\{([^|}]+)(?:\|([^}]+))?\}\}/g, (_, key, alias) => {
|
||||
if (!Object.hasOwn(theme.tokens, key)) throw Error(`Missing template color: ${key}`)
|
||||
const value = theme.tokens[key].toLowerCase()
|
||||
// Preserve existing palette aliases when they match the canonical Default.
|
||||
// A separately configured role always wins if its JSON value differs.
|
||||
return alias && resolve(alias) === value ? alias : value
|
||||
})
|
||||
}
|
||||
const defaults = Object.fromEntries(definitions.map(def => [def.property, def.type === "color" ? theme.tokens[def.key].toLowerCase() : resolve(graph.get(def.property))]))
|
||||
for (const def of definitions) if (!(def.type === "color" ? /^#[\da-f]{6}$/i : /^(?:[2-9]|1[0-2])px$/).test(defaults[def.property])) throw Error(`Unresolved fallback: ${def.property}`)
|
||||
const outputs = templates.map(({name,source}) => [`packages/ui/src/theme/${name}.css`, `/* Generated from themes/default.json + scripts/theme-templates/${name}.css. Do not edit. */\n${render(source)}`])
|
||||
outputs.push(["packages/ui/src/theme/default-values.generated.ts", `// Generated by npm run generate:theme-defaults. Do not edit.\n// Colors: themes/default.json; geometry/aliases: scripts/theme-templates.\nexport const runtimeThemeDefaults = ${JSON.stringify(defaults, null, 2)} as const\n`])
|
||||
for (const [path, content] of outputs) {
|
||||
if (process.argv.includes("--check")) {
|
||||
if (read(path) !== content) throw Error(`Stale ${path}. Run npm run generate:theme-defaults.`)
|
||||
} else writeFileSync(new URL(path, repositoryRoot), content)
|
||||
}
|
||||
console.log(`Theme defaults: ${Object.keys(theme.tokens).length} JSON colors / ${definitions.length} contract defaults; ${process.argv.includes("--check") ? "generated outputs match" : "generated CSS + data"}`)
|
||||
Reference in new issue
Block a user