-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcheck-render-output.mjs
More file actions
117 lines (99 loc) · 3.88 KB
/
Copy pathcheck-render-output.mjs
File metadata and controls
117 lines (99 loc) · 3.88 KB
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#!/usr/bin/env node
import { existsSync, readFileSync } from "node:fs";
function parseArgs(argv) {
const args = {
graph: "fixtures/sample-repo/docs/ai/visualize/codebase-graph.json",
html: "fixtures/sample-repo/docs/ai/visualize/codebase-map.html",
};
for (let i = 0; i < argv.length; i++) {
const arg = argv[i];
if (arg === "--graph") args.graph = argv[++i];
else if (arg === "--html") args.html = argv[++i];
else if (arg === "--help" || arg === "-h") args.help = true;
}
return args;
}
function usage() {
console.log(`Usage: node scripts/check-render-output.mjs [--graph <graph.json>] [--html <map.html>]
Checks that a generated graph and HTML map are populated, self-contained, and
free of visualizer-output graph nodes.
`);
}
function fail(message) {
console.error(`check:render failed - ${message}`);
process.exit(1);
}
function readJson(path) {
try {
return JSON.parse(readFileSync(path, "utf8"));
} catch (err) {
fail(`could not parse JSON at ${path}: ${err.message}`);
}
}
function requireFile(path, label) {
if (!existsSync(path)) fail(`${label} does not exist: ${path}`);
}
function extractGraphData(html) {
const match = html.match(/<script id="graph-data" type="application\/json">([\s\S]*?)<\/script>/);
if (!match) fail("HTML is missing the graph-data script block");
if (!match[1].trim()) fail("graph-data script block is empty");
try {
return JSON.parse(match[1]);
} catch (err) {
fail(`embedded graph-data JSON does not parse: ${err.message}`);
}
}
function countReferences(edges) {
return edges.filter((edge) => edge.type === "references").length;
}
function main() {
const args = parseArgs(process.argv.slice(2));
if (args.help) {
usage();
process.exit(0);
}
requireFile(args.graph, "graph");
requireFile(args.html, "HTML map");
const graph = readJson(args.graph);
const html = readFileSync(args.html, "utf8");
const embedded = extractGraphData(html);
const nodes = graph.nodes || [];
const edges = graph.edges || [];
if (!Array.isArray(nodes) || !nodes.length) fail("graph has no nodes");
if (!Array.isArray(edges)) fail("graph edges are not an array");
if (!graph.metadata?.target_root) fail("graph metadata is missing target_root");
if (!graph.metadata?.generated_at) fail("graph metadata is missing generated_at");
if (!graph.metadata?.producer_version) fail("graph metadata is missing producer_version");
const embeddedNodes = embedded.nodes || [];
const embeddedEdges = embedded.edges || [];
if (embeddedNodes.length !== nodes.length) fail("embedded graph node count does not match graph JSON");
if (embeddedEdges.length !== edges.length) fail("embedded graph edge count does not match graph JSON");
const visualizerNodes = nodes
.map((node) => node.path || node.id || "")
.filter((path) => path.startsWith("docs/ai/visualize/"));
if (visualizerNodes.length) {
fail(`graph contains visualizer output nodes: ${visualizerNodes.slice(0, 5).join(", ")}`);
}
for (const id of ["h-target", "h-gen", "overview", "groups", "important", "center-panel", "detail"]) {
if (!html.includes(`id="${id}"`)) fail(`HTML is missing #${id}`);
}
for (const marker of [
"How to use this map",
"Copy agent briefing",
"graph-help-line",
"Sparse references",
"Center selected node or current focus",
]) {
if (!html.includes(marker)) fail(`HTML is missing renderer guidance marker: ${marker}`);
}
if (/<(script|link|img|iframe)\b[^>]*(src|href)=/i.test(html)) {
fail("HTML contains external resource references");
}
const fileCount = nodes.filter((node) => node.type === "file").length;
const folderCount = nodes.filter((node) => node.type === "folder").length;
const references = countReferences(edges);
console.log(
`check:render OK - ${fileCount} files, ${folderCount} folders, ${edges.length} edges, ${references} references edges.`,
);
}
main();