All files / packages/design-tokens/src/validators css-properties-audit.ts

100% Statements 27/27
100% Branches 12/12
100% Functions 8/8
100% Lines 22/22

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63                                            8x   8x 8x 8x 8x   8x 8x 1x   7x 1x 1x   6x   9x 7x       7x     8x           8x 1x   6x 6x     7x 7x  
#!/usr/bin/env node
 
/**
 * Post-build validator that ensures every --slds-g-* token's cssProperties match
 * the role encoded in its name (e.g. surface/container → background only,
 * sizing-border → border-width, spacing-var-block → block axis only).
 *
 * Usage:
 *   yarn validate:css-properties                         # Audit every built theme
 *   yarn validate:css-properties --theme=cosmos          # Audit a single theme
 *   yarn validate:css-properties --json                  # Machine-readable output
 *   yarn validate:css-properties path/to/file.json       # Audit a specific *.global.tokens.flat.json
 *
 * Exits 1 when any anomaly is found.
 * This is the CLI entry point. The rules live in css-properties-audit-utils.ts.
 */
 
import fs from 'node:fs';
import path from 'node:path';
import { auditTokenProperties, printReport, toAuditEntries } from './css-properties-audit-utils.js';
import { parseValidatorArgs } from './validator-utils.js';
 
const THEMES_DIR = path.resolve(process.cwd(), 'dist/themes');
 
const { themeFilter } = parseValidatorArgs();
const args = process.argv.slice(2);
const asJson = args.includes('--json');
const positional = args.filter((arg) => !arg.startsWith('-'));
 
const getInputs = (): Array<{ label: string; file: string }> => {
  if (positional.length > 0) {
    return positional.map((file) => ({ label: file, file: path.resolve(file) }));
  }
  if (!fs.existsSync(THEMES_DIR)) {
    console.error(`āŒ ${THEMES_DIR} not found. Run \`yarn build\` first.`);
    process.exit(1);
  }
  return fs
    .readdirSync(THEMES_DIR)
    .filter((theme) => !themeFilter || theme === themeFilter)
    .map((theme) => ({
      label: theme,
      file: path.join(THEMES_DIR, theme, `${theme}.global.tokens.flat.json`),
    }))
    .filter(({ file }) => fs.existsSync(file));
};
 
const results = getInputs().map(({ label, file }) => ({
  label,
  file,
  ...auditTokenProperties(toAuditEntries(JSON.parse(fs.readFileSync(file, 'utf8')))),
}));
 
if (asJson) {
  console.log(JSON.stringify(results, null, 2));
} else {
  console.log('\nšŸ” CSS Properties Audit\n');
  for (const result of results) printReport(result.label, result);
}
 
const total = results.reduce((sum, result) => sum + result.findings.length, 0);
process.exit(total > 0 || results.length === 0 ? 1 : 0);