@thymian/plugin-reporter 0.1.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.
package/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # Thymian
2
+
3
+ Add resilience and HTTP conformance to your API development workflow. Thymian is a lightweight, language-agnostic library that helps you build robust APIs.
4
+
5
+ ## @thymian/reporter
6
+
7
+ Event-based reporting for test results and issues.
8
+
9
+ ## Documentation
10
+
11
+ For comprehensive documentation, visit [Thymian Documentation](https://thymian.dev/).
@@ -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":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAOlC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,EAAE,OAAO,EAAkB,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAElE,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,IAAY,EAAU,EAAE,CAC9C;;;IAGE,IAAI;aACK,CAAC;AAEd,MAAM,UAAU,kBAAkB,CAAC,QAA+B;IAChE,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QACzB,OAAO,GAAG,WAAW,QAAQ,CAAC;IAChC,CAAC;IACD,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;QACxB,OAAO,GAAG,UAAU,UAAU,CAAC;IACjC,CAAC;IACD,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;QACxB,OAAO,GAAG,UAAU,OAAO,CAAC;IAC9B,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAMD,MAAM,OAAO,iBAAiB;IAKC;IAJ7B,OAAO,CAA4B;IAElB,OAAO,GAAoB,EAAE,CAAC;IAE/C,YAA6B,MAAc;QAAd,WAAM,GAAN,MAAM,CAAQ;IAAG,CAAC;IAE/C,IAAI,CAAC,OAAiC;QACpC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,MAAM,CAAC,MAAqB;QAC1B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9B,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAEvC,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QAE/B,MAAM,EAAE,eAAe,EAAE,aAAa,EAAE,cAAc,EAAE,GACtD,QAAQ,CAAC,UAAU,CAAC;QAEtB,KAAK,CAAC,IAAI,CACR,cAAc,eAAe,iBAAiB,aAAa,oBAAoB,CAChF,CAAC;QACF,KAAK,CAAC,IAAI,CACR,sBAAsB,cAAc,CAAC,KAAK,YAAY,cAAc,CAAC,IAAI,cAAc,cAAc,CAAC,IAAI,cAAc,cAAc,CAAC,IAAI,SAAS,CACrJ,CAAC;QACF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEf,KAAK,MAAM,MAAM,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;YACtC,KAAK,CAAC,IAAI,CAAC,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;YAClC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAEf,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBACnB,KAAK,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;gBAClC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACf,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACjB,CAAC;YAED,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;gBACpB,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;oBACtC,KAAK,CAAC,IAAI,CAAC,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;oBACrC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;oBAEf,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;wBACjC,MAAM,GAAG,GAAG,kBAAkB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;wBAC9C,KAAK,CAAC,IAAI,CAAC,OAAO,GAAG,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;wBAE5C,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;4BAClB,KAAK,CAAC,IAAI,CAAC,iBAAiB,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;wBAChD,CAAC;wBAED,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;4BACjB,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;wBACpC,CAAC;wBAED,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;4BACxC,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;4BAC/B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gCAC9B,KAAK,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC;4BAC7D,CAAC;wBACH,CAAC;oBACH,CAAC;oBAED,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACjB,CAAC;YACH,CAAC;YAED,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjB,CAAC;QAED,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAElE,MAAM,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;QAE9D,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,4BAA4B,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC;QAEpE,OAAO,SAAS,CAAC;IACnB,CAAC;CACF"}
@@ -0,0 +1,15 @@
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
+ //# 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;AAQ1D,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;IAqE1C,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;CAGpC"}
@@ -0,0 +1,84 @@
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
+ export function formatSeverityPrefix(severity) {
8
+ if (severity === 'hint') {
9
+ return `${chalk.blue(`${hintSymbol} ${severity}`)}: `;
10
+ }
11
+ if (severity === 'warn') {
12
+ return `${chalk.yellow(`${warnSymbol} warning`)}: `;
13
+ }
14
+ if (severity === 'error') {
15
+ return `${chalk.red(`${errorSymbol} ${severity}`)}: `;
16
+ }
17
+ return '';
18
+ }
19
+ export class TextFormatter {
20
+ options;
21
+ reports = [];
22
+ init(options) {
23
+ this.options = {
24
+ summaryOnly: false,
25
+ ...options,
26
+ };
27
+ }
28
+ async flush() {
29
+ if (this.reports.length === 0) {
30
+ const message = `${chalk.green(successSymbol)} No problems found`;
31
+ if (this.options.path) {
32
+ const plainText = stripVTControlCharacters(message);
33
+ await mkdir(path.dirname(this.options.path), { recursive: true });
34
+ await writeFile(this.options.path, plainText, 'utf-8');
35
+ }
36
+ return message;
37
+ }
38
+ const analysis = analyze(this.reports);
39
+ const lines = [];
40
+ if (!this.options.summaryOnly) {
41
+ for (const report of analysis.reports) {
42
+ lines.push(`${report.source} ${Array.from({
43
+ length: Math.max(1, 80 - report.source.length),
44
+ })
45
+ .map(() => '─')
46
+ .join('')}`);
47
+ lines.push('');
48
+ if (report.message) {
49
+ lines.push(` ${report.message}`);
50
+ lines.push('');
51
+ lines.push('');
52
+ }
53
+ if (report.sections && report.sections.length > 0) {
54
+ for (const section of report.sections) {
55
+ lines.push(` ${chalk.bold(section.heading)}`);
56
+ lines.push('');
57
+ for (const item of section.items) {
58
+ const prefix = formatSeverityPrefix(item.severity);
59
+ lines.push(` ${prefix}${item.message}`);
60
+ if (item.ruleName) {
61
+ lines.push(` ${chalk.dim(item.ruleName)}`);
62
+ }
63
+ }
64
+ lines.push('');
65
+ }
66
+ }
67
+ }
68
+ }
69
+ lines.push('');
70
+ const { error, warn, hint } = analysis.statistics.severityCounts;
71
+ 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'}`)}.`);
72
+ const coloredOutput = lines.join('\n');
73
+ if (this.options.path) {
74
+ const plainText = stripVTControlCharacters(coloredOutput);
75
+ await mkdir(path.dirname(this.options.path), { recursive: true });
76
+ await writeFile(this.options.path, plainText, 'utf-8');
77
+ }
78
+ return coloredOutput;
79
+ }
80
+ report(report) {
81
+ this.reports.push(report);
82
+ }
83
+ }
84
+ //# sourceMappingURL=text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/formatters/text.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAGrD,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,EAAE,OAAO,EAAkB,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EACL,WAAW,EACX,UAAU,EACV,aAAa,EACb,UAAU,GACX,MAAM,aAAa,CAAC;AAErB,MAAM,UAAU,oBAAoB,CAAC,QAA+B;IAClE,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;QACxB,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,UAAU,IAAI,QAAQ,EAAE,CAAC,IAAI,CAAC;IACxD,CAAC;IACD,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;QACxB,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,UAAU,UAAU,CAAC,IAAI,CAAC;IACtD,CAAC;IACD,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QACzB,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,WAAW,IAAI,QAAQ,EAAE,CAAC,IAAI,CAAC;IACxD,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC;AAOD,MAAM,OAAO,aAAa;IACxB,OAAO,CAAwB;IAEd,OAAO,GAAoB,EAAE,CAAC;IAE/C,IAAI,CAAC,OAAsC;QACzC,IAAI,CAAC,OAAO,GAAG;YACb,WAAW,EAAE,KAAK;YAClB,GAAG,OAAO;SACX,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9B,MAAM,OAAO,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,oBAAoB,CAAC;YAElE,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;gBACtB,MAAM,SAAS,GAAG,wBAAwB,CAAC,OAAO,CAAC,CAAC;gBACpD,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;gBAClE,MAAM,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;YACzD,CAAC;YAED,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACvC,MAAM,KAAK,GAAa,EAAE,CAAC;QAE3B,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;YAC9B,KAAK,MAAM,MAAM,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;gBACtC,KAAK,CAAC,IAAI,CACR,GAAG,MAAM,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC;oBAC7B,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;iBAC/C,CAAC;qBACC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC;qBACd,IAAI,CAAC,EAAE,CAAC,EAAE,CACd,CAAC;gBACF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBAEf,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;oBACnB,KAAK,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;oBAClC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;oBACf,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACjB,CAAC;gBAED,IAAI,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAClD,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;wBACtC,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;wBAC/C,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;wBAEf,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;4BACjC,MAAM,MAAM,GAAG,oBAAoB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;4BACnD,KAAK,CAAC,IAAI,CAAC,OAAO,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;4BAC3C,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gCAClB,KAAK,CAAC,IAAI,CAAC,UAAU,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;4BACnD,CAAC;wBACH,CAAC;wBAED,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;oBACjB,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,UAAU,CAAC,cAAc,CAAC;QACjE,KAAK,CAAC,IAAI,CACR,SAAS,KAAK,CAAC,GAAG,CAAC,GAAG,KAAK,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,KAAK,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,QAAQ,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,GAAG,CACvM,CAAC;QAEF,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEvC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YACtB,MAAM,SAAS,GAAG,wBAAwB,CAAC,aAAa,CAAC,CAAC;YAC1D,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAClE,MAAM,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;QACzD,CAAC;QAED,OAAO,aAAa,CAAC;IACvB,CAAC;IAED,MAAM,CAAC,MAAqB;QAC1B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5B,CAAC;CACF"}
@@ -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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"}
@@ -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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"}
@@ -0,0 +1,5 @@
1
+ export declare const errorSymbol = "\u2716";
2
+ export declare const warnSymbol = "\u26A0";
3
+ export declare const hintSymbol = "\u2139";
4
+ export declare const successSymbol = "\u2713";
5
+ //# sourceMappingURL=style.d.ts.map
@@ -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,5 @@
1
+ export const errorSymbol = '✖';
2
+ export const warnSymbol = '⚠';
3
+ export const hintSymbol = 'ℹ';
4
+ export const successSymbol = '✓';
5
+ //# sourceMappingURL=style.js.map
@@ -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"}
@@ -0,0 +1,2 @@
1
+ export declare function createList(list: string[]): string;
2
+ //# sourceMappingURL=utils.d.ts.map
@@ -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,29 @@
1
+ {
2
+ "name": "@thymian/plugin-reporter",
3
+ "version": "0.1.0",
4
+ "license": "AGPL-3.0-only",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "type": "module",
9
+ "main": "./dist/index.js",
10
+ "module": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "exports": {
13
+ "./package.json": "./package.json",
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js",
17
+ "default": "./dist/index.js"
18
+ }
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "!**/*.tsbuildinfo"
23
+ ],
24
+ "dependencies": {
25
+ "chalk": "^4.1.2",
26
+ "tslib": "^2.3.0",
27
+ "@thymian/core": "0.1.0"
28
+ }
29
+ }