@chappibunny/repolens 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,408 @@
1
+ // Renderers for v0.8.0 Extended Analysis documents:
2
+ // - GraphQL Schema
3
+ // - TypeScript Type Graph
4
+ // - Dependency Graph
5
+ // - Architecture Drift
6
+
7
+ export function renderGraphQLSchema(graphqlResult) {
8
+ if (!graphqlResult?.detected) {
9
+ return "# GraphQL Schema\n\nNo GraphQL schema detected in this repository.\n\n" +
10
+ "RepoLens looks for `.graphql`/`.gql` files, inline SDL (gql tagged templates), " +
11
+ "resolver patterns, and GraphQL libraries (Apollo, Yoga, Nexus, Pothos, etc.).\n";
12
+ }
13
+
14
+ const lines = [];
15
+ lines.push("# GraphQL Schema");
16
+ lines.push("");
17
+ lines.push(`> ${graphqlResult.summary}`);
18
+ lines.push("");
19
+
20
+ // Libraries
21
+ if (graphqlResult.libraries.length > 0) {
22
+ lines.push("## Libraries & Frameworks");
23
+ lines.push("");
24
+ for (const lib of graphqlResult.libraries) {
25
+ lines.push(`- ${lib}`);
26
+ }
27
+ lines.push("");
28
+ }
29
+
30
+ // Schema files
31
+ if (graphqlResult.schemaFiles.length > 0) {
32
+ lines.push("## Schema Files");
33
+ lines.push("");
34
+ for (const file of graphqlResult.schemaFiles) {
35
+ lines.push(`- \`${file}\``);
36
+ }
37
+ lines.push("");
38
+ }
39
+
40
+ // Queries
41
+ if (graphqlResult.queries.length > 0) {
42
+ lines.push("## Queries");
43
+ lines.push("");
44
+ lines.push("| Query | Return Type | Source |");
45
+ lines.push("|-------|-------------|--------|");
46
+ for (const q of graphqlResult.queries) {
47
+ lines.push(`| \`${q.name}\` | \`${q.type}\` | \`${q.source}\` |`);
48
+ }
49
+ lines.push("");
50
+ }
51
+
52
+ // Mutations
53
+ if (graphqlResult.mutations.length > 0) {
54
+ lines.push("## Mutations");
55
+ lines.push("");
56
+ lines.push("| Mutation | Return Type | Source |");
57
+ lines.push("|----------|-------------|--------|");
58
+ for (const m of graphqlResult.mutations) {
59
+ lines.push(`| \`${m.name}\` | \`${m.type}\` | \`${m.source}\` |`);
60
+ }
61
+ lines.push("");
62
+ }
63
+
64
+ // Subscriptions
65
+ if (graphqlResult.subscriptions.length > 0) {
66
+ lines.push("## Subscriptions");
67
+ lines.push("");
68
+ lines.push("| Subscription | Return Type | Source |");
69
+ lines.push("|--------------|-------------|--------|");
70
+ for (const s of graphqlResult.subscriptions) {
71
+ lines.push(`| \`${s.name}\` | \`${s.type}\` | \`${s.source}\` |`);
72
+ }
73
+ lines.push("");
74
+ }
75
+
76
+ // Object Types
77
+ if (graphqlResult.types.length > 0) {
78
+ lines.push("## Object Types");
79
+ lines.push("");
80
+ for (const t of graphqlResult.types) {
81
+ const impl = t.implements?.length ? ` (implements ${t.implements.join(", ")})` : "";
82
+ lines.push(`### \`${t.name}\`${impl}`);
83
+ lines.push("");
84
+ if (t.fields.length > 0) {
85
+ lines.push("| Field | Type |");
86
+ lines.push("|-------|------|");
87
+ for (const f of t.fields) {
88
+ lines.push(`| \`${f.name}\` | \`${f.type}\` |`);
89
+ }
90
+ }
91
+ lines.push(`\n*Source: \`${t.source}\`*\n`);
92
+ }
93
+ }
94
+
95
+ // Enums
96
+ if (graphqlResult.enums.length > 0) {
97
+ lines.push("## Enums");
98
+ lines.push("");
99
+ for (const e of graphqlResult.enums) {
100
+ lines.push(`- **${e.name}**: ${e.values.join(", ")}`);
101
+ }
102
+ lines.push("");
103
+ }
104
+
105
+ // Input Types
106
+ if (graphqlResult.inputs.length > 0) {
107
+ lines.push("## Input Types");
108
+ lines.push("");
109
+ for (const i of graphqlResult.inputs) {
110
+ lines.push(`### \`${i.name}\``);
111
+ lines.push("");
112
+ if (i.fields.length > 0) {
113
+ lines.push("| Field | Type |");
114
+ lines.push("|-------|------|");
115
+ for (const f of i.fields) {
116
+ lines.push(`| \`${f.name}\` | \`${f.type}\` |`);
117
+ }
118
+ }
119
+ lines.push("");
120
+ }
121
+ }
122
+
123
+ // Interfaces
124
+ if (graphqlResult.interfaces.length > 0) {
125
+ lines.push("## Interfaces");
126
+ lines.push("");
127
+ for (const iface of graphqlResult.interfaces) {
128
+ lines.push(`### \`${iface.name}\``);
129
+ lines.push("");
130
+ if (iface.fields.length > 0) {
131
+ lines.push("| Field | Type |");
132
+ lines.push("|-------|------|");
133
+ for (const f of iface.fields) {
134
+ lines.push(`| \`${f.name}\` | \`${f.type}\` |`);
135
+ }
136
+ }
137
+ lines.push("");
138
+ }
139
+ }
140
+
141
+ // Unions
142
+ if (graphqlResult.unions.length > 0) {
143
+ lines.push("## Unions");
144
+ lines.push("");
145
+ for (const u of graphqlResult.unions) {
146
+ lines.push(`- **${u.name}** = ${u.members.join(" | ")}`);
147
+ }
148
+ lines.push("");
149
+ }
150
+
151
+ // Resolver Files
152
+ if (graphqlResult.resolverFiles.length > 0) {
153
+ lines.push("## Resolver Files");
154
+ lines.push("");
155
+ for (const file of graphqlResult.resolverFiles) {
156
+ lines.push(`- \`${file}\``);
157
+ }
158
+ lines.push("");
159
+ }
160
+
161
+ return lines.join("\n");
162
+ }
163
+
164
+ export function renderTypeGraph(tsResult) {
165
+ if (!tsResult?.detected) {
166
+ return "# TypeScript Type Graph\n\nNo TypeScript type declarations detected in this repository.\n\n" +
167
+ "RepoLens looks for `.ts`/`.tsx` files containing interfaces, type aliases, classes, and enums.\n";
168
+ }
169
+
170
+ const lines = [];
171
+ lines.push("# TypeScript Type Graph");
172
+ lines.push("");
173
+ lines.push(`> ${tsResult.summary}`);
174
+ lines.push("");
175
+
176
+ // Interfaces
177
+ if (tsResult.interfaces.length > 0) {
178
+ lines.push("## Interfaces");
179
+ lines.push("");
180
+ lines.push("| Interface | Extends | Source |");
181
+ lines.push("|-----------|---------|--------|");
182
+ for (const iface of tsResult.interfaces) {
183
+ const ext = iface.extends.length > 0 ? iface.extends.join(", ") : "—";
184
+ lines.push(`| \`${iface.name}\` | ${ext} | \`${iface.source}\` |`);
185
+ }
186
+ lines.push("");
187
+ }
188
+
189
+ // Classes
190
+ if (tsResult.classes.length > 0) {
191
+ lines.push("## Classes");
192
+ lines.push("");
193
+ lines.push("| Class | Extends | Implements | Source |");
194
+ lines.push("|-------|---------|------------|--------|");
195
+ for (const cls of tsResult.classes) {
196
+ const ext = cls.extends || "—";
197
+ const impl = cls.implements.length > 0 ? cls.implements.join(", ") : "—";
198
+ lines.push(`| \`${cls.name}\` | ${ext} | ${impl} | \`${cls.source}\` |`);
199
+ }
200
+ lines.push("");
201
+ }
202
+
203
+ // Type Aliases
204
+ if (tsResult.typeAliases.length > 0) {
205
+ lines.push("## Type Aliases");
206
+ lines.push("");
207
+ lines.push("| Type | References | Source |");
208
+ lines.push("|------|------------|--------|");
209
+ for (const t of tsResult.typeAliases) {
210
+ const refs = t.refs.length > 0 ? t.refs.join(", ") : "—";
211
+ lines.push(`| \`${t.name}\` | ${refs} | \`${t.source}\` |`);
212
+ }
213
+ lines.push("");
214
+ }
215
+
216
+ // Enums
217
+ if (tsResult.enums.length > 0) {
218
+ lines.push("## Enums");
219
+ lines.push("");
220
+ lines.push("| Enum | Source |");
221
+ lines.push("|------|--------|");
222
+ for (const e of tsResult.enums) {
223
+ lines.push(`| \`${e.name}\` | \`${e.source}\` |`);
224
+ }
225
+ lines.push("");
226
+ }
227
+
228
+ // Relationship Graph (Unicode)
229
+ if (tsResult.relationships.length > 0) {
230
+ lines.push("## Type Relationships");
231
+ lines.push("");
232
+ lines.push("```");
233
+ // Group by source type
234
+ const byFrom = new Map();
235
+ for (const rel of tsResult.relationships) {
236
+ if (!byFrom.has(rel.from)) byFrom.set(rel.from, []);
237
+ byFrom.get(rel.from).push(rel);
238
+ }
239
+ for (const [from, rels] of byFrom) {
240
+ lines.push(`${from}`);
241
+ for (let i = 0; i < rels.length; i++) {
242
+ const connector = i === rels.length - 1 ? "└──" : "├──";
243
+ const arrow = rels[i].type === "extends" ? "extends" :
244
+ rels[i].type === "implements" ? "implements" :
245
+ rels[i].type === "references" ? "uses" : rels[i].type;
246
+ lines.push(` ${connector} ${arrow} → ${rels[i].to}`);
247
+ }
248
+ }
249
+ lines.push("```");
250
+ lines.push("");
251
+ }
252
+
253
+ return lines.join("\n");
254
+ }
255
+
256
+ export function renderDependencyGraph(depResult) {
257
+ if (!depResult?.nodes?.length) {
258
+ return "# Dependency Graph\n\nNo code files found to analyze for dependencies.\n";
259
+ }
260
+
261
+ const lines = [];
262
+ lines.push("# Dependency Graph");
263
+ lines.push("");
264
+ lines.push(`> ${depResult.summary}`);
265
+ lines.push("");
266
+
267
+ // Stats overview
268
+ lines.push("## Overview");
269
+ lines.push("");
270
+ const s = depResult.stats;
271
+ lines.push(`| Metric | Value |`);
272
+ lines.push(`|--------|-------|`);
273
+ lines.push(`| Source files | ${s.totalFiles} |`);
274
+ lines.push(`| Import edges | ${s.totalEdges} |`);
275
+ lines.push(`| External packages | ${s.externalDeps} |`);
276
+ lines.push(`| Circular dependencies | ${s.cycles} |`);
277
+ lines.push(`| Orphan files | ${s.orphanFiles} |`);
278
+ lines.push("");
279
+
280
+ // Hub modules (most imported)
281
+ if (s.hubs.length > 0) {
282
+ lines.push("## Hub Modules (Most Imported)");
283
+ lines.push("");
284
+ lines.push("| Module | Imported By |");
285
+ lines.push("|--------|-------------|");
286
+ for (const hub of s.hubs) {
287
+ lines.push(`| \`${hub.key}\` | ${hub.importedBy} files |`);
288
+ }
289
+ lines.push("");
290
+ }
291
+
292
+ // Circular dependencies
293
+ if (depResult.cycles.length > 0) {
294
+ lines.push("## ⚠️ Circular Dependencies");
295
+ lines.push("");
296
+ lines.push("The following circular dependency chains were detected:");
297
+ lines.push("");
298
+ for (let i = 0; i < Math.min(depResult.cycles.length, 20); i++) {
299
+ const cycle = depResult.cycles[i];
300
+ lines.push(`${i + 1}. \`${cycle.join("` → `")}\``);
301
+ }
302
+ if (depResult.cycles.length > 20) {
303
+ lines.push(`\n*...and ${depResult.cycles.length - 20} more cycles*`);
304
+ }
305
+ lines.push("");
306
+ }
307
+
308
+ // External dependencies
309
+ if (depResult.externalDeps.length > 0) {
310
+ lines.push("## External Dependencies");
311
+ lines.push("");
312
+ const cols = 3;
313
+ const perCol = Math.ceil(depResult.externalDeps.length / cols);
314
+ for (const dep of depResult.externalDeps) {
315
+ lines.push(`- \`${dep}\``);
316
+ }
317
+ lines.push("");
318
+ }
319
+
320
+ return lines.join("\n");
321
+ }
322
+
323
+ export function renderArchitectureDrift(driftResult) {
324
+ const lines = [];
325
+ lines.push("# Architecture Drift Report");
326
+ lines.push("");
327
+
328
+ if (!driftResult.hasBaseline) {
329
+ lines.push("> " + driftResult.summary);
330
+ lines.push("");
331
+ lines.push("Once a baseline is established, this report will track structural changes including:");
332
+ lines.push("- New/removed modules and API endpoints");
333
+ lines.push("- Dependency shifts and circular dependency trends");
334
+ lines.push("- Framework and technology stack changes");
335
+ lines.push("- GraphQL schema evolution");
336
+ lines.push("- Overall codebase scale changes");
337
+ lines.push("");
338
+ return lines.join("\n");
339
+ }
340
+
341
+ lines.push(`> Compared against baseline from **${driftResult.baselineTimestamp}**`);
342
+ lines.push("");
343
+ lines.push(`**${driftResult.summary}**`);
344
+ lines.push("");
345
+
346
+ if (driftResult.drifts.length === 0) {
347
+ lines.push("✅ No architecture drift detected. The codebase structure matches the baseline.");
348
+ lines.push("");
349
+ return lines.join("\n");
350
+ }
351
+
352
+ // Group drifts by severity
353
+ const critical = driftResult.drifts.filter(d => d.severity === "critical");
354
+ const warnings = driftResult.drifts.filter(d => d.severity === "warning");
355
+ const infos = driftResult.drifts.filter(d => d.severity === "info");
356
+
357
+ if (critical.length > 0) {
358
+ lines.push("## 🔴 Critical Changes");
359
+ lines.push("");
360
+ for (const drift of critical) {
361
+ lines.push(`### ${formatCategoryLabel(drift.category)} — ${drift.type}`);
362
+ for (const item of drift.items) {
363
+ lines.push(`- ${item}`);
364
+ }
365
+ lines.push("");
366
+ }
367
+ }
368
+
369
+ if (warnings.length > 0) {
370
+ lines.push("## 🟡 Warnings");
371
+ lines.push("");
372
+ for (const drift of warnings) {
373
+ lines.push(`### ${formatCategoryLabel(drift.category)} — ${drift.type}`);
374
+ for (const item of drift.items) {
375
+ lines.push(`- ${item}`);
376
+ }
377
+ lines.push("");
378
+ }
379
+ }
380
+
381
+ if (infos.length > 0) {
382
+ lines.push("## 🟢 Informational");
383
+ lines.push("");
384
+ for (const drift of infos) {
385
+ lines.push(`### ${formatCategoryLabel(drift.category)} — ${drift.type}`);
386
+ for (const item of drift.items) {
387
+ lines.push(`- ${item}`);
388
+ }
389
+ lines.push("");
390
+ }
391
+ }
392
+
393
+ return lines.join("\n");
394
+ }
395
+
396
+ function formatCategoryLabel(category) {
397
+ const labels = {
398
+ modules: "Modules",
399
+ api: "API Endpoints",
400
+ pages: "Pages",
401
+ dependencies: "Dependencies",
402
+ frameworks: "Frameworks",
403
+ cycles: "Circular Dependencies",
404
+ graphql: "GraphQL Schema",
405
+ scale: "Codebase Scale",
406
+ };
407
+ return labels[category] || category;
408
+ }
@@ -21,6 +21,10 @@ const ALL_DOCUMENT_KEYS = [
21
21
  "data_flows",
22
22
  "change_impact",
23
23
  "developer_onboarding",
24
+ "graphql_schema",
25
+ "type_graph",
26
+ "dependency_graph",
27
+ "architecture_drift",
24
28
  ];
25
29
 
26
30
  /**