/** * Lint an eidos component CSS against its morfo contract. * * Usage: npx tsx scripts/eidos-lint.ts * e.g. npx tsx scripts/eidos-lint.ts toggle * * Reads `src/uix/morfo/components/{name}.ts` + * `src/uix/eidos/components/{name}/{name}.css` (or legacy flat * `components/{name}.css`), compiles the morfo, and reports which * selectors are morfo-backed, eidos-only DOM signals/wrapper attrs, or * invalid (value not in declared enum). */ import { readFileSync, existsSync } from 'node:fs' import { resolve } from 'node:path' import { compileMorfo } from '../src/uix/morfo/compile' import { formatLintReport, lintEidosCss } from '../src/uix/eidos/lint' import type { Morfo } from '../src/uix/morfo/types' async function main() { const name = process.argv[2] if (!name) { console.error('Usage: tsx scripts/eidos-lint.ts ') process.exit(2) } const morfoPath = resolve(`src/uix/morfo/components/${name}.ts`) const subfolderCssPath = resolve(`src/uix/eidos/components/${name}/${name}.css`) const flatCssPath = resolve(`src/uix/eidos/components/${name}.css`) const cssPath = existsSync(subfolderCssPath) ? subfolderCssPath : flatCssPath if (!existsSync(morfoPath)) { console.error(`No morfo file at ${morfoPath}`) process.exit(2) } if (!existsSync(cssPath)) { console.error(`No eidos CSS at ${subfolderCssPath} or ${flatCssPath}`) process.exit(2) } const mod = (await import(`../src/uix/morfo/components/${name}`)) as Record const morfoExportName = `${name.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase())}Morfo` const morfo = mod[morfoExportName] as Morfo | undefined if (!morfo) { console.error(`Module exports ${Object.keys(mod).join(', ')}; expected ${morfoExportName}.`) process.exit(2) } const compiled = compileMorfo(morfo) const css = readFileSync(cssPath, 'utf-8') const report = lintEidosCss(css, compiled) console.log(formatLintReport(report)) if (report.counts.invalid > 0) process.exit(1) } main().catch((err) => { console.error(err) process.exit(2) })