/** * Action: page-labels-validation * * Validates shape labels on a single page and reports three categories of issues: * 1. empty_label — shape has no label (or whitespace only) * 2. duplicate_label — two or more shapes share the same label * 3. long_label — label exceeds MAX_LABEL_LENGTH characters * * Edges are excluded (connectors often have no label by design). */ import { parseDiagram } from "../../services/drawio-parser/parser.js"; const MAX_LABEL_LENGTH = 80; export function run(filePath: string): Record { const { shapes } = parseDiagram(filePath); const issues: Record[] = []; // Track label → [ids] for duplicate detection const labelIndex = new Map(); for (const [id, shape] of shapes) { const label = shape.label?.trim() ?? ""; // 1. Empty label if (label === "") { issues.push({ type: "empty_label", shapeId: id }); continue; // skip further checks for unlabelled shapes } // 2. Long label if (label.length > MAX_LABEL_LENGTH) { issues.push({ type: "long_label", shapeId: id, label, length: label.length, maxAllowed: MAX_LABEL_LENGTH, }); } // Accumulate for duplicate check if (!labelIndex.has(label)) labelIndex.set(label, []); labelIndex.get(label)!.push(id); } // 3. Duplicate labels for (const [label, ids] of labelIndex) { if (ids.length > 1) { issues.push({ type: "duplicate_label", label, shapeIds: ids, count: ids.length, }); } } const empty = issues.filter((i) => i["type"] === "empty_label").length; const duplicate = issues.filter((i) => i["type"] === "duplicate_label").length; const long = issues.filter((i) => i["type"] === "long_label").length; return { action: "page-labels-validation", file: filePath, config: { maxLabelLength: MAX_LABEL_LENGTH }, summary: { emptyLabels: empty, duplicateLabels: duplicate, longLabels: long, totalIssues: issues.length, }, issues, }; }