@ts-for-gir/reporter 4.0.0-beta.39 → 4.0.0-beta.41
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.
- package/package.json +3 -2
- package/src/console-reporter.ts +56 -197
- package/src/index.ts +1 -0
- package/src/lazy-reporter.ts +35 -0
- package/src/message-analyzer.ts +4 -4
- package/src/reporter-base.ts +5 -1
- package/src/reporter-service.ts +18 -237
- package/src/types/report.ts +218 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ts-for-gir/reporter",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.41",
|
|
4
4
|
"description": "Problem reporting and comprehensive generation analysis for ts-for-gir",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"module": "src/index.ts",
|
|
@@ -37,7 +37,8 @@
|
|
|
37
37
|
"analysis"
|
|
38
38
|
],
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@
|
|
40
|
+
"@ts-for-gir/tsconfig": "^4.0.0-beta.41",
|
|
41
|
+
"@types/node": "^24.12.0",
|
|
41
42
|
"typescript": "^5.9.3"
|
|
42
43
|
},
|
|
43
44
|
"dependencies": {
|
package/src/console-reporter.ts
CHANGED
|
@@ -8,8 +8,9 @@ import { blue, gray, green, red, yellow, yellowBright } from "colorette";
|
|
|
8
8
|
import { PACKAGE_VERSION } from "./constants.ts";
|
|
9
9
|
import { analyzeError, analyzeWarning } from "./message-analyzer.ts";
|
|
10
10
|
import { ReporterBase } from "./reporter-base.ts";
|
|
11
|
-
import type { GenerationReport,
|
|
11
|
+
import type { GenerationReport, ReporterConfig } from "./types/index.ts";
|
|
12
12
|
import { ProblemCategory, ProblemSeverity } from "./types/index.ts";
|
|
13
|
+
import { computeProblemStatistics, generateReportSummary, groupProblemsByCategory } from "./types/report.ts";
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Console Reporter implementation with full logging capabilities
|
|
@@ -146,10 +147,16 @@ export class ConsoleReporter extends ReporterBase {
|
|
|
146
147
|
// === Problem-specific reporting methods ===
|
|
147
148
|
|
|
148
149
|
public reportTypeResolutionError(typeName: string, namespace: string, message: string, details?: string): void {
|
|
149
|
-
this.addProblem(
|
|
150
|
-
|
|
150
|
+
this.addProblem(
|
|
151
|
+
ProblemSeverity.ERROR,
|
|
152
|
+
ProblemCategory.TYPE_RESOLUTION,
|
|
153
|
+
message,
|
|
154
|
+
details,
|
|
151
155
|
typeName,
|
|
152
|
-
|
|
156
|
+
namespace,
|
|
157
|
+
undefined,
|
|
158
|
+
namespace,
|
|
159
|
+
);
|
|
153
160
|
|
|
154
161
|
if (this.config.verbose) {
|
|
155
162
|
const txt = this.prependInfo(message, "ERROR:");
|
|
@@ -157,11 +164,23 @@ export class ConsoleReporter extends ReporterBase {
|
|
|
157
164
|
}
|
|
158
165
|
}
|
|
159
166
|
|
|
160
|
-
public reportTypeResolutionWarning(
|
|
161
|
-
|
|
162
|
-
|
|
167
|
+
public reportTypeResolutionWarning(
|
|
168
|
+
typeName: string,
|
|
169
|
+
namespace: string,
|
|
170
|
+
message: string,
|
|
171
|
+
details?: string,
|
|
172
|
+
sourceModule?: string,
|
|
173
|
+
): void {
|
|
174
|
+
this.addProblem(
|
|
175
|
+
ProblemSeverity.WARNING,
|
|
176
|
+
ProblemCategory.TYPE_RESOLUTION,
|
|
177
|
+
message,
|
|
178
|
+
details,
|
|
163
179
|
typeName,
|
|
164
|
-
|
|
180
|
+
namespace,
|
|
181
|
+
sourceModule ? { sourceModule } : undefined,
|
|
182
|
+
namespace,
|
|
183
|
+
);
|
|
165
184
|
|
|
166
185
|
if (this.config.verbose) {
|
|
167
186
|
const txt = this.prependInfo(message, "WARN:");
|
|
@@ -204,10 +223,16 @@ export class ConsoleReporter extends ReporterBase {
|
|
|
204
223
|
public reportTypeConflict(conflictType: string, elementName: string, namespace: string, details?: string): void {
|
|
205
224
|
const message = `Type conflict (${conflictType}): ${elementName}`;
|
|
206
225
|
|
|
207
|
-
this.addProblem(
|
|
208
|
-
|
|
226
|
+
this.addProblem(
|
|
227
|
+
ProblemSeverity.WARNING,
|
|
228
|
+
ProblemCategory.TYPE_CONFLICT,
|
|
229
|
+
message,
|
|
230
|
+
details,
|
|
231
|
+
elementName,
|
|
209
232
|
namespace,
|
|
210
|
-
|
|
233
|
+
{ conflictType },
|
|
234
|
+
namespace,
|
|
235
|
+
);
|
|
211
236
|
|
|
212
237
|
if (this.config.verbose) {
|
|
213
238
|
const txt = this.prependInfo(message, "WARN:");
|
|
@@ -235,182 +260,9 @@ export class ConsoleReporter extends ReporterBase {
|
|
|
235
260
|
|
|
236
261
|
// === Report generation methods ===
|
|
237
262
|
|
|
238
|
-
private generateStatistics(): ReportStatistics {
|
|
239
|
-
const bySeverity = Object.values(ProblemSeverity).reduce(
|
|
240
|
-
(acc, severity) => {
|
|
241
|
-
acc[severity] = 0;
|
|
242
|
-
return acc;
|
|
243
|
-
},
|
|
244
|
-
{} as Record<ProblemSeverity, number>,
|
|
245
|
-
);
|
|
246
|
-
|
|
247
|
-
const byCategory = Object.values(ProblemCategory).reduce(
|
|
248
|
-
(acc, category) => {
|
|
249
|
-
acc[category] = 0;
|
|
250
|
-
return acc;
|
|
251
|
-
},
|
|
252
|
-
{} as Record<ProblemCategory, number>,
|
|
253
|
-
);
|
|
254
|
-
|
|
255
|
-
const byModule: Record<string, number> = {};
|
|
256
|
-
|
|
257
|
-
// Type-specific tracking
|
|
258
|
-
const unresolvedTypes: Record<string, { count: number; namespaces: Set<string> }> = {};
|
|
259
|
-
const typeConflicts: Record<string, { count: number; examples: Set<string> }> = {};
|
|
260
|
-
const namespaceProblems: Record<string, { count: number; types: Set<string> }> = {};
|
|
261
|
-
|
|
262
|
-
for (const problem of this.problems) {
|
|
263
|
-
bySeverity[problem.severity]++;
|
|
264
|
-
byCategory[problem.category]++;
|
|
265
|
-
byModule[problem.module] = (byModule[problem.module] || 0) + 1;
|
|
266
|
-
|
|
267
|
-
// Track type resolution problems
|
|
268
|
-
if (problem.category === ProblemCategory.TYPE_RESOLUTION && problem.typeName) {
|
|
269
|
-
if (!unresolvedTypes[problem.typeName]) {
|
|
270
|
-
unresolvedTypes[problem.typeName] = { count: 0, namespaces: new Set() };
|
|
271
|
-
}
|
|
272
|
-
unresolvedTypes[problem.typeName].count++;
|
|
273
|
-
if (problem.location) {
|
|
274
|
-
unresolvedTypes[problem.typeName].namespaces.add(problem.location);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// Track namespace problems
|
|
278
|
-
if (problem.location) {
|
|
279
|
-
if (!namespaceProblems[problem.location]) {
|
|
280
|
-
namespaceProblems[problem.location] = { count: 0, types: new Set() };
|
|
281
|
-
}
|
|
282
|
-
namespaceProblems[problem.location].count++;
|
|
283
|
-
namespaceProblems[problem.location].types.add(problem.typeName);
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// Track type conflicts
|
|
288
|
-
if (problem.category === ProblemCategory.TYPE_CONFLICT && problem.metadata?.conflictType) {
|
|
289
|
-
const conflictType = problem.metadata.conflictType as string;
|
|
290
|
-
if (!typeConflicts[conflictType]) {
|
|
291
|
-
typeConflicts[conflictType] = { count: 0, examples: new Set() };
|
|
292
|
-
}
|
|
293
|
-
typeConflicts[conflictType].count++;
|
|
294
|
-
if (problem.typeName) {
|
|
295
|
-
typeConflicts[conflictType].examples.add(problem.typeName);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// Convert to arrays and sort
|
|
301
|
-
const commonUnresolvedTypes = Object.entries(unresolvedTypes)
|
|
302
|
-
.map(([type, data]) => ({
|
|
303
|
-
type,
|
|
304
|
-
count: data.count,
|
|
305
|
-
namespaces: Array.from(data.namespaces),
|
|
306
|
-
}))
|
|
307
|
-
.sort((a, b) => b.count - a.count)
|
|
308
|
-
.slice(0, 20);
|
|
309
|
-
|
|
310
|
-
const commonTypeConflicts = Object.entries(typeConflicts)
|
|
311
|
-
.map(([conflictType, data]) => ({
|
|
312
|
-
conflictType,
|
|
313
|
-
count: data.count,
|
|
314
|
-
examples: Array.from(data.examples).slice(0, 5),
|
|
315
|
-
}))
|
|
316
|
-
.sort((a, b) => b.count - a.count);
|
|
317
|
-
|
|
318
|
-
const problematicNamespaces = Object.entries(namespaceProblems)
|
|
319
|
-
.map(([namespace, data]) => ({
|
|
320
|
-
namespace,
|
|
321
|
-
problems: data.count,
|
|
322
|
-
types: Array.from(data.types).slice(0, 10),
|
|
323
|
-
}))
|
|
324
|
-
.sort((a, b) => b.problems - a.problems)
|
|
325
|
-
.slice(0, 10);
|
|
326
|
-
|
|
327
|
-
const mostProblematicModules = Object.entries(byModule)
|
|
328
|
-
.sort(([, a], [, b]) => b - a)
|
|
329
|
-
.slice(0, 10)
|
|
330
|
-
.map(([module, count]) => ({ module, count }));
|
|
331
|
-
|
|
332
|
-
const endTime = new Date();
|
|
333
|
-
const durationMs = endTime.getTime() - this.startTime.getTime();
|
|
334
|
-
|
|
335
|
-
return {
|
|
336
|
-
bySeverity,
|
|
337
|
-
byCategory,
|
|
338
|
-
byModule,
|
|
339
|
-
totalProblems: this.problems.length,
|
|
340
|
-
mostProblematicModules,
|
|
341
|
-
typeStatistics: {
|
|
342
|
-
commonUnresolvedTypes,
|
|
343
|
-
commonTypeConflicts,
|
|
344
|
-
problematicNamespaces,
|
|
345
|
-
},
|
|
346
|
-
startTime: this.startTime,
|
|
347
|
-
endTime,
|
|
348
|
-
durationMs,
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
private generateSummary(statistics: ReportStatistics): GenerationReport["summary"] {
|
|
353
|
-
const { bySeverity, byCategory, totalProblems } = statistics;
|
|
354
|
-
|
|
355
|
-
let status: "success" | "partial" = "success";
|
|
356
|
-
if (bySeverity[ProblemSeverity.ERROR] > 0 || bySeverity[ProblemSeverity.WARNING] > 20) {
|
|
357
|
-
status = "partial";
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
const keyIssues: string[] = [];
|
|
361
|
-
const recommendations: string[] = [];
|
|
362
|
-
|
|
363
|
-
// Analyze key issues
|
|
364
|
-
if (byCategory[ProblemCategory.TYPE_RESOLUTION] > 0) {
|
|
365
|
-
keyIssues.push(`${byCategory[ProblemCategory.TYPE_RESOLUTION]} type resolution issues detected`);
|
|
366
|
-
recommendations.push("Review GIR files for missing or incorrect type definitions");
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
if (byCategory[ProblemCategory.PARSING_FAILURE] > 0) {
|
|
370
|
-
keyIssues.push(`${byCategory[ProblemCategory.PARSING_FAILURE]} parsing failures occurred`);
|
|
371
|
-
recommendations.push("Check GIR file syntax and ensure proper introspection data");
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
if (byCategory[ProblemCategory.GENERATION_FAILURE] > 0) {
|
|
375
|
-
keyIssues.push(`${byCategory[ProblemCategory.GENERATION_FAILURE]} generation failures encountered`);
|
|
376
|
-
recommendations.push("Review template configuration and output settings");
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
if (byCategory[ProblemCategory.TYPE_CONFLICT] > 5) {
|
|
380
|
-
keyIssues.push(`High number of type conflicts (${byCategory[ProblemCategory.TYPE_CONFLICT]})`);
|
|
381
|
-
recommendations.push("Consider using ignore patterns or updating GIR files to resolve conflicts");
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
if (keyIssues.length === 0 && totalProblems > 0) {
|
|
385
|
-
keyIssues.push(`${totalProblems} minor issues detected`);
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
if (recommendations.length === 0 && totalProblems > 0) {
|
|
389
|
-
recommendations.push("Review detailed problem list for specific improvement opportunities");
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
return {
|
|
393
|
-
status,
|
|
394
|
-
keyIssues,
|
|
395
|
-
recommendations,
|
|
396
|
-
};
|
|
397
|
-
}
|
|
398
|
-
|
|
399
263
|
public generateReport(): GenerationReport {
|
|
400
|
-
const statistics = this.
|
|
401
|
-
const summary =
|
|
402
|
-
|
|
403
|
-
const problemsByCategory = Object.values(ProblemCategory).reduce(
|
|
404
|
-
(acc, category) => {
|
|
405
|
-
acc[category] = [];
|
|
406
|
-
return acc;
|
|
407
|
-
},
|
|
408
|
-
{} as Record<ProblemCategory, ProblemEntry[]>,
|
|
409
|
-
);
|
|
410
|
-
|
|
411
|
-
for (const problem of this.problems) {
|
|
412
|
-
problemsByCategory[problem.category].push(problem);
|
|
413
|
-
}
|
|
264
|
+
const statistics = computeProblemStatistics(this.problems, this.startTime);
|
|
265
|
+
const summary = generateReportSummary(statistics);
|
|
414
266
|
|
|
415
267
|
return {
|
|
416
268
|
metadata: {
|
|
@@ -419,7 +271,7 @@ export class ConsoleReporter extends ReporterBase {
|
|
|
419
271
|
},
|
|
420
272
|
statistics,
|
|
421
273
|
problems: [...this.problems],
|
|
422
|
-
problemsByCategory,
|
|
274
|
+
problemsByCategory: groupProblemsByCategory(this.problems),
|
|
423
275
|
summary,
|
|
424
276
|
};
|
|
425
277
|
}
|
|
@@ -455,8 +307,11 @@ export class ConsoleReporter extends ReporterBase {
|
|
|
455
307
|
console.log("=".repeat(50));
|
|
456
308
|
|
|
457
309
|
// Status
|
|
458
|
-
const statusColor = summary.status === "
|
|
310
|
+
const statusColor = summary.status === "partial" ? yellow : green;
|
|
459
311
|
console.log(`Status: ${statusColor(summary.status.toUpperCase())}`);
|
|
312
|
+
if (summary.status === "success_with_warnings") {
|
|
313
|
+
console.log(green(" All issues are non-blocking. Generated types are complete."));
|
|
314
|
+
}
|
|
460
315
|
|
|
461
316
|
// Statistics
|
|
462
317
|
console.log(`\n📈 Statistics:`);
|
|
@@ -481,13 +336,17 @@ export class ConsoleReporter extends ReporterBase {
|
|
|
481
336
|
|
|
482
337
|
// Type-specific statistics
|
|
483
338
|
if (statistics.typeStatistics.commonUnresolvedTypes.length > 0) {
|
|
484
|
-
console.log(`\n
|
|
485
|
-
statistics.typeStatistics.commonUnresolvedTypes
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
console.log(`
|
|
489
|
-
|
|
490
|
-
|
|
339
|
+
console.log(`\n⚠️ Most Common Unresolved Types (produce 'never' type):`);
|
|
340
|
+
statistics.typeStatistics.commonUnresolvedTypes
|
|
341
|
+
.slice(0, 10)
|
|
342
|
+
.forEach(({ type, count, namespaces, sourceModules }) => {
|
|
343
|
+
console.log(` ${yellow(type)}: ${count} occurrences (origin: ${namespaces.join(", ") || "unknown"})`);
|
|
344
|
+
if (sourceModules.length > 0) {
|
|
345
|
+
const moduleList = sourceModules.slice(0, 5).join(", ");
|
|
346
|
+
const moreModules = sourceModules.length > 5 ? ` and ${sourceModules.length - 5} more` : "";
|
|
347
|
+
console.log(` └─ Referenced from: ${gray(moduleList + moreModules)}`);
|
|
348
|
+
}
|
|
349
|
+
});
|
|
491
350
|
}
|
|
492
351
|
|
|
493
352
|
if (statistics.typeStatistics.commonTypeConflicts.length > 0) {
|
|
@@ -501,9 +360,9 @@ export class ConsoleReporter extends ReporterBase {
|
|
|
501
360
|
}
|
|
502
361
|
|
|
503
362
|
if (statistics.typeStatistics.problematicNamespaces.length > 0) {
|
|
504
|
-
console.log(`\n
|
|
363
|
+
console.log(`\n📂 Namespaces with Most Warnings:`);
|
|
505
364
|
statistics.typeStatistics.problematicNamespaces.slice(0, 5).forEach(({ namespace, problems, types }) => {
|
|
506
|
-
console.log(` ${namespace}: ${problems}
|
|
365
|
+
console.log(` ${namespace}: ${problems} warnings`);
|
|
507
366
|
if (types.length > 0) {
|
|
508
367
|
const typeList = types.slice(0, 5).join(", ");
|
|
509
368
|
const moreTypes = types.length > 5 ? ` and ${types.length - 5} more` : "";
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Backwards compatibility export - alias ConsoleReporter as Reporter
|
|
2
2
|
export { ConsoleReporter, ConsoleReporter as Reporter } from "./console-reporter.ts";
|
|
3
|
+
export { LazyReporter } from "./lazy-reporter.ts";
|
|
3
4
|
export type { AnalyzedMessage } from "./message-analyzer.ts";
|
|
4
5
|
export { analyzeError, analyzeWarning } from "./message-analyzer.ts";
|
|
5
6
|
export { ReporterBase } from "./reporter-base.ts";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ConsoleReporter } from "./console-reporter.ts";
|
|
2
|
+
import { ReporterService } from "./reporter-service.ts";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A lazily-initialized reporter that auto-registers with ReporterService.
|
|
6
|
+
* Consolidates the repeated lazy-init + configure pattern used across the codebase.
|
|
7
|
+
*/
|
|
8
|
+
export class LazyReporter {
|
|
9
|
+
private config = { enabled: false, output: "ts-for-gir-report.json" };
|
|
10
|
+
private instance: ConsoleReporter | null = null;
|
|
11
|
+
|
|
12
|
+
constructor(private readonly name: string) {}
|
|
13
|
+
|
|
14
|
+
configure(enabled: boolean, output = "ts-for-gir-report.json"): void {
|
|
15
|
+
this.config = { enabled, output };
|
|
16
|
+
this.instance = null;
|
|
17
|
+
|
|
18
|
+
if (enabled) {
|
|
19
|
+
this.instance = new ConsoleReporter(true, this.name, enabled, output);
|
|
20
|
+
ReporterService.getInstance().registerReporter(this.name, this.instance);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get(): ConsoleReporter {
|
|
25
|
+
if (!this.instance) {
|
|
26
|
+
const { enabled, output } = this.config;
|
|
27
|
+
this.instance = new ConsoleReporter(true, this.name, enabled, output);
|
|
28
|
+
|
|
29
|
+
if (enabled) {
|
|
30
|
+
ReporterService.getInstance().registerReporter(this.name, this.instance);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return this.instance;
|
|
34
|
+
}
|
|
35
|
+
}
|
package/src/message-analyzer.ts
CHANGED
|
@@ -71,13 +71,13 @@ export function analyzeWarning(message: string, args?: unknown[]): AnalyzedMessa
|
|
|
71
71
|
export function analyzeError(message: string, args?: unknown[]): AnalyzedMessage | null {
|
|
72
72
|
const details = args && args.length > 0 ? JSON.stringify(args) : undefined;
|
|
73
73
|
|
|
74
|
-
// Type resolution
|
|
74
|
+
// Type resolution failures are non-fatal (produce 'never' type), classify as WARNING
|
|
75
75
|
if (message.includes("Unable to resolve type") || message.includes("could not be resolved")) {
|
|
76
76
|
const unresolvedMatch = message.match(/Unable to resolve type (\w+) in same namespace (\w+)!/);
|
|
77
77
|
if (unresolvedMatch) {
|
|
78
78
|
const [, typeName, namespace] = unresolvedMatch;
|
|
79
79
|
return {
|
|
80
|
-
severity: ProblemSeverity.
|
|
80
|
+
severity: ProblemSeverity.WARNING,
|
|
81
81
|
category: ProblemCategory.TYPE_RESOLUTION,
|
|
82
82
|
typeName,
|
|
83
83
|
namespace,
|
|
@@ -93,12 +93,12 @@ export function analyzeError(message: string, args?: unknown[]): AnalyzedMessage
|
|
|
93
93
|
const namespace = namespaceMatch ? namespaceMatch[1] : context;
|
|
94
94
|
|
|
95
95
|
return {
|
|
96
|
-
severity: ProblemSeverity.
|
|
96
|
+
severity: ProblemSeverity.WARNING,
|
|
97
97
|
category: ProblemCategory.TYPE_RESOLUTION,
|
|
98
98
|
typeName,
|
|
99
99
|
namespace,
|
|
100
100
|
details,
|
|
101
|
-
metadata: { namespace, typeName, context },
|
|
101
|
+
metadata: { namespace, typeName, context, resolutionType: "cross_namespace" },
|
|
102
102
|
};
|
|
103
103
|
}
|
|
104
104
|
}
|
package/src/reporter-base.ts
CHANGED
|
@@ -42,6 +42,8 @@ export abstract class ReporterBase {
|
|
|
42
42
|
typeName?: string,
|
|
43
43
|
location?: string,
|
|
44
44
|
metadata?: Record<string, unknown>,
|
|
45
|
+
/** Override the module field with the actual GIR namespace instead of the reporter name */
|
|
46
|
+
moduleOverride?: string,
|
|
45
47
|
): void {
|
|
46
48
|
if (!this.config.enabled) {
|
|
47
49
|
return;
|
|
@@ -51,7 +53,7 @@ export abstract class ReporterBase {
|
|
|
51
53
|
id: this.generateProblemId(),
|
|
52
54
|
severity,
|
|
53
55
|
category,
|
|
54
|
-
module: this.config.moduleName,
|
|
56
|
+
module: moduleOverride || this.config.moduleName,
|
|
55
57
|
message,
|
|
56
58
|
details,
|
|
57
59
|
typeName,
|
|
@@ -105,12 +107,14 @@ export abstract class ReporterBase {
|
|
|
105
107
|
|
|
106
108
|
/**
|
|
107
109
|
* Report a type resolution warning (fallback cases)
|
|
110
|
+
* @param sourceModule - The GIR module that references this type (e.g., "Shell 17")
|
|
108
111
|
*/
|
|
109
112
|
public abstract reportTypeResolutionWarning(
|
|
110
113
|
typeName: string,
|
|
111
114
|
namespace: string,
|
|
112
115
|
message: string,
|
|
113
116
|
details?: string,
|
|
117
|
+
sourceModule?: string,
|
|
114
118
|
): void;
|
|
115
119
|
|
|
116
120
|
/**
|
package/src/reporter-service.ts
CHANGED
|
@@ -8,8 +8,8 @@ import { resolve } from "node:path";
|
|
|
8
8
|
import { blue, green, red, yellow } from "colorette";
|
|
9
9
|
import { PACKAGE_VERSION } from "./constants.ts";
|
|
10
10
|
import type { ReporterBase } from "./reporter-base.ts";
|
|
11
|
-
import type { GenerationReport, ProblemEntry
|
|
12
|
-
import {
|
|
11
|
+
import type { GenerationReport, ProblemEntry } from "./types/index.ts";
|
|
12
|
+
import { computeProblemStatistics, generateReportSummary, groupProblemsByCategory } from "./types/report.ts";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Centralized service for managing multiple Reporter instances
|
|
@@ -83,8 +83,7 @@ export class ReporterService {
|
|
|
83
83
|
const allProblems: ProblemEntry[] = [];
|
|
84
84
|
|
|
85
85
|
for (const reporter of this.reporters.values()) {
|
|
86
|
-
|
|
87
|
-
allProblems.push(...report.problems);
|
|
86
|
+
allProblems.push(...reporter.getProblems());
|
|
88
87
|
}
|
|
89
88
|
|
|
90
89
|
return allProblems;
|
|
@@ -93,231 +92,14 @@ export class ReporterService {
|
|
|
93
92
|
/**
|
|
94
93
|
* Generate comprehensive statistics from all reporters
|
|
95
94
|
*/
|
|
96
|
-
private generateComprehensiveStatistics(): ReportStatistics {
|
|
97
|
-
const allProblems = this.collectAllProblems();
|
|
98
|
-
|
|
99
|
-
// If we have no reporters, return empty stats
|
|
100
|
-
if (this.reporters.size === 0) {
|
|
101
|
-
return {
|
|
102
|
-
bySeverity: {
|
|
103
|
-
[ProblemSeverity.DEBUG]: 0,
|
|
104
|
-
[ProblemSeverity.INFO]: 0,
|
|
105
|
-
[ProblemSeverity.WARNING]: 0,
|
|
106
|
-
[ProblemSeverity.ERROR]: 0,
|
|
107
|
-
[ProblemSeverity.CRITICAL]: 0,
|
|
108
|
-
},
|
|
109
|
-
byCategory: {
|
|
110
|
-
[ProblemCategory.TYPE_RESOLUTION]: 0,
|
|
111
|
-
[ProblemCategory.PARSING_FAILURE]: 0,
|
|
112
|
-
[ProblemCategory.GENERATION_FAILURE]: 0,
|
|
113
|
-
[ProblemCategory.TYPE_CONFLICT]: 0,
|
|
114
|
-
[ProblemCategory.DEPENDENCY_ISSUE]: 0,
|
|
115
|
-
[ProblemCategory.CONFIGURATION]: 0,
|
|
116
|
-
[ProblemCategory.IO_ERROR]: 0,
|
|
117
|
-
[ProblemCategory.GENERAL]: 0,
|
|
118
|
-
},
|
|
119
|
-
byModule: {},
|
|
120
|
-
totalProblems: 0,
|
|
121
|
-
mostProblematicModules: [],
|
|
122
|
-
typeStatistics: {
|
|
123
|
-
commonUnresolvedTypes: [],
|
|
124
|
-
commonTypeConflicts: [],
|
|
125
|
-
problematicNamespaces: [],
|
|
126
|
-
},
|
|
127
|
-
startTime: new Date(),
|
|
128
|
-
endTime: new Date(),
|
|
129
|
-
durationMs: 0,
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Get the first reporter's report as base for timing
|
|
134
|
-
const firstReport = this.reporters.values().next().value?.generateReport();
|
|
135
|
-
const startTime = firstReport?.statistics.startTime || new Date();
|
|
136
|
-
|
|
137
|
-
// Use current time as end time
|
|
138
|
-
const endTime = new Date();
|
|
139
|
-
const durationMs = endTime.getTime() - startTime.getTime();
|
|
140
|
-
|
|
141
|
-
// Aggregate statistics
|
|
142
|
-
const bySeverity: Record<ProblemSeverity, number> = {
|
|
143
|
-
[ProblemSeverity.DEBUG]: 0,
|
|
144
|
-
[ProblemSeverity.INFO]: 0,
|
|
145
|
-
[ProblemSeverity.WARNING]: 0,
|
|
146
|
-
[ProblemSeverity.ERROR]: 0,
|
|
147
|
-
[ProblemSeverity.CRITICAL]: 0,
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
const byCategory: Record<ProblemCategory, number> = {
|
|
151
|
-
[ProblemCategory.TYPE_RESOLUTION]: 0,
|
|
152
|
-
[ProblemCategory.PARSING_FAILURE]: 0,
|
|
153
|
-
[ProblemCategory.GENERATION_FAILURE]: 0,
|
|
154
|
-
[ProblemCategory.TYPE_CONFLICT]: 0,
|
|
155
|
-
[ProblemCategory.DEPENDENCY_ISSUE]: 0,
|
|
156
|
-
[ProblemCategory.CONFIGURATION]: 0,
|
|
157
|
-
[ProblemCategory.IO_ERROR]: 0,
|
|
158
|
-
[ProblemCategory.GENERAL]: 0,
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
const byModule: Record<string, number> = {};
|
|
162
|
-
|
|
163
|
-
// Type-specific tracking
|
|
164
|
-
const unresolvedTypes: Record<string, { count: number; namespaces: Set<string> }> = {};
|
|
165
|
-
const typeConflicts: Record<string, { count: number; examples: Set<string> }> = {};
|
|
166
|
-
const namespaceProblems: Record<string, { count: number; types: Set<string> }> = {};
|
|
167
|
-
|
|
168
|
-
for (const problem of allProblems) {
|
|
169
|
-
bySeverity[problem.severity] = (bySeverity[problem.severity] || 0) + 1;
|
|
170
|
-
byCategory[problem.category] = (byCategory[problem.category] || 0) + 1;
|
|
171
|
-
byModule[problem.module] = (byModule[problem.module] || 0) + 1;
|
|
172
|
-
|
|
173
|
-
// Track type resolution problems
|
|
174
|
-
if (problem.category === ProblemCategory.TYPE_RESOLUTION && problem.typeName) {
|
|
175
|
-
if (!unresolvedTypes[problem.typeName]) {
|
|
176
|
-
unresolvedTypes[problem.typeName] = { count: 0, namespaces: new Set() };
|
|
177
|
-
}
|
|
178
|
-
unresolvedTypes[problem.typeName].count++;
|
|
179
|
-
if (problem.location) {
|
|
180
|
-
unresolvedTypes[problem.typeName].namespaces.add(problem.location);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Track namespace problems
|
|
184
|
-
if (problem.location) {
|
|
185
|
-
if (!namespaceProblems[problem.location]) {
|
|
186
|
-
namespaceProblems[problem.location] = { count: 0, types: new Set() };
|
|
187
|
-
}
|
|
188
|
-
namespaceProblems[problem.location].count++;
|
|
189
|
-
namespaceProblems[problem.location].types.add(problem.typeName);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// Track type conflicts
|
|
194
|
-
if (problem.category === ProblemCategory.TYPE_CONFLICT && problem.metadata?.conflictType) {
|
|
195
|
-
const conflictType = problem.metadata.conflictType as string;
|
|
196
|
-
if (!typeConflicts[conflictType]) {
|
|
197
|
-
typeConflicts[conflictType] = { count: 0, examples: new Set() };
|
|
198
|
-
}
|
|
199
|
-
typeConflicts[conflictType].count++;
|
|
200
|
-
if (problem.typeName) {
|
|
201
|
-
typeConflicts[conflictType].examples.add(problem.typeName);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Convert to arrays and sort
|
|
207
|
-
const commonUnresolvedTypes = Object.entries(unresolvedTypes)
|
|
208
|
-
.map(([type, data]) => ({
|
|
209
|
-
type,
|
|
210
|
-
count: data.count,
|
|
211
|
-
namespaces: Array.from(data.namespaces),
|
|
212
|
-
}))
|
|
213
|
-
.sort((a, b) => b.count - a.count)
|
|
214
|
-
.slice(0, 20);
|
|
215
|
-
|
|
216
|
-
const commonTypeConflicts = Object.entries(typeConflicts)
|
|
217
|
-
.map(([conflictType, data]) => ({
|
|
218
|
-
conflictType,
|
|
219
|
-
count: data.count,
|
|
220
|
-
examples: Array.from(data.examples).slice(0, 5),
|
|
221
|
-
}))
|
|
222
|
-
.sort((a, b) => b.count - a.count);
|
|
223
|
-
|
|
224
|
-
const problematicNamespaces = Object.entries(namespaceProblems)
|
|
225
|
-
.map(([namespace, data]) => ({
|
|
226
|
-
namespace,
|
|
227
|
-
problems: data.count,
|
|
228
|
-
types: Array.from(data.types).slice(0, 10),
|
|
229
|
-
}))
|
|
230
|
-
.sort((a, b) => b.problems - a.problems)
|
|
231
|
-
.slice(0, 10);
|
|
232
|
-
|
|
233
|
-
const mostProblematicModules = Object.entries(byModule)
|
|
234
|
-
.sort(([, a], [, b]) => b - a)
|
|
235
|
-
.slice(0, 10)
|
|
236
|
-
.map(([module, count]) => ({ module, count }));
|
|
237
|
-
|
|
238
|
-
return {
|
|
239
|
-
bySeverity,
|
|
240
|
-
byCategory,
|
|
241
|
-
byModule,
|
|
242
|
-
totalProblems: allProblems.length,
|
|
243
|
-
mostProblematicModules,
|
|
244
|
-
typeStatistics: {
|
|
245
|
-
commonUnresolvedTypes,
|
|
246
|
-
commonTypeConflicts,
|
|
247
|
-
problematicNamespaces,
|
|
248
|
-
},
|
|
249
|
-
startTime,
|
|
250
|
-
endTime,
|
|
251
|
-
durationMs,
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
|
|
255
95
|
/**
|
|
256
96
|
* Generate comprehensive report from all reporters
|
|
257
97
|
*/
|
|
258
98
|
public generateComprehensiveReport(): GenerationReport {
|
|
259
|
-
const statistics = this.generateComprehensiveStatistics();
|
|
260
99
|
const allProblems = this.collectAllProblems();
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
const
|
|
264
|
-
(acc, category) => {
|
|
265
|
-
acc[category] = [];
|
|
266
|
-
return acc;
|
|
267
|
-
},
|
|
268
|
-
{} as Record<ProblemCategory, ProblemEntry[]>,
|
|
269
|
-
);
|
|
270
|
-
|
|
271
|
-
for (const problem of allProblems) {
|
|
272
|
-
problemsByCategory[problem.category].push(problem);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// Generate summary
|
|
276
|
-
const errorCount = statistics.bySeverity[ProblemSeverity.ERROR] || 0;
|
|
277
|
-
const _criticalCount = statistics.bySeverity[ProblemSeverity.CRITICAL] || 0;
|
|
278
|
-
const warningCount = statistics.bySeverity[ProblemSeverity.WARNING] || 0;
|
|
279
|
-
|
|
280
|
-
let status: "success" | "partial" = "success";
|
|
281
|
-
if (errorCount > 0 || warningCount > 20) {
|
|
282
|
-
status = "partial";
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
const keyIssues: string[] = [];
|
|
286
|
-
const recommendations: string[] = [];
|
|
287
|
-
|
|
288
|
-
// Analyze key issues across all modules
|
|
289
|
-
const typeResolutionCount = statistics.byCategory[ProblemCategory.TYPE_RESOLUTION] || 0;
|
|
290
|
-
const parsingFailureCount = statistics.byCategory[ProblemCategory.PARSING_FAILURE] || 0;
|
|
291
|
-
const generationFailureCount = statistics.byCategory[ProblemCategory.GENERATION_FAILURE] || 0;
|
|
292
|
-
const conflictCount = statistics.byCategory[ProblemCategory.TYPE_CONFLICT] || 0;
|
|
293
|
-
|
|
294
|
-
if (typeResolutionCount > 0) {
|
|
295
|
-
keyIssues.push(`${typeResolutionCount} type resolution issues across all modules`);
|
|
296
|
-
recommendations.push("Review GIR files for missing or incorrect type definitions");
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
if (parsingFailureCount > 0) {
|
|
300
|
-
keyIssues.push(`${parsingFailureCount} parsing failures encountered`);
|
|
301
|
-
recommendations.push("Check GIR file syntax and ensure proper introspection data");
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
if (generationFailureCount > 0) {
|
|
305
|
-
keyIssues.push(`${generationFailureCount} generation failures occurred`);
|
|
306
|
-
recommendations.push("Review template configuration and output settings");
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
if (conflictCount > 10) {
|
|
310
|
-
keyIssues.push(`High number of type conflicts (${conflictCount})`);
|
|
311
|
-
recommendations.push("Consider using ignore patterns or updating GIR files to resolve conflicts");
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
if (keyIssues.length === 0 && statistics.totalProblems > 0) {
|
|
315
|
-
keyIssues.push(`${statistics.totalProblems} minor issues detected across all modules`);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
if (recommendations.length === 0 && statistics.totalProblems > 0) {
|
|
319
|
-
recommendations.push("Review detailed problem list for specific improvement opportunities");
|
|
320
|
-
}
|
|
100
|
+
const startTime = this.reporters.values().next().value?.getProblems()[0]?.timestamp || new Date();
|
|
101
|
+
const statistics = computeProblemStatistics(allProblems, startTime);
|
|
102
|
+
const summary = generateReportSummary(statistics);
|
|
321
103
|
|
|
322
104
|
return {
|
|
323
105
|
metadata: {
|
|
@@ -326,24 +108,20 @@ export class ReporterService {
|
|
|
326
108
|
},
|
|
327
109
|
statistics,
|
|
328
110
|
problems: allProblems,
|
|
329
|
-
problemsByCategory,
|
|
330
|
-
summary
|
|
331
|
-
status,
|
|
332
|
-
keyIssues,
|
|
333
|
-
recommendations,
|
|
334
|
-
},
|
|
111
|
+
problemsByCategory: groupProblemsByCategory(allProblems),
|
|
112
|
+
summary,
|
|
335
113
|
};
|
|
336
114
|
}
|
|
337
115
|
|
|
338
116
|
/**
|
|
339
117
|
* Save comprehensive report to file
|
|
340
118
|
*/
|
|
341
|
-
public async saveComprehensiveReport(outputPath?: string): Promise<void> {
|
|
119
|
+
public async saveComprehensiveReport(outputPath?: string, precomputedReport?: GenerationReport): Promise<void> {
|
|
342
120
|
if (!this.config.enabled) {
|
|
343
121
|
return;
|
|
344
122
|
}
|
|
345
123
|
|
|
346
|
-
const report = this.generateComprehensiveReport();
|
|
124
|
+
const report = precomputedReport || this.generateComprehensiveReport();
|
|
347
125
|
const filePath = outputPath || this.config.outputPath;
|
|
348
126
|
|
|
349
127
|
try {
|
|
@@ -358,8 +136,8 @@ export class ReporterService {
|
|
|
358
136
|
/**
|
|
359
137
|
* Print comprehensive summary to console
|
|
360
138
|
*/
|
|
361
|
-
public printComprehensiveSummary(): void {
|
|
362
|
-
const report = this.generateComprehensiveReport();
|
|
139
|
+
public printComprehensiveSummary(precomputedReport?: GenerationReport): void {
|
|
140
|
+
const report = precomputedReport || this.generateComprehensiveReport();
|
|
363
141
|
const { statistics, summary } = report;
|
|
364
142
|
|
|
365
143
|
console.log(`\n${"=".repeat(60)}`);
|
|
@@ -367,8 +145,11 @@ export class ReporterService {
|
|
|
367
145
|
console.log("=".repeat(60));
|
|
368
146
|
|
|
369
147
|
// Overall status
|
|
370
|
-
const statusColor = summary.status === "
|
|
148
|
+
const statusColor = summary.status === "partial" ? yellow : green;
|
|
371
149
|
console.log(`\n🎯 Overall Status: ${statusColor(summary.status.toUpperCase())}`);
|
|
150
|
+
if (summary.status === "success_with_warnings") {
|
|
151
|
+
console.log(green(" All issues are non-blocking. Generated types are complete."));
|
|
152
|
+
}
|
|
372
153
|
|
|
373
154
|
// Total statistics
|
|
374
155
|
console.log(`\n📈 Total Statistics:`);
|
|
@@ -396,9 +177,9 @@ export class ReporterService {
|
|
|
396
177
|
}
|
|
397
178
|
}
|
|
398
179
|
|
|
399
|
-
// Most problematic
|
|
180
|
+
// Most problematic namespaces (byModule now tracks GIR namespaces)
|
|
400
181
|
if (statistics.mostProblematicModules.length > 0) {
|
|
401
|
-
console.log(`\n📦 Most
|
|
182
|
+
console.log(`\n📦 Namespaces with Most Issues:`);
|
|
402
183
|
statistics.mostProblematicModules.slice(0, 10).forEach(({ module, count }) => {
|
|
403
184
|
const percentage = Math.round((count / statistics.totalProblems) * 100);
|
|
404
185
|
console.log(` ${module}: ${count} issues (${percentage}%)`);
|
package/src/types/report.ts
CHANGED
|
@@ -1,11 +1,226 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ProblemCategory, type ProblemEntry, ProblemSeverity } from "./problem.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generation status levels
|
|
5
|
+
*/
|
|
6
|
+
export type GenerationStatus = "success" | "success_with_warnings" | "partial";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Determine the generation status based on problem statistics.
|
|
10
|
+
* - "partial": actual errors, parsing failures, or generation failures occurred
|
|
11
|
+
* - "success_with_warnings": generation completed but with non-blocking warnings
|
|
12
|
+
* - "success": no problems at all
|
|
13
|
+
*/
|
|
14
|
+
export function determineGenerationStatus(
|
|
15
|
+
bySeverity: Record<ProblemSeverity, number>,
|
|
16
|
+
byCategory: Record<ProblemCategory, number>,
|
|
17
|
+
): GenerationStatus {
|
|
18
|
+
const hasErrors = (bySeverity[ProblemSeverity.ERROR] || 0) > 0;
|
|
19
|
+
const hasCritical = (bySeverity[ProblemSeverity.CRITICAL] || 0) > 0;
|
|
20
|
+
const hasGenerationFailures = (byCategory[ProblemCategory.GENERATION_FAILURE] || 0) > 0;
|
|
21
|
+
const hasParsingFailures = (byCategory[ProblemCategory.PARSING_FAILURE] || 0) > 0;
|
|
22
|
+
|
|
23
|
+
if (hasErrors || hasCritical || hasGenerationFailures || hasParsingFailures) {
|
|
24
|
+
return "partial";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if ((bySeverity[ProblemSeverity.WARNING] || 0) > 0) {
|
|
28
|
+
return "success_with_warnings";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return "success";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Compute problem statistics from a list of problems.
|
|
36
|
+
* Shared between ConsoleReporter and ReporterService to avoid duplication.
|
|
37
|
+
*/
|
|
38
|
+
export function computeProblemStatistics(problems: ProblemEntry[], startTime: Date): ReportStatistics {
|
|
39
|
+
const bySeverity: Record<ProblemSeverity, number> = {
|
|
40
|
+
[ProblemSeverity.DEBUG]: 0,
|
|
41
|
+
[ProblemSeverity.INFO]: 0,
|
|
42
|
+
[ProblemSeverity.WARNING]: 0,
|
|
43
|
+
[ProblemSeverity.ERROR]: 0,
|
|
44
|
+
[ProblemSeverity.CRITICAL]: 0,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const byCategory: Record<ProblemCategory, number> = {
|
|
48
|
+
[ProblemCategory.TYPE_RESOLUTION]: 0,
|
|
49
|
+
[ProblemCategory.PARSING_FAILURE]: 0,
|
|
50
|
+
[ProblemCategory.GENERATION_FAILURE]: 0,
|
|
51
|
+
[ProblemCategory.TYPE_CONFLICT]: 0,
|
|
52
|
+
[ProblemCategory.DEPENDENCY_ISSUE]: 0,
|
|
53
|
+
[ProblemCategory.CONFIGURATION]: 0,
|
|
54
|
+
[ProblemCategory.IO_ERROR]: 0,
|
|
55
|
+
[ProblemCategory.GENERAL]: 0,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const byModule: Record<string, number> = {};
|
|
59
|
+
|
|
60
|
+
const unresolvedTypes: Record<string, { count: number; namespaces: Set<string>; sourceModules: Set<string> }> = {};
|
|
61
|
+
const typeConflicts: Record<string, { count: number; examples: Set<string> }> = {};
|
|
62
|
+
const namespaceProblems: Record<string, { count: number; types: Set<string> }> = {};
|
|
63
|
+
|
|
64
|
+
for (const problem of problems) {
|
|
65
|
+
bySeverity[problem.severity]++;
|
|
66
|
+
byCategory[problem.category]++;
|
|
67
|
+
byModule[problem.module] = (byModule[problem.module] || 0) + 1;
|
|
68
|
+
|
|
69
|
+
if (problem.category === ProblemCategory.TYPE_RESOLUTION && problem.typeName) {
|
|
70
|
+
if (!unresolvedTypes[problem.typeName]) {
|
|
71
|
+
unresolvedTypes[problem.typeName] = { count: 0, namespaces: new Set(), sourceModules: new Set() };
|
|
72
|
+
}
|
|
73
|
+
unresolvedTypes[problem.typeName].count++;
|
|
74
|
+
if (problem.location) {
|
|
75
|
+
unresolvedTypes[problem.typeName].namespaces.add(problem.location);
|
|
76
|
+
}
|
|
77
|
+
const sourceModule = problem.metadata?.sourceModule as string | undefined;
|
|
78
|
+
if (sourceModule) {
|
|
79
|
+
unresolvedTypes[problem.typeName].sourceModules.add(sourceModule);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (problem.location) {
|
|
83
|
+
if (!namespaceProblems[problem.location]) {
|
|
84
|
+
namespaceProblems[problem.location] = { count: 0, types: new Set() };
|
|
85
|
+
}
|
|
86
|
+
namespaceProblems[problem.location].count++;
|
|
87
|
+
namespaceProblems[problem.location].types.add(problem.typeName);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (problem.category === ProblemCategory.TYPE_CONFLICT && problem.metadata?.conflictType) {
|
|
92
|
+
const conflictType = problem.metadata.conflictType as string;
|
|
93
|
+
if (!typeConflicts[conflictType]) {
|
|
94
|
+
typeConflicts[conflictType] = { count: 0, examples: new Set() };
|
|
95
|
+
}
|
|
96
|
+
typeConflicts[conflictType].count++;
|
|
97
|
+
if (problem.typeName) {
|
|
98
|
+
typeConflicts[conflictType].examples.add(problem.typeName);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const commonUnresolvedTypes = Object.entries(unresolvedTypes)
|
|
104
|
+
.map(([type, data]) => ({
|
|
105
|
+
type,
|
|
106
|
+
count: data.count,
|
|
107
|
+
namespaces: Array.from(data.namespaces),
|
|
108
|
+
sourceModules: Array.from(data.sourceModules),
|
|
109
|
+
}))
|
|
110
|
+
.sort((a, b) => b.count - a.count)
|
|
111
|
+
.slice(0, 20);
|
|
112
|
+
|
|
113
|
+
const commonTypeConflicts = Object.entries(typeConflicts)
|
|
114
|
+
.map(([conflictType, data]) => ({
|
|
115
|
+
conflictType,
|
|
116
|
+
count: data.count,
|
|
117
|
+
examples: Array.from(data.examples).slice(0, 5),
|
|
118
|
+
}))
|
|
119
|
+
.sort((a, b) => b.count - a.count);
|
|
120
|
+
|
|
121
|
+
const problematicNamespaces = Object.entries(namespaceProblems)
|
|
122
|
+
.map(([namespace, data]) => ({
|
|
123
|
+
namespace,
|
|
124
|
+
problems: data.count,
|
|
125
|
+
types: Array.from(data.types).slice(0, 10),
|
|
126
|
+
}))
|
|
127
|
+
.sort((a, b) => b.problems - a.problems)
|
|
128
|
+
.slice(0, 10);
|
|
129
|
+
|
|
130
|
+
const mostProblematicModules = Object.entries(byModule)
|
|
131
|
+
.sort(([, a], [, b]) => b - a)
|
|
132
|
+
.slice(0, 10)
|
|
133
|
+
.map(([module, count]) => ({ module, count }));
|
|
134
|
+
|
|
135
|
+
const endTime = new Date();
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
bySeverity,
|
|
139
|
+
byCategory,
|
|
140
|
+
byModule,
|
|
141
|
+
totalProblems: problems.length,
|
|
142
|
+
mostProblematicModules,
|
|
143
|
+
typeStatistics: { commonUnresolvedTypes, commonTypeConflicts, problematicNamespaces },
|
|
144
|
+
startTime,
|
|
145
|
+
endTime,
|
|
146
|
+
durationMs: endTime.getTime() - startTime.getTime(),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Generate report summary with key issues and recommendations.
|
|
152
|
+
* Shared between ConsoleReporter and ReporterService.
|
|
153
|
+
*/
|
|
154
|
+
export function generateReportSummary(statistics: ReportStatistics): GenerationReport["summary"] {
|
|
155
|
+
const { bySeverity, byCategory, totalProblems } = statistics;
|
|
156
|
+
|
|
157
|
+
const status = determineGenerationStatus(bySeverity, byCategory);
|
|
158
|
+
|
|
159
|
+
const keyIssues: string[] = [];
|
|
160
|
+
const recommendations: string[] = [];
|
|
161
|
+
|
|
162
|
+
if (byCategory[ProblemCategory.TYPE_RESOLUTION] > 0) {
|
|
163
|
+
keyIssues.push(
|
|
164
|
+
`${byCategory[ProblemCategory.TYPE_RESOLUTION]} type resolution warnings (produce 'never' type in output)`,
|
|
165
|
+
);
|
|
166
|
+
recommendations.push(
|
|
167
|
+
"Unresolved types produce 'never' in output — these are typically non-introspectable types or missing GIR dependencies",
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (byCategory[ProblemCategory.PARSING_FAILURE] > 0) {
|
|
172
|
+
keyIssues.push(`${byCategory[ProblemCategory.PARSING_FAILURE]} parsing failures occurred`);
|
|
173
|
+
recommendations.push("Check GIR file syntax and ensure proper introspection data");
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (byCategory[ProblemCategory.GENERATION_FAILURE] > 0) {
|
|
177
|
+
keyIssues.push(`${byCategory[ProblemCategory.GENERATION_FAILURE]} generation failures encountered`);
|
|
178
|
+
recommendations.push("Review template configuration and output settings");
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (byCategory[ProblemCategory.TYPE_CONFLICT] > 0) {
|
|
182
|
+
keyIssues.push(`${byCategory[ProblemCategory.TYPE_CONFLICT]} type conflicts detected`);
|
|
183
|
+
recommendations.push(
|
|
184
|
+
"Type conflicts are handled automatically — conflicting members are omitted or use union types",
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (keyIssues.length === 0 && totalProblems > 0) {
|
|
189
|
+
keyIssues.push(`${totalProblems} minor issues detected`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (recommendations.length === 0 && totalProblems > 0) {
|
|
193
|
+
recommendations.push("Review detailed problem list for specific improvement opportunities");
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return { status, keyIssues, recommendations };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Group problems by category.
|
|
201
|
+
*/
|
|
202
|
+
export function groupProblemsByCategory(problems: ProblemEntry[]): Record<ProblemCategory, ProblemEntry[]> {
|
|
203
|
+
const result = Object.values(ProblemCategory).reduce(
|
|
204
|
+
(acc, category) => {
|
|
205
|
+
acc[category] = [];
|
|
206
|
+
return acc;
|
|
207
|
+
},
|
|
208
|
+
{} as Record<ProblemCategory, ProblemEntry[]>,
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
for (const problem of problems) {
|
|
212
|
+
result[problem.category].push(problem);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return result;
|
|
216
|
+
}
|
|
2
217
|
|
|
3
218
|
/**
|
|
4
219
|
* Aggregated problem statistics by type
|
|
5
220
|
*/
|
|
6
221
|
export interface ProblemTypeStatistics {
|
|
7
222
|
/** Most common unresolved types */
|
|
8
|
-
commonUnresolvedTypes: Array<{ type: string; count: number; namespaces: string[] }>;
|
|
223
|
+
commonUnresolvedTypes: Array<{ type: string; count: number; namespaces: string[]; sourceModules: string[] }>;
|
|
9
224
|
/** Most common type conflicts */
|
|
10
225
|
commonTypeConflicts: Array<{ conflictType: string; count: number; examples: string[] }>;
|
|
11
226
|
/** Namespaces with most problems */
|
|
@@ -54,7 +269,7 @@ export interface GenerationReport {
|
|
|
54
269
|
/** Summary and recommendations */
|
|
55
270
|
summary: {
|
|
56
271
|
/** Overall generation status */
|
|
57
|
-
status:
|
|
272
|
+
status: GenerationStatus;
|
|
58
273
|
/** Key issues summary */
|
|
59
274
|
keyIssues: string[];
|
|
60
275
|
/** Recommendations for fixing issues */
|