UNPKG

@eagleoutice/flowr-dev

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

117 lines 5.69 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.LinterOutputFormat = void 0; exports.formatLints = formatLints; /** * Machine-readable renderings of linting results, for the tools that consume them (a CI, a code scanner, an editor). * @module */ const linter_rules_1 = require("./linter-rules"); const linter_format_1 = require("./linter-format"); const files_1 = require("../util/files"); const doc_files_1 = require("../documentation/doc-util/doc-files"); /** The formats linting results are reported in. */ var LinterOutputFormat; (function (LinterOutputFormat) { /** flowR's own summary, made to be read by a human. The default, and the one {@link formatLints} leaves to flowR. */ LinterOutputFormat["Text"] = "text"; /** [SARIF 2.1.0](https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html), e.g. to upload to GitHub code scanning */ LinterOutputFormat["Sarif"] = "sarif"; /** [GitHub workflow commands](https://docs.github.com/actions/reference/workflow-commands-for-github-actions), one annotation per finding */ LinterOutputFormat["Github"] = "github"; })(LinterOutputFormat || (exports.LinterOutputFormat = LinterOutputFormat = {})); /** flattened, as neither format groups by rule */ function findings(results) { return Object.entries(results).flatMap(([name, perRuleResults]) => { const rule = name; const perRule = perRuleResults; /* staying silent about a rule that threw would claim it passed */ if (linter_format_1.LintingResults.isError(perRule)) { return [{ rule, message: `the linting rule failed: ${linter_format_1.LintingResults.stringifyError(perRule)}`, level: 'error', loc: undefined }]; } return perRule.results.map(result => ({ rule, message: linter_rules_1.LintingRules[rule].prettyPrint[linter_format_1.LintingPrettyPrintContext.Query](result, perRule['.meta']), level: (result.certainty === linter_format_1.LintingResultCertainty.Certain ? 'warning' : 'note'), loc: result.loc })); }); } /** Neither a github annotation nor its sarif ingestion attaches to an absolute path, so report it from the workspace. */ function reportedPath(file) { return (0, files_1.relativeTo)(process.env.GITHUB_WORKSPACE ?? process.cwd(), file); } /** a finding flowR cannot locate carries no location, rather than a broken one */ function sarifLocation(loc) { if (loc === undefined || loc[4] === undefined) { return []; } return [{ physicalLocation: { artifactLocation: { uri: reportedPath(loc[4]) }, region: { startLine: loc[0], startColumn: loc[1], endLine: loc[2], endColumn: loc[3] } } }]; } /** * Renders the linting results as SARIF 2.1.0, the format code scanners (e.g. GitHub's) ingest, on a single line. * Only the rules that produced a finding are described, as SARIF requires every reported rule to be declared. */ function lintsToSarif(results, flowrVersion) { const flat = findings(results); const reported = [...new Set(flat.map(f => f.rule))]; return JSON.stringify({ $schema: 'https://json.schemastore.org/sarif-2.1.0.json', version: '2.1.0', runs: [{ tool: { driver: { name: 'flowR', informationUri: doc_files_1.FlowrGithubRef, version: flowrVersion, rules: reported.map(name => ({ id: name, name: linter_rules_1.LintingRules[name].info.name, shortDescription: { text: linter_rules_1.LintingRules[name].info.description }, properties: { tags: linter_rules_1.LintingRules[name].info.tags } })) } }, results: flat.map(f => ({ ruleId: f.rule, level: f.level, message: { text: f.message }, locations: sarifLocation(f.loc) })) }] }); } /** GitHub has no 'note' */ const githubCommand = { error: 'error', warning: 'warning', note: 'notice' }; /** `%`, `\r` and `\n` would end the workflow command */ function escapeGithubData(text) { return text.replaceAll('%', '%25').replaceAll('\r', '%0D').replaceAll('\n', '%0A'); } /** Renders the linting results as GitHub workflow commands, which a workflow run turns into annotations. */ function lintsToGithub(results) { return findings(results).map(f => { const loc = f.loc !== undefined && f.loc[4] !== undefined ? f.loc : undefined; const where = loc === undefined ? '' : ` file=${escapeGithubData(reportedPath(loc[4]))},line=${loc[0]},col=${loc[1]},endLine=${loc[2]},endColumn=${loc[3]},`; return `::${githubCommand[f.level]}${where}${loc === undefined ? ' ' : ''}title=${escapeGithubData(f.rule)}::${escapeGithubData(f.message)}`; }).join('\n'); } /** * Renders the linting results in the given {@link LinterOutputFormat|format}, `undefined` for * {@link LinterOutputFormat.Text|Text}: that one is flowR's own summary, which only flowR itself renders. */ function formatLints(results, format, flowrVersion) { switch (format) { case LinterOutputFormat.Sarif: return lintsToSarif(results, flowrVersion); case LinterOutputFormat.Github: return lintsToGithub(results); case LinterOutputFormat.Text: return undefined; } } //# sourceMappingURL=linter-output.js.map