@thymian/plugin-reporter 0.0.0-PLACEHOLDER
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/README.md +59 -0
- package/dist/formatter.d.ts +18 -0
- package/dist/formatter.d.ts.map +1 -0
- package/dist/formatter.js +24 -0
- package/dist/formatter.js.map +1 -0
- package/dist/formatters/csv.d.ts +17 -0
- package/dist/formatters/csv.d.ts.map +1 -0
- package/dist/formatters/csv.js +74 -0
- package/dist/formatters/csv.js.map +1 -0
- package/dist/formatters/markdown.d.ts +17 -0
- package/dist/formatters/markdown.d.ts.map +1 -0
- package/dist/formatters/markdown.js +85 -0
- package/dist/formatters/markdown.js.map +1 -0
- package/dist/formatters/text.d.ts +23 -0
- package/dist/formatters/text.d.ts.map +1 -0
- package/dist/formatters/text.js +140 -0
- package/dist/formatters/text.js.map +1 -0
- package/dist/get-formatters.d.ts +27 -0
- package/dist/get-formatters.d.ts.map +1 -0
- package/dist/get-formatters.js +61 -0
- package/dist/get-formatters.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +100 -0
- package/dist/index.js.map +1 -0
- package/dist/style.d.ts +5 -0
- package/dist/style.d.ts.map +1 -0
- package/dist/style.js +5 -0
- package/dist/style.js.map +1 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +17 -0
- package/dist/utils.js.map +1 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<!-- HEADER:START - Do not remove or modify this section -->
|
|
2
|
+
<div align="center">
|
|
3
|
+
<img src="https://raw.githubusercontent.com/thymianofficial/thymian/main/astro-docs/src/assets/logo.svg" alt="Thymian Logo" width="200"/>
|
|
4
|
+
|
|
5
|
+
# Thymian
|
|
6
|
+
|
|
7
|
+
**Add resilience and HTTP conformance to your API development workflow**
|
|
8
|
+
|
|
9
|
+
[](https://github.com/thymianofficial/thymian/blob/main/LICENSE)
|
|
10
|
+
[](https://github.com/thymianofficial/thymian/actions/workflows/ci.yaml)
|
|
11
|
+
[](https://thymian.dev)
|
|
12
|
+
[](https://discord.gg/TRSwCxbz9f)
|
|
13
|
+
[](https://www.reddit.com/r/ThymianOfficial/)
|
|
14
|
+
[](https://x.com/thymiandev)
|
|
15
|
+
|
|
16
|
+
</div>
|
|
17
|
+
<!-- HEADER:END -->
|
|
18
|
+
|
|
19
|
+
## @thymian/plugin-reporter
|
|
20
|
+
|
|
21
|
+
Formats and outputs Thymian validation results. Listens for rule violations and test outcomes emitted during a Thymian run, then generates structured reports for the console, CI pipelines, or custom integrations.
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install @thymian/plugin-reporter
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
> **Getting started with Thymian?** See the [main Thymian package](https://www.npmjs.com/package/thymian) for quick installation and first-run instructions.
|
|
30
|
+
|
|
31
|
+
<!-- FOOTER:START - Do not remove or modify this section -->
|
|
32
|
+
|
|
33
|
+
## 📚 Documentation
|
|
34
|
+
|
|
35
|
+
- **[Getting Started](https://thymian.dev/introduction/getting-started)** - Set up Thymian in minutes
|
|
36
|
+
- **[Documentation](https://thymian.dev)** - Comprehensive guides and API reference
|
|
37
|
+
- **[CLI Reference](https://thymian.dev/references/cli)** - Complete CLI command documentation
|
|
38
|
+
- **[HTTP Rules](https://thymian.dev/guides/http-rules/how-to-use-rules)** - Learn about HTTP conformance validation
|
|
39
|
+
|
|
40
|
+
## 🏢 Enterprise Support
|
|
41
|
+
|
|
42
|
+
Get professional consulting and dedicated support from the creators of Thymian. We offer:
|
|
43
|
+
|
|
44
|
+
- API design and governance strategies
|
|
45
|
+
- HTTP standards compliance auditing
|
|
46
|
+
- Custom plugin development
|
|
47
|
+
- Custom rule development
|
|
48
|
+
- Team training and workshops
|
|
49
|
+
|
|
50
|
+
**[Learn more about Enterprise Support](https://thymian.dev/enterprise)** | **Email: [support@thymian.dev](mailto:support@thymian.dev)**
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
<div align="center">
|
|
55
|
+
|
|
56
|
+
**Shipped with 🌱 by [qupaya](https://qupaya.com)**
|
|
57
|
+
|
|
58
|
+
</div>
|
|
59
|
+
<!-- FOOTER:END -->
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ThymianReport, ThymianReportSeverity } from '@thymian/core';
|
|
2
|
+
export type ThymianReportStatistics = {
|
|
3
|
+
numberOfReports: number;
|
|
4
|
+
numberOfItems: number;
|
|
5
|
+
severityCounts: Record<ThymianReportSeverity, number>;
|
|
6
|
+
};
|
|
7
|
+
export type ReportAnalysis = {
|
|
8
|
+
reports: ThymianReport[];
|
|
9
|
+
statistics: ThymianReportStatistics;
|
|
10
|
+
};
|
|
11
|
+
export declare function analyze(reports: ThymianReport[]): ReportAnalysis;
|
|
12
|
+
export interface Formatter<Options = Record<PropertyKey, unknown>> {
|
|
13
|
+
options: Options;
|
|
14
|
+
init(options: Options): void | Promise<void>;
|
|
15
|
+
report(report: ThymianReport): void | Promise<void>;
|
|
16
|
+
flush(): string | undefined | Promise<string | undefined>;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=formatter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../src/formatter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EAEb,qBAAqB,EACtB,MAAM,eAAe,CAAC;AAEvB,MAAM,MAAM,uBAAuB,GAAG;IACpC,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;CACvD,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,UAAU,EAAE,uBAAuB,CAAC;CACrC,CAAC;AAQF,wBAAgB,OAAO,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,cAAc,CAsBhE;AAED,MAAM,WAAW,SAAS,CAAC,OAAO,GAAG,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC;IAC/D,OAAO,EAAE,OAAO,CAAC;IAEjB,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7C,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpD,KAAK,IAAI,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CAC3D"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
function countItems(reports) {
|
|
2
|
+
return reports.flatMap((report) => report.sections?.flatMap((s) => s.items) ?? []);
|
|
3
|
+
}
|
|
4
|
+
export function analyze(reports) {
|
|
5
|
+
const items = countItems(reports);
|
|
6
|
+
const severityCounts = {
|
|
7
|
+
error: 0,
|
|
8
|
+
warn: 0,
|
|
9
|
+
hint: 0,
|
|
10
|
+
info: 0,
|
|
11
|
+
};
|
|
12
|
+
for (const item of items) {
|
|
13
|
+
severityCounts[item.severity]++;
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
reports,
|
|
17
|
+
statistics: {
|
|
18
|
+
numberOfReports: reports.length,
|
|
19
|
+
numberOfItems: items.length,
|
|
20
|
+
severityCounts,
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=formatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatter.js","sourceRoot":"","sources":["../src/formatter.ts"],"names":[],"mappings":"AAiBA,SAAS,UAAU,CAAC,OAAwB;IAC1C,OAAO,OAAO,CAAC,OAAO,CACpB,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAC3D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,OAAwB;IAC9C,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IAElC,MAAM,cAAc,GAA0C;QAC5D,KAAK,EAAE,CAAC;QACR,IAAI,EAAE,CAAC;QACP,IAAI,EAAE,CAAC;QACP,IAAI,EAAE,CAAC;KACR,CAAC;IAEF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;IAClC,CAAC;IAED,OAAO;QACL,OAAO;QACP,UAAU,EAAE;YACV,eAAe,EAAE,OAAO,CAAC,MAAM;YAC/B,aAAa,EAAE,KAAK,CAAC,MAAM;YAC3B,cAAc;SACf;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Logger, ThymianReport } from '@thymian/core';
|
|
2
|
+
import type { Formatter } from '../formatter.js';
|
|
3
|
+
export type CsvFormatterOptions = {
|
|
4
|
+
path: string;
|
|
5
|
+
};
|
|
6
|
+
export declare class CsvFormatter implements Formatter<CsvFormatterOptions> {
|
|
7
|
+
private readonly logger;
|
|
8
|
+
private stream;
|
|
9
|
+
options: CsvFormatterOptions;
|
|
10
|
+
constructor(logger: Logger);
|
|
11
|
+
flush(): Promise<string | undefined>;
|
|
12
|
+
init(options: CsvFormatterOptions): Promise<void>;
|
|
13
|
+
report(report: ThymianReport): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
export declare function thymianReportToCsvLines(report: ThymianReport): string[];
|
|
16
|
+
export declare function csvSafe(str: string | undefined): string;
|
|
17
|
+
//# sourceMappingURL=csv.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csv.d.ts","sourceRoot":"","sources":["../../src/formatters/csv.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,qBAAa,YAAa,YAAW,SAAS,CAAC,mBAAmB,CAAC;IAKrD,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,OAAO,CAAC,MAAM,CAAe;IAE7B,OAAO,EAAG,mBAAmB,CAAC;gBAED,MAAM,EAAE,MAAM;IAE3C,KAAK,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAUpC,IAAI,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBjD,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;CAmB7C;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,EAAE,CAkBvE;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAavD"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { createWriteStream } from 'node:fs';
|
|
2
|
+
export class CsvFormatter {
|
|
3
|
+
logger;
|
|
4
|
+
stream;
|
|
5
|
+
options;
|
|
6
|
+
constructor(logger) {
|
|
7
|
+
this.logger = logger;
|
|
8
|
+
}
|
|
9
|
+
flush() {
|
|
10
|
+
return new Promise((resolve) => {
|
|
11
|
+
this.stream.end(() => {
|
|
12
|
+
this.logger.debug(`Wrote CSV report to ${this.options.path}`);
|
|
13
|
+
resolve(undefined);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
init(options) {
|
|
18
|
+
this.options = options;
|
|
19
|
+
this.stream = createWriteStream(options.path, 'utf-8');
|
|
20
|
+
this.stream.on('error', (err) => {
|
|
21
|
+
this.logger.error(`Failed to write CSV report to ${this.options.path}: ${err.message}`);
|
|
22
|
+
});
|
|
23
|
+
return new Promise((resolve) => {
|
|
24
|
+
this.stream.on('ready', () => {
|
|
25
|
+
this.stream.write('source,section,severity,ruleName,message,details\n');
|
|
26
|
+
resolve();
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
report(report) {
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
const lines = thymianReportToCsvLines(report);
|
|
33
|
+
if (lines.length === 0) {
|
|
34
|
+
resolve();
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const data = lines.join('');
|
|
38
|
+
this.stream.write(data, (err) => {
|
|
39
|
+
if (err) {
|
|
40
|
+
reject(err);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
resolve();
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export function thymianReportToCsvLines(report) {
|
|
50
|
+
const lines = [];
|
|
51
|
+
if (!report.sections || report.sections.length === 0) {
|
|
52
|
+
// Report with no sections — emit a single row with source and message
|
|
53
|
+
lines.push(`${csvSafe(report.source)},,,,${csvSafe(report.message)},\n`);
|
|
54
|
+
return lines;
|
|
55
|
+
}
|
|
56
|
+
for (const section of report.sections) {
|
|
57
|
+
for (const item of section.items) {
|
|
58
|
+
lines.push(`${csvSafe(report.source)},${csvSafe(section.heading)},${csvSafe(item.severity)},${csvSafe(item.ruleName)},${csvSafe(item.message)},${csvSafe(item.details)}\n`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return lines;
|
|
62
|
+
}
|
|
63
|
+
export function csvSafe(str) {
|
|
64
|
+
if (!str) {
|
|
65
|
+
return '';
|
|
66
|
+
}
|
|
67
|
+
// Escape double quotes by doubling them, wrap in quotes if needed
|
|
68
|
+
const escaped = str.replaceAll('"', '""').replaceAll('\n', ' ');
|
|
69
|
+
if (escaped.includes(',') || escaped.includes('"') || escaped.includes(' ')) {
|
|
70
|
+
return `"${escaped}"`;
|
|
71
|
+
}
|
|
72
|
+
return escaped;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=csv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csv.js","sourceRoot":"","sources":["../../src/formatters/csv.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Logger, ThymianReport, ThymianReportSeverity } from '@thymian/core';
|
|
2
|
+
import { type Formatter } from '../formatter.js';
|
|
3
|
+
export declare const details: (text: string) => string;
|
|
4
|
+
export declare function mapSeverityToBadge(severity: ThymianReportSeverity): string;
|
|
5
|
+
export type MarkdownFormatterOptions = {
|
|
6
|
+
path: string;
|
|
7
|
+
};
|
|
8
|
+
export declare class MarkdownFormatter implements Formatter<MarkdownFormatterOptions> {
|
|
9
|
+
private readonly logger;
|
|
10
|
+
options: MarkdownFormatterOptions;
|
|
11
|
+
private readonly reports;
|
|
12
|
+
constructor(logger: Logger);
|
|
13
|
+
init(options: MarkdownFormatterOptions): void;
|
|
14
|
+
report(report: ThymianReport): void;
|
|
15
|
+
flush(): Promise<string | undefined>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=markdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/formatters/markdown.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,MAAM,EACN,aAAa,EACb,qBAAqB,EACtB,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAW,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG1D,eAAO,MAAM,OAAO,GAAI,MAAM,MAAM,KAAG,MAK1B,CAAC;AAEd,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,CAW1E;AAED,MAAM,MAAM,wBAAwB,GAAG;IACrC,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,qBAAa,iBAAkB,YAAW,SAAS,CAAC,wBAAwB,CAAC;IAK/D,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,OAAO,EAAG,wBAAwB,CAAC;IAEnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;gBAElB,MAAM,EAAE,MAAM;IAE3C,IAAI,CAAC,OAAO,EAAE,wBAAwB,GAAG,IAAI;IAI7C,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;IAI7B,KAAK,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;CAuE3C"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import { mkdir, writeFile } from 'fs/promises';
|
|
3
|
+
import { analyze } from '../formatter.js';
|
|
4
|
+
import { errorSymbol, hintSymbol, warnSymbol } from '../style.js';
|
|
5
|
+
export const details = (text) => `
|
|
6
|
+
<details>
|
|
7
|
+
<summary>More details</summary>
|
|
8
|
+
${text}
|
|
9
|
+
</details>`;
|
|
10
|
+
export function mapSeverityToBadge(severity) {
|
|
11
|
+
if (severity === 'error') {
|
|
12
|
+
return `${errorSymbol} error`;
|
|
13
|
+
}
|
|
14
|
+
if (severity === 'warn') {
|
|
15
|
+
return `${warnSymbol} warning`;
|
|
16
|
+
}
|
|
17
|
+
if (severity === 'hint') {
|
|
18
|
+
return `${hintSymbol} hint`;
|
|
19
|
+
}
|
|
20
|
+
return 'info';
|
|
21
|
+
}
|
|
22
|
+
export class MarkdownFormatter {
|
|
23
|
+
logger;
|
|
24
|
+
options;
|
|
25
|
+
reports = [];
|
|
26
|
+
constructor(logger) {
|
|
27
|
+
this.logger = logger;
|
|
28
|
+
}
|
|
29
|
+
init(options) {
|
|
30
|
+
this.options = options;
|
|
31
|
+
}
|
|
32
|
+
report(report) {
|
|
33
|
+
this.reports.push(report);
|
|
34
|
+
}
|
|
35
|
+
async flush() {
|
|
36
|
+
if (this.reports.length === 0) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
const analysis = analyze(this.reports);
|
|
40
|
+
const lines = [];
|
|
41
|
+
lines.push('# Thymian Report');
|
|
42
|
+
const { numberOfReports, numberOfItems, severityCounts } = analysis.statistics;
|
|
43
|
+
lines.push(`A total of ${numberOfReports} reports with ${numberOfItems} items were found.`);
|
|
44
|
+
lines.push(`Of these there are ${severityCounts.error} errors, ${severityCounts.warn} warnings, ${severityCounts.hint} hints and ${severityCounts.info} infos.`);
|
|
45
|
+
lines.push('');
|
|
46
|
+
for (const report of analysis.reports) {
|
|
47
|
+
lines.push(`## ${report.source}`);
|
|
48
|
+
lines.push('');
|
|
49
|
+
if (report.message) {
|
|
50
|
+
lines.push(` ${report.message}`);
|
|
51
|
+
lines.push('');
|
|
52
|
+
lines.push('');
|
|
53
|
+
}
|
|
54
|
+
if (report.sections) {
|
|
55
|
+
for (const section of report.sections) {
|
|
56
|
+
lines.push(`### ${section.heading}`);
|
|
57
|
+
lines.push('');
|
|
58
|
+
for (const item of section.items) {
|
|
59
|
+
const sev = mapSeverityToBadge(item.severity);
|
|
60
|
+
lines.push(`- **${sev}**: ${item.message}`);
|
|
61
|
+
if (item.ruleName) {
|
|
62
|
+
lines.push(`<br/> *Rule: ${item.ruleName}*`);
|
|
63
|
+
}
|
|
64
|
+
if (item.details) {
|
|
65
|
+
lines.push(details(item.details));
|
|
66
|
+
}
|
|
67
|
+
if (item.links && item.links.length > 0) {
|
|
68
|
+
lines.push(' Related links:');
|
|
69
|
+
for (const link of item.links) {
|
|
70
|
+
lines.push(` - [${link.title ?? link.url}](${link.url})`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
lines.push('');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
lines.push('');
|
|
78
|
+
}
|
|
79
|
+
await mkdir(path.dirname(this.options.path), { recursive: true });
|
|
80
|
+
await writeFile(this.options.path, lines.join('\n'), 'utf-8');
|
|
81
|
+
this.logger.debug(`Wrote Markdown report to ${this.options.path}.`);
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=markdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/formatters/markdown.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ThymianReport, ThymianReportSeverity } from '@thymian/core';
|
|
2
|
+
import { type Formatter } from '../formatter.js';
|
|
3
|
+
export declare function formatSeverityPrefix(severity: ThymianReportSeverity): string;
|
|
4
|
+
export type TextFormatterOptions = {
|
|
5
|
+
summaryOnly: boolean;
|
|
6
|
+
path?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare class TextFormatter implements Formatter<Partial<TextFormatterOptions>> {
|
|
9
|
+
options: TextFormatterOptions;
|
|
10
|
+
private readonly reports;
|
|
11
|
+
init(options: Partial<TextFormatterOptions>): void;
|
|
12
|
+
flush(): Promise<string | undefined>;
|
|
13
|
+
report(report: ThymianReport): void;
|
|
14
|
+
/**
|
|
15
|
+
* Ensure output is plain text (no ANSI escape sequences) when stdout is not a TTY.
|
|
16
|
+
*
|
|
17
|
+
* Chalk auto-detects TTY and sets `chalk.level = 0` for non-TTY, which prevents
|
|
18
|
+
* new styling. However, if chalk level was forced or if any ANSI codes slip through
|
|
19
|
+
* from other sources, this safety net strips them for non-TTY output.
|
|
20
|
+
*/
|
|
21
|
+
private ensurePlainTextForNonTty;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=text.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../src/formatters/text.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAI1E,OAAO,EAAW,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAsC1D,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,CAY5E;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,WAAW,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,qBAAa,aAAc,YAAW,SAAS,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC5E,OAAO,EAAG,oBAAoB,CAAC;IAE/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAE/C,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG,IAAI;IAO5C,KAAK,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAuF1C,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;IAInC;;;;;;OAMG;IACH,OAAO,CAAC,wBAAwB;CAMjC"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import { stripVTControlCharacters } from 'node:util';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { mkdir, writeFile } from 'fs/promises';
|
|
5
|
+
import { analyze } from '../formatter.js';
|
|
6
|
+
import { errorSymbol, hintSymbol, successSymbol, warnSymbol, } from '../style.js';
|
|
7
|
+
/**
|
|
8
|
+
* Visible column width of a severity prefix in a monospace terminal.
|
|
9
|
+
*
|
|
10
|
+
* Unicode symbols (✖ U+2716, ⚠ U+26A0, ℹ U+2139) each occupy one terminal
|
|
11
|
+
* column in standard monospace fonts. We use hardcoded values instead of
|
|
12
|
+
* `.length` on template strings because JavaScript's string length counts
|
|
13
|
+
* UTF-16 code units, which may differ from visual column width for characters
|
|
14
|
+
* outside the BMP. For these specific BMP symbols the values happen to match.
|
|
15
|
+
*
|
|
16
|
+
* Widths: "✖ error: " = 9, "⚠ warning: " = 11, "ℹ hint: " = 8
|
|
17
|
+
*/
|
|
18
|
+
function severityPrefixWidth(severity) {
|
|
19
|
+
switch (severity) {
|
|
20
|
+
case 'error':
|
|
21
|
+
return 9; // "✖ error: "
|
|
22
|
+
case 'warn':
|
|
23
|
+
return 11; // "⚠ warning: "
|
|
24
|
+
case 'hint':
|
|
25
|
+
return 8; // "ℹ hint: "
|
|
26
|
+
default:
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Regex matching severity-grouped section headings produced by the core report
|
|
32
|
+
* sorter in severity mode, e.g. "Errors (3)", "Warnings (1)", "Hints (2)", "Info (1)".
|
|
33
|
+
*/
|
|
34
|
+
const SEVERITY_HEADING_RE = /^(Errors|Warnings|Hints|Info)\s*\(\d+\)$/;
|
|
35
|
+
export function formatSeverityPrefix(severity) {
|
|
36
|
+
if (severity === 'hint') {
|
|
37
|
+
return `${chalk.blue(`${hintSymbol} ${severity}`)}: `;
|
|
38
|
+
}
|
|
39
|
+
if (severity === 'warn') {
|
|
40
|
+
return `${chalk.yellow(`${warnSymbol} warning`)}: `;
|
|
41
|
+
}
|
|
42
|
+
if (severity === 'error') {
|
|
43
|
+
return `${chalk.red(`${errorSymbol} ${severity}`)}: `;
|
|
44
|
+
}
|
|
45
|
+
return '';
|
|
46
|
+
}
|
|
47
|
+
export class TextFormatter {
|
|
48
|
+
options;
|
|
49
|
+
reports = [];
|
|
50
|
+
init(options) {
|
|
51
|
+
this.options = {
|
|
52
|
+
summaryOnly: false,
|
|
53
|
+
...options,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
async flush() {
|
|
57
|
+
if (this.reports.length === 0) {
|
|
58
|
+
const message = `${chalk.green(successSymbol)} No problems found`;
|
|
59
|
+
if (this.options.path) {
|
|
60
|
+
const plainText = stripVTControlCharacters(message);
|
|
61
|
+
await mkdir(path.dirname(this.options.path), { recursive: true });
|
|
62
|
+
await writeFile(this.options.path, plainText, 'utf-8');
|
|
63
|
+
}
|
|
64
|
+
return this.ensurePlainTextForNonTty(message);
|
|
65
|
+
}
|
|
66
|
+
const analysis = analyze(this.reports);
|
|
67
|
+
const lines = [];
|
|
68
|
+
if (!this.options.summaryOnly) {
|
|
69
|
+
for (const report of analysis.reports) {
|
|
70
|
+
lines.push(`${report.source} ${Array.from({
|
|
71
|
+
length: Math.max(1, 80 - report.source.length),
|
|
72
|
+
})
|
|
73
|
+
.map(() => '─')
|
|
74
|
+
.join('')}`);
|
|
75
|
+
lines.push('');
|
|
76
|
+
if (report.message) {
|
|
77
|
+
lines.push(` ${report.message}`);
|
|
78
|
+
lines.push('');
|
|
79
|
+
lines.push('');
|
|
80
|
+
}
|
|
81
|
+
if (report.sections && report.sections.length > 0) {
|
|
82
|
+
for (const section of report.sections) {
|
|
83
|
+
const isSeverityGrouped = SEVERITY_HEADING_RE.test(section.heading);
|
|
84
|
+
lines.push(` ${chalk.bold(section.heading)}`);
|
|
85
|
+
lines.push('');
|
|
86
|
+
for (const item of section.items) {
|
|
87
|
+
// When grouped by severity the heading already conveys the
|
|
88
|
+
// severity level → skip the per-item severity prefix to
|
|
89
|
+
// avoid redundant "Errors (3) ✖ error: …" lines.
|
|
90
|
+
const prefix = isSeverityGrouped
|
|
91
|
+
? ''
|
|
92
|
+
: formatSeverityPrefix(item.severity);
|
|
93
|
+
// 4 spaces base indentation + prefix + message
|
|
94
|
+
lines.push(` ${prefix}${item.message}`);
|
|
95
|
+
// When grouped by rule the heading already shows the rule
|
|
96
|
+
// name → skip the per-item rule name to avoid duplication.
|
|
97
|
+
const ruleRedundant = item.ruleName === section.heading;
|
|
98
|
+
if (item.ruleName && !ruleRedundant) {
|
|
99
|
+
// Align rule name under message text:
|
|
100
|
+
// 4 spaces base indent + prefix visible width
|
|
101
|
+
const indent = isSeverityGrouped
|
|
102
|
+
? 4
|
|
103
|
+
: 4 + severityPrefixWidth(item.severity);
|
|
104
|
+
lines.push(`${' '.repeat(indent)}${chalk.dim(item.ruleName)}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
lines.push('');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
lines.push('');
|
|
113
|
+
const { error, warn, hint } = analysis.statistics.severityCounts;
|
|
114
|
+
lines.push(`Found ${chalk.red(`${error} ${error === 1 ? 'error' : 'errors'}`)}, ${chalk.yellow(`${warn} ${warn === 1 ? 'warning' : 'warnings'}`)} and ${chalk.blue(`${hint} ${hint === 1 ? 'hint' : 'hints'}`)}.`);
|
|
115
|
+
const coloredOutput = lines.join('\n');
|
|
116
|
+
if (this.options.path) {
|
|
117
|
+
const plainText = stripVTControlCharacters(coloredOutput);
|
|
118
|
+
await mkdir(path.dirname(this.options.path), { recursive: true });
|
|
119
|
+
await writeFile(this.options.path, plainText, 'utf-8');
|
|
120
|
+
}
|
|
121
|
+
return this.ensurePlainTextForNonTty(coloredOutput);
|
|
122
|
+
}
|
|
123
|
+
report(report) {
|
|
124
|
+
this.reports.push(report);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Ensure output is plain text (no ANSI escape sequences) when stdout is not a TTY.
|
|
128
|
+
*
|
|
129
|
+
* Chalk auto-detects TTY and sets `chalk.level = 0` for non-TTY, which prevents
|
|
130
|
+
* new styling. However, if chalk level was forced or if any ANSI codes slip through
|
|
131
|
+
* from other sources, this safety net strips them for non-TTY output.
|
|
132
|
+
*/
|
|
133
|
+
ensurePlainTextForNonTty(output) {
|
|
134
|
+
if (!process.stdout.isTTY) {
|
|
135
|
+
return stripVTControlCharacters(output);
|
|
136
|
+
}
|
|
137
|
+
return output;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=text.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/formatters/text.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type Logger } from '@thymian/core';
|
|
2
|
+
import type { Formatter } from './formatter.js';
|
|
3
|
+
import { type CsvFormatterOptions } from './formatters/csv.js';
|
|
4
|
+
import { type MarkdownFormatterOptions } from './formatters/markdown.js';
|
|
5
|
+
import { type TextFormatterOptions } from './formatters/text.js';
|
|
6
|
+
export type Formatters = {
|
|
7
|
+
text: Partial<TextFormatterOptions>;
|
|
8
|
+
markdown: Partial<MarkdownFormatterOptions>;
|
|
9
|
+
csv: Partial<CsvFormatterOptions>;
|
|
10
|
+
};
|
|
11
|
+
export type ReporterPluginOptions = {
|
|
12
|
+
formatters?: Partial<Formatters>;
|
|
13
|
+
};
|
|
14
|
+
export type FormatterConstructor<T> = (logger: Logger) => Formatter<T>;
|
|
15
|
+
export type FormatterRegistryEntry<K extends keyof Formatters> = {
|
|
16
|
+
factory: FormatterConstructor<Formatters[K]>;
|
|
17
|
+
prepareOptions: <T = Formatters[K]>(options: Formatters[K], pluginOptions: {
|
|
18
|
+
cwd: string;
|
|
19
|
+
logger: Logger;
|
|
20
|
+
}) => Formatters[K] | T;
|
|
21
|
+
};
|
|
22
|
+
export declare const FORMATTER_REGISTRY: {
|
|
23
|
+
[K in keyof Formatters]: FormatterRegistryEntry<K>;
|
|
24
|
+
};
|
|
25
|
+
export declare function isValidFormatter(name: string): name is keyof Formatters;
|
|
26
|
+
export declare function getFormatters(config: ReporterPluginOptions['formatters'], cwd: string, logger: Logger): Promise<Formatter[]>;
|
|
27
|
+
//# sourceMappingURL=get-formatters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-formatters.d.ts","sourceRoot":"","sources":["../src/get-formatters.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,MAAM,EAAoB,MAAM,eAAe,CAAC;AAE9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAEL,KAAK,wBAAwB,EAC9B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAiB,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAEhF,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACpC,QAAQ,EAAE,OAAO,CAAC,wBAAwB,CAAC,CAAC;IAC5C,GAAG,EAAE,OAAO,CAAC,mBAAmB,CAAC,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,UAAU,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,oBAAoB,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC;AAEvE,MAAM,MAAM,sBAAsB,CAAC,CAAC,SAAS,MAAM,UAAU,IAAI;IAC/D,OAAO,EAAE,oBAAoB,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7C,cAAc,EAAE,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,EAChC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,EACtB,aAAa,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAC3C,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;CACxB,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE;KAC9B,CAAC,IAAI,MAAM,UAAU,GAAG,sBAAsB,CAAC,CAAC,CAAC;CAoC1C,CAAC;AAEX,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,MAAM,UAAU,CAEvE;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,qBAAqB,CAAC,YAAY,CAAM,EAChD,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,SAAS,EAAE,CAAC,CA+BtB"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { ThymianBaseError } from '@thymian/core';
|
|
3
|
+
import { CsvFormatter } from './formatters/csv.js';
|
|
4
|
+
import { MarkdownFormatter, } from './formatters/markdown.js';
|
|
5
|
+
import { TextFormatter } from './formatters/text.js';
|
|
6
|
+
export const FORMATTER_REGISTRY = {
|
|
7
|
+
text: {
|
|
8
|
+
factory: () => new TextFormatter(),
|
|
9
|
+
prepareOptions: (options, { cwd }) => ({
|
|
10
|
+
summaryOnly: false,
|
|
11
|
+
...options,
|
|
12
|
+
...(typeof options.path === 'string'
|
|
13
|
+
? { path: join(cwd, options.path) }
|
|
14
|
+
: {}),
|
|
15
|
+
}),
|
|
16
|
+
},
|
|
17
|
+
markdown: {
|
|
18
|
+
factory: (logger) => new MarkdownFormatter(logger),
|
|
19
|
+
prepareOptions: (options, pluginOptions) => ({
|
|
20
|
+
path: join(pluginOptions.cwd, typeof options.path === 'string'
|
|
21
|
+
? options.path
|
|
22
|
+
: '.thymian/reports/report.md'),
|
|
23
|
+
...options,
|
|
24
|
+
}),
|
|
25
|
+
},
|
|
26
|
+
csv: {
|
|
27
|
+
factory: (logger) => new CsvFormatter(logger),
|
|
28
|
+
prepareOptions: (options, { cwd }) => ({
|
|
29
|
+
path: join(cwd, typeof options.path === 'string'
|
|
30
|
+
? options.path
|
|
31
|
+
: '.thymian/reports/report.csv'),
|
|
32
|
+
...options,
|
|
33
|
+
}),
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
export function isValidFormatter(name) {
|
|
37
|
+
return Object.hasOwn(FORMATTER_REGISTRY, name);
|
|
38
|
+
}
|
|
39
|
+
export async function getFormatters(config = {}, cwd, logger) {
|
|
40
|
+
return Promise.all(Object.entries(config).map(async ([name, options]) => {
|
|
41
|
+
if (!isValidFormatter(name)) {
|
|
42
|
+
throw new ThymianBaseError(`Unknown formatter "${name}". Available formatters: ${Object.keys(FORMATTER_REGISTRY).join(', ')}.`, {
|
|
43
|
+
name: 'UnknownFormatterError',
|
|
44
|
+
ref: 'https://thymian.dev/references/errors/unknown-formatter-error/',
|
|
45
|
+
suggestions: [
|
|
46
|
+
'If you want to add your own formatter, implement a new plugin and listen on the `core.report` event and/or open a Github issue.',
|
|
47
|
+
],
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
const formatterConfig = FORMATTER_REGISTRY[name];
|
|
51
|
+
const formatter = formatterConfig.factory(logger);
|
|
52
|
+
const preparedOptions = formatterConfig.prepareOptions(options, {
|
|
53
|
+
cwd,
|
|
54
|
+
logger,
|
|
55
|
+
});
|
|
56
|
+
// we know that the options are valid, so we can safely cast them
|
|
57
|
+
await formatter.init(preparedOptions);
|
|
58
|
+
return formatter;
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=get-formatters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-formatters.js","sourceRoot":"","sources":["../src/get-formatters.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type ThymianPlugin } from '@thymian/core';
|
|
2
|
+
import { type Formatters } from './get-formatters.js';
|
|
3
|
+
export type ReporterPluginOptions = {
|
|
4
|
+
formatters?: Partial<Formatters>;
|
|
5
|
+
};
|
|
6
|
+
export declare const reporterPlugin: ThymianPlugin<ReporterPluginOptions>;
|
|
7
|
+
export default reporterPlugin;
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAsB,MAAM,eAAe,CAAC;AAEvE,OAAO,EAAE,KAAK,UAAU,EAAiB,MAAM,qBAAqB,CAAC;AAErE,MAAM,MAAM,qBAAqB,GAAG;IAClC,UAAU,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;CAClC,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,aAAa,CAAC,qBAAqB,CAgH/D,CAAC;AAEF,eAAe,cAAc,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import {} from '@thymian/core';
|
|
2
|
+
import { getFormatters } from './get-formatters.js';
|
|
3
|
+
export const reporterPlugin = {
|
|
4
|
+
name: '@thymian/plugin-reporter',
|
|
5
|
+
options: {
|
|
6
|
+
type: 'object',
|
|
7
|
+
additionalProperties: false,
|
|
8
|
+
properties: {
|
|
9
|
+
formatters: {
|
|
10
|
+
nullable: true,
|
|
11
|
+
description: 'Configuration for different report formatters',
|
|
12
|
+
type: 'object',
|
|
13
|
+
properties: {
|
|
14
|
+
text: {
|
|
15
|
+
nullable: true,
|
|
16
|
+
description: 'Configuration for the text (console) formatter',
|
|
17
|
+
type: 'object',
|
|
18
|
+
properties: {
|
|
19
|
+
summaryOnly: {
|
|
20
|
+
description: 'When true, only shows the summary without detailed reports',
|
|
21
|
+
type: 'boolean',
|
|
22
|
+
nullable: true,
|
|
23
|
+
},
|
|
24
|
+
path: {
|
|
25
|
+
description: 'File path where the plain text report will be saved (ANSI escape codes are stripped)',
|
|
26
|
+
type: 'string',
|
|
27
|
+
nullable: true,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
additionalProperties: false,
|
|
31
|
+
},
|
|
32
|
+
markdown: {
|
|
33
|
+
description: 'Configuration for the Markdown formatter',
|
|
34
|
+
nullable: true,
|
|
35
|
+
type: 'object',
|
|
36
|
+
properties: {
|
|
37
|
+
path: {
|
|
38
|
+
description: 'File path where the markdown report will be saved',
|
|
39
|
+
type: 'string',
|
|
40
|
+
nullable: true,
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
additionalProperties: false,
|
|
44
|
+
},
|
|
45
|
+
csv: {
|
|
46
|
+
description: 'Configuration for the CSV formatter',
|
|
47
|
+
nullable: true,
|
|
48
|
+
type: 'object',
|
|
49
|
+
properties: {
|
|
50
|
+
path: {
|
|
51
|
+
description: 'File path where the CSV report will be saved',
|
|
52
|
+
type: 'string',
|
|
53
|
+
nullable: true,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
additionalProperties: false,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
additionalProperties: false,
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
version: '0.x',
|
|
64
|
+
events: {
|
|
65
|
+
listensOn: ['core.report'],
|
|
66
|
+
},
|
|
67
|
+
actions: {
|
|
68
|
+
listensOn: ['core.report.flush', 'core.close'],
|
|
69
|
+
},
|
|
70
|
+
async plugin(emitter, logger, { formatters: userFormatters, cwd }) {
|
|
71
|
+
const formatters = Object.fromEntries(Object.entries({
|
|
72
|
+
text: {},
|
|
73
|
+
...(userFormatters ?? {}),
|
|
74
|
+
}).filter(([, options]) => options != null));
|
|
75
|
+
let hasFlushed = false;
|
|
76
|
+
const flushReporters = async () => {
|
|
77
|
+
if (hasFlushed) {
|
|
78
|
+
return {};
|
|
79
|
+
}
|
|
80
|
+
hasFlushed = true;
|
|
81
|
+
const results = await Promise.all(reporters.map(async (r) => r.flush()));
|
|
82
|
+
// Collect text output from formatters that return strings (e.g. TextFormatter)
|
|
83
|
+
const textParts = results.filter((r) => typeof r === 'string');
|
|
84
|
+
return textParts.length > 0 ? { text: textParts.join('\n') } : {};
|
|
85
|
+
};
|
|
86
|
+
const reporters = await getFormatters(formatters, cwd, logger);
|
|
87
|
+
emitter.on('core.report', async (report) => {
|
|
88
|
+
reporters.forEach((r) => r.report(report));
|
|
89
|
+
});
|
|
90
|
+
emitter.onAction('core.report.flush', async (_event, ctx) => {
|
|
91
|
+
ctx.reply(await flushReporters());
|
|
92
|
+
});
|
|
93
|
+
emitter.onAction('core.close', async (_event, ctx) => {
|
|
94
|
+
await flushReporters();
|
|
95
|
+
ctx.reply();
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
export default reporterPlugin;
|
|
100
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/style.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"style.d.ts","sourceRoot":"","sources":["../src/style.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,WAAM,CAAC;AAC/B,eAAO,MAAM,UAAU,WAAM,CAAC;AAC9B,eAAO,MAAM,UAAU,WAAM,CAAC;AAC9B,eAAO,MAAM,aAAa,WAAM,CAAC"}
|
package/dist/style.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"style.js","sourceRoot":"","sources":["../src/style.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG,GAAG,CAAC;AAC/B,MAAM,CAAC,MAAM,UAAU,GAAG,GAAG,CAAC;AAC9B,MAAM,CAAC,MAAM,UAAU,GAAG,GAAG,CAAC;AAC9B,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,CAAC"}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAgBjD"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function createList(list) {
|
|
2
|
+
if (list.length === 0) {
|
|
3
|
+
return '';
|
|
4
|
+
}
|
|
5
|
+
else if (list.length === 1) {
|
|
6
|
+
return `${list[0]}`;
|
|
7
|
+
}
|
|
8
|
+
else {
|
|
9
|
+
// length >= 2
|
|
10
|
+
const last = list.at(-1);
|
|
11
|
+
return (list
|
|
12
|
+
.slice(0, list.length - 1)
|
|
13
|
+
.map((el) => `${el}`)
|
|
14
|
+
.join(', ') + ` and ${last}`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,UAAU,CAAC,IAAc;IACvC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,EAAE,CAAC;IACZ,CAAC;SAAM,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IACtB,CAAC;SAAM,CAAC;QACN,cAAc;QACd,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAW,CAAC;QAEnC,OAAO,CACL,IAAI;aACD,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;aACzB,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC;aACpB,IAAI,CAAC,IAAI,CAAC,GAAG,QAAQ,IAAI,EAAE,CAC/B,CAAC;IACJ,CAAC;AACH,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@thymian/plugin-reporter",
|
|
3
|
+
"version": "0.0.0-PLACEHOLDER",
|
|
4
|
+
"description": "Thymian plugin for formatting and outputting conformance analysis reports",
|
|
5
|
+
"license": "AGPL-3.0-only",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/thymianofficial/thymian/issues"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://thymian.dev",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/thymianofficial/thymian.git",
|
|
13
|
+
"directory": "packages/plugin-reporter"
|
|
14
|
+
},
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"thymian",
|
|
20
|
+
"api-reports",
|
|
21
|
+
"ci",
|
|
22
|
+
"reporter",
|
|
23
|
+
"reporting",
|
|
24
|
+
"test-reporter"
|
|
25
|
+
],
|
|
26
|
+
"type": "module",
|
|
27
|
+
"main": "./dist/index.js",
|
|
28
|
+
"module": "./dist/index.js",
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"exports": {
|
|
31
|
+
"./package.json": "./package.json",
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"import": "./dist/index.js",
|
|
35
|
+
"default": "./dist/index.js"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"dist",
|
|
40
|
+
"!**/*.tsbuildinfo"
|
|
41
|
+
],
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"chalk": "^4.1.2",
|
|
44
|
+
"tslib": "^2.3.0",
|
|
45
|
+
"@thymian/core": "0.0.0-PLACEHOLDER"
|
|
46
|
+
}
|
|
47
|
+
}
|