#!/usr/bin/env node
import fs from "node:fs/promises";
import path from "node:path";
const input = process.argv[2] ?? "tokens";
const output = process.argv[3] ?? "color-preview.html";
function isColor(value) {
return (
typeof value === "string" &&
(
/^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i.test(value) ||
/^rgb\(/i.test(value) ||
/^rgba\(/i.test(value) ||
/^hsl\(/i.test(value) ||
/^hsla\(/i.test(value) ||
/^oklch\(/i.test(value) ||
/^color\(/i.test(value)
)
);
}
function unwrapToken(node) {
if (!node || typeof node !== "object") return node;
if ("$value" in node) return node.$value;
if ("value" in node) return node.value;
return node;
}
async function walkFiles(dir) {
const out = [];
const entries = await fs.readdir(dir, { withFileTypes: true });
for (const entry of entries) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) {
out.push(...await walkFiles(full));
} else if (/\.(json|tokens\.json)$/i.test(entry.name)) {
out.push(full);
}
}
return out;
}
function collectColors(obj, prefix = [], source = "unknown") {
const colors = [];
if (!obj || typeof obj !== "object") return colors;
for (const [key, raw] of Object.entries(obj)) {
const value = unwrapToken(raw);
const name = [...prefix, key];
if (isColor(value)) {
colors.push({
name: name.join("."),
value,
source
});
} else if (value && typeof value === "object") {
colors.push(...collectColors(value, name, source));
}
}
return colors;
}
function htmlEscape(s) {
return String(s)
.replaceAll("&", "&")
.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll('"', """);
}
function makeHtml(colors) {
const cards = colors.map(c => `
No colors found.
"}