release: complete UI component catalog 0.2.58
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
import { readdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { basename, join } from "node:path";
|
||||
|
||||
const uiRoot = join(import.meta.dirname, "..", "packages", "ui");
|
||||
const componentsDir = join(uiRoot, "components");
|
||||
const catalog = JSON.parse(readFileSync(join(uiRoot, "component-catalog.json"), "utf8"));
|
||||
const metadata = new Map(catalog.components.map((entry) => [entry.name, entry]));
|
||||
|
||||
function block(source, keyword) {
|
||||
const start = source.indexOf(`${keyword} {`);
|
||||
if (start < 0) return "";
|
||||
const open = source.indexOf("{", start);
|
||||
let depth = 0;
|
||||
for (let index = open; index < source.length; index++) {
|
||||
if (source[index] === "{") depth++;
|
||||
if (source[index] === "}" && --depth === 0) return source.slice(open + 1, index);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function propsOf(source) {
|
||||
const props = [];
|
||||
for (const line of block(source, "props").split(/\r?\n/)) {
|
||||
const match = line.trim().match(/^([A-Za-z][A-Za-z0-9_]*)\s*(?:=\s*(.+))?$/);
|
||||
if (!match) continue;
|
||||
const [, name, rawDefault] = match;
|
||||
const value = rawDefault?.trim();
|
||||
const type =
|
||||
value === "true" || value === "false"
|
||||
? "boolean"
|
||||
: /^-?\d+(?:\.\d+)?$/.test(value ?? "")
|
||||
? "number"
|
||||
: "string";
|
||||
props.push({ name, type, required: value === undefined, default: value ?? null });
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
function words(name) {
|
||||
return name.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase();
|
||||
}
|
||||
|
||||
const components = readdirSync(componentsDir)
|
||||
.filter((file) => file.endsWith(".wrn"))
|
||||
.sort((a, b) => a.localeCompare(b))
|
||||
.map((file) => {
|
||||
const name = basename(file, ".wrn");
|
||||
const source = readFileSync(join(componentsDir, file), "utf8");
|
||||
const entry = metadata.get(name);
|
||||
const slots = [...source.matchAll(/<slot(?:\s+name="([^"]+)")?/g)].map(
|
||||
(match) => match[1] ?? "default",
|
||||
);
|
||||
const events = [
|
||||
...new Set([...source.matchAll(/@([A-Za-z][A-Za-z0-9_-]*)=/g)].map((match) => match[1])),
|
||||
];
|
||||
return {
|
||||
name,
|
||||
mount: name,
|
||||
category: entry?.category ?? "core",
|
||||
purpose: entry?.purpose ?? `Reusable ${words(name)} component.`,
|
||||
props: propsOf(source),
|
||||
slots: [...new Set(slots)],
|
||||
events,
|
||||
source: `components/${file}`,
|
||||
};
|
||||
});
|
||||
|
||||
writeFileSync(
|
||||
join(uiRoot, "component-reference.json"),
|
||||
JSON.stringify(
|
||||
{ generatedFrom: "packages/ui/components/*.wrn", count: components.length, components },
|
||||
null,
|
||||
2,
|
||||
) + "\n",
|
||||
);
|
||||
|
||||
const lines = [
|
||||
"# WRNexus UI component reference",
|
||||
"",
|
||||
`This reference is generated from the ${components.length} packaged \`.wrn\` component sources. Props marked required have no default; all others show their runtime default.`,
|
||||
"",
|
||||
];
|
||||
for (const category of [...new Set(components.map((component) => component.category))].sort()) {
|
||||
lines.push(`## ${category[0].toUpperCase()}${category.slice(1)}`, "");
|
||||
for (const component of components.filter((item) => item.category === category)) {
|
||||
const props = component.props.length
|
||||
? component.props
|
||||
.map(
|
||||
(prop) =>
|
||||
`\`${prop.name}: ${prop.type}${prop.required ? " (required)" : ` = ${prop.default}`}\``,
|
||||
)
|
||||
.join(", ")
|
||||
: "None";
|
||||
lines.push(
|
||||
`### ${component.name}`,
|
||||
"",
|
||||
component.purpose,
|
||||
"",
|
||||
`- Mount: \`data-component="${component.mount}"\``,
|
||||
`- Props: ${props}`,
|
||||
`- Slots: ${component.slots.length ? component.slots.map((slot) => `\`${slot}\``).join(", ") : "None"}`,
|
||||
`- Events: ${component.events.length ? component.events.map((event) => `\`${event}\``).join(", ") : "None"}`,
|
||||
"",
|
||||
);
|
||||
}
|
||||
}
|
||||
writeFileSync(join(uiRoot, "COMPONENTS.md"), `${lines.join("\n")}\n`);
|
||||
Reference in New Issue
Block a user