generated from SAP/repository-template
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add new output format markdown (#258)
Added new `markdown` option for output as suggested in the issue. Fixes #178 JIRA: CPOUI5FOUNDATION-890 --------- Co-authored-by: Konrad <konrad.kost@gmx.de> Co-authored-by: Merlin Beutlberger <m.beutlberger@sap.com>
- Loading branch information
1 parent
9b4125b
commit 7329058
Showing
9 changed files
with
356 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
import {LintMessageSeverity, LintResult, LintMessage} from "../linter/LinterContext.js"; | ||
|
||
export class Markdown { | ||
format(lintResults: LintResult[], showDetails: boolean): string { | ||
let totalErrorCount = 0; | ||
let totalWarningCount = 0; | ||
let totalFatalErrorCount = 0; | ||
|
||
// Sort by file path | ||
lintResults.sort((a, b) => a.filePath.localeCompare(b.filePath)); | ||
|
||
let findings = ""; | ||
lintResults.forEach(({filePath, messages, errorCount, warningCount, fatalErrorCount}) => { | ||
if (!errorCount && !warningCount) { | ||
// Skip files without errors or warnings | ||
return; | ||
} | ||
// Accumulate totals | ||
totalErrorCount += errorCount; | ||
totalWarningCount += warningCount; | ||
totalFatalErrorCount += fatalErrorCount; | ||
|
||
// Add the file path as a section header | ||
findings += `### ${filePath}\n\n`; | ||
if (showDetails === true) { | ||
findings += `| Severity | Line | Message | Details |\n`; | ||
findings += `|----------|------|---------|---------|\n`; | ||
} else { | ||
findings += `| Severity | Line | Message |\n`; | ||
findings += `|----------|------|---------|\n`; | ||
} | ||
|
||
// Sort messages by severity (sorting order: fatal-errors, errors, warnings) | ||
messages.sort((a, b) => { | ||
// Handle fatal errors first to push them to the bottom | ||
if (a.fatal !== b.fatal) { | ||
return a.fatal ? -1 : 1; // Fatal errors go to the top | ||
} | ||
// Then, compare by severity | ||
if (a.severity !== b.severity) { | ||
return b.severity - a.severity; | ||
} | ||
// If severity is the same, compare by line number (handling nulls) | ||
if ((a.line ?? 0) !== (b.line ?? 0)) { | ||
return (a.line ?? 0) - (b.line ?? 0); | ||
} | ||
// If both severity and line number are the same, compare by column number (handling nulls) | ||
return (a.column ?? 0) - (b.column ?? 0); | ||
}); | ||
|
||
// Format each message | ||
messages.forEach((msg) => { | ||
const severity = this.formatSeverity(msg.severity, msg.fatal); | ||
const location = this.formatLocation(msg.line, msg.column); | ||
let details; | ||
if (showDetails) { | ||
details = ` ${this.formatMessageDetails(msg)} |`; | ||
} else { | ||
details = ""; | ||
} | ||
|
||
findings += `| ${severity} | \`${location}\` | ${msg.message} |${details}\n`; | ||
}); | ||
|
||
findings += "\n"; | ||
}); | ||
|
||
let summary = "## Summary\n\n"; | ||
summary += | ||
`> ${totalErrorCount + totalWarningCount} problems ` + | ||
`(${totalErrorCount} errors, ${totalWarningCount} warnings) \n`; | ||
if (totalFatalErrorCount) { | ||
summary += `> **${totalFatalErrorCount} fatal errors**\n`; | ||
} | ||
|
||
if (findings) { | ||
findings = `## Findings\n${findings}`; | ||
} | ||
|
||
let output = `# UI5 linter Report | ||
${summary} | ||
${findings}`; | ||
|
||
// Suggest using the details option if not all details are shown | ||
if (!showDetails && (totalErrorCount + totalWarningCount) > 0) { | ||
output += "**Note:** Use `ui5lint --details` to show more information about the findings.\n"; | ||
} | ||
return output; | ||
} | ||
|
||
// Formats the severity of the lint message using appropriate emoji | ||
private formatSeverity(severity: LintMessageSeverity, fatal: LintMessage["fatal"]): string { | ||
if (fatal === true) { | ||
return "Fatal Error"; | ||
} else if (severity === LintMessageSeverity.Warning) { | ||
return "Warning"; | ||
} else if (severity === LintMessageSeverity.Error) { | ||
return "Error"; | ||
} else { | ||
throw new Error(`Unknown severity: ${LintMessageSeverity[severity]}`); | ||
} | ||
} | ||
|
||
// Formats the location of the lint message (line and column numbers) | ||
private formatLocation(line?: number, column?: number): string { | ||
// Default to 0 if line or column are not provided | ||
return `${line ?? 0}:${column ?? 0}`; | ||
} | ||
|
||
// Formats additional message details if `showDetails` is true | ||
private formatMessageDetails(msg: LintMessage): string { | ||
if (!msg.messageDetails) { | ||
return ""; | ||
} | ||
// Replace multiple spaces or newlines with a single space for clean output | ||
return `${msg.messageDetails.replace(/\s\s+|\n/g, " ")}`; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
import anyTest, {TestFn} from "ava"; | ||
import {Markdown} from "../../../src/formatter/markdown.js"; | ||
import {LintResult, LintMessageSeverity} from "../../../src/linter/LinterContext.js"; | ||
|
||
const test = anyTest as TestFn<{ | ||
lintResults: LintResult[]; | ||
}>; | ||
|
||
test.beforeEach((t) => { | ||
t.context.lintResults = [ | ||
{ | ||
filePath: "webapp/Component.js", | ||
messages: [ | ||
{ | ||
ruleId: "rule1", | ||
severity: LintMessageSeverity.Error, | ||
line: 1, | ||
column: 1, | ||
message: "Error message", | ||
messageDetails: "Message details", | ||
}, | ||
{ | ||
ruleId: "rule2", | ||
severity: LintMessageSeverity.Warning, | ||
line: 2, | ||
column: 2, | ||
message: "Warning message", | ||
messageDetails: "Message details", | ||
}, | ||
], | ||
coverageInfo: [], | ||
errorCount: 1, | ||
fatalErrorCount: 0, | ||
warningCount: 1, | ||
}, | ||
{ | ||
filePath: "webapp/Main.controller.js", | ||
messages: [ | ||
{ | ||
ruleId: "rule3", | ||
severity: LintMessageSeverity.Error, | ||
line: 11, | ||
column: 3, | ||
message: "Another error message", | ||
messageDetails: "Message details", | ||
}, | ||
{ | ||
ruleId: "rule3", | ||
severity: LintMessageSeverity.Error, | ||
line: 12, | ||
column: 3, | ||
message: "Another error message", | ||
messageDetails: "Message details", | ||
fatal: true, | ||
}, | ||
{ | ||
ruleId: "rule3", | ||
severity: LintMessageSeverity.Error, | ||
line: 3, | ||
column: 6, | ||
message: "Another error message", | ||
messageDetails: "Message details", | ||
fatal: true, | ||
}, | ||
{ | ||
ruleId: "rule3", | ||
severity: LintMessageSeverity.Warning, | ||
line: 12, | ||
column: 3, | ||
message: "Another error message", | ||
messageDetails: "Message details", | ||
}, | ||
{ | ||
ruleId: "rule3", | ||
severity: LintMessageSeverity.Error, | ||
line: 11, | ||
column: 2, | ||
message: "Another error message", | ||
messageDetails: "Message details", | ||
}, | ||
], | ||
coverageInfo: [], | ||
errorCount: 4, | ||
fatalErrorCount: 2, | ||
warningCount: 1, | ||
}, | ||
]; | ||
}); | ||
|
||
test("Default", (t) => { | ||
const {lintResults} = t.context; | ||
|
||
const markdownFormatter = new Markdown(); | ||
const markdownResult = markdownFormatter.format(lintResults, false); | ||
|
||
t.snapshot(markdownResult); | ||
}); | ||
|
||
test("Details", (t) => { | ||
const {lintResults} = t.context; | ||
|
||
const markdownFormatter = new Markdown(); | ||
const markdownResult = markdownFormatter.format(lintResults, true); | ||
|
||
t.snapshot(markdownResult); | ||
}); | ||
|
||
test("No findings", (t) => { | ||
const markdownFormatter = new Markdown(); | ||
const markdownResult = markdownFormatter.format([], true); | ||
|
||
t.snapshot(markdownResult); | ||
}); |
Oops, something went wrong.