diff --git a/packages/code-analyzer-core/package.json b/packages/code-analyzer-core/package.json index 6b0513fc..86207a62 100644 --- a/packages/code-analyzer-core/package.json +++ b/packages/code-analyzer-core/package.json @@ -1,7 +1,7 @@ { "name": "@salesforce/code-analyzer-core", "description": "Core Package for the Salesforce Code Analyzer", - "version": "0.19.1", + "version": "0.20.0-SNAPSHOT", "author": "The Salesforce Code Analyzer Team", "license": "BSD-3-Clause", "homepage": "https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/overview", diff --git a/packages/code-analyzer-core/src/output-format.ts b/packages/code-analyzer-core/src/output-format.ts index 771ce1c9..a8698a97 100644 --- a/packages/code-analyzer-core/src/output-format.ts +++ b/packages/code-analyzer-core/src/output-format.ts @@ -14,6 +14,8 @@ export enum OutputFormat { SARIF = "SARIF" } +export const CODE_ANALYZER_CORE_NAME: string = 'code-analyzer'; + export abstract class OutputFormatter { abstract format(results: RunResults): string diff --git a/packages/code-analyzer-core/src/output-formats/json-output-format.ts b/packages/code-analyzer-core/src/output-formats/json-output-format.ts index be339d61..64317992 100644 --- a/packages/code-analyzer-core/src/output-formats/json-output-format.ts +++ b/packages/code-analyzer-core/src/output-formats/json-output-format.ts @@ -1,5 +1,5 @@ import {CodeLocation, RunResults, Violation} from "../results"; -import {OutputFormatter} from "../output-format"; +import {OutputFormatter, CODE_ANALYZER_CORE_NAME} from "../output-format"; import {Rule, SeverityLevel} from "../rules"; export type JsonResultsOutput = { @@ -64,7 +64,9 @@ export function toJsonResultsOutput(results: RunResults, sanitizeFcn: (text: str } function toJsonVersionObject(results: RunResults): JsonVersionOutput { - const versions: JsonVersionOutput = {}; + const versions: JsonVersionOutput = { + [CODE_ANALYZER_CORE_NAME]: results.getCoreVersion() + }; const engineNames: string[] = results.getEngineNames(); for (const engineName of engineNames) { versions[engineName] = results.getEngineRunResults(engineName).getEngineVersion(); diff --git a/packages/code-analyzer-core/src/output-formats/xml-output-format.ts b/packages/code-analyzer-core/src/output-formats/xml-output-format.ts index 9fdb7acd..6452dbc8 100644 --- a/packages/code-analyzer-core/src/output-formats/xml-output-format.ts +++ b/packages/code-analyzer-core/src/output-formats/xml-output-format.ts @@ -1,6 +1,6 @@ import {RunResults} from "../results"; import * as xmlbuilder from "xmlbuilder"; -import {OutputFormatter} from "../output-format"; +import {OutputFormatter, CODE_ANALYZER_CORE_NAME} from "../output-format"; import {JsonResultsOutput, toJsonResultsOutput} from "./json-output-format"; export class XmlOutputFormatter implements OutputFormatter { @@ -19,6 +19,7 @@ export class XmlOutputFormatter implements OutputFormatter { violationCountsNode.node('sev5').text(`${resultsOutput.violationCounts.sev5}`); const versionsNode: xmlbuilder.XMLElement = resultsNode.node('versions'); + versionsNode.node(CODE_ANALYZER_CORE_NAME).text(results.getCoreVersion()); const engineNames: string[] = results.getEngineNames(); for (const engineName of engineNames) { versionsNode.node(engineName).text(results.getEngineRunResults(engineName).getEngineVersion()); diff --git a/packages/code-analyzer-core/src/results.ts b/packages/code-analyzer-core/src/results.ts index 4a9a3978..83a2c661 100644 --- a/packages/code-analyzer-core/src/results.ts +++ b/packages/code-analyzer-core/src/results.ts @@ -4,6 +4,7 @@ import {getMessage} from "./messages"; import {Clock, RealClock, toAbsolutePath} from "./utils"; import {OutputFormat, OutputFormatter} from "./output-format"; import path from "node:path"; +import fs from "node:fs"; export interface CodeLocation { getFile(): string | undefined @@ -32,6 +33,7 @@ export interface EngineRunResults { export interface RunResults { getRunDirectory(): string + getCoreVersion(): string getViolationCount(): number getViolationCountOfSeverity(severity: SeverityLevel): number getViolations(): Violation[] @@ -238,6 +240,7 @@ export class UnexpectedErrorEngineRunResults implements EngineRunResults { export class RunResultsImpl implements RunResults { private readonly clock: Clock; private readonly runDir: string; + private coreVersion: string = ''; // This value will be overwritten before it ever has a chance to matter. private readonly engineRunResultsMap: Map = new Map(); constructor(clock: Clock = new RealClock(), runDir: string = process.cwd() + path.sep) { @@ -245,10 +248,19 @@ export class RunResultsImpl implements RunResults { this.runDir = runDir; } - getRunDirectory() { + getRunDirectory(): string { return this.runDir; } + getCoreVersion(): string { + if (!this.coreVersion) { + const pathToPackageJson: string = path.join(__dirname, '..', 'package.json'); + const packageJson: { version: string } = JSON.parse(fs.readFileSync(pathToPackageJson, 'utf-8')); + this.coreVersion = packageJson.version; + } + return this.coreVersion; + } + getViolations(): Violation[] { return Array.from(this.engineRunResultsMap.values()).flatMap( engineRunResults => engineRunResults.getViolations()); diff --git a/packages/code-analyzer-core/test/output-format.test.ts b/packages/code-analyzer-core/test/output-format.test.ts index 819cf5d8..6f313950 100644 --- a/packages/code-analyzer-core/test/output-format.test.ts +++ b/packages/code-analyzer-core/test/output-format.test.ts @@ -174,7 +174,10 @@ function getContentsOfExpectedOutputFile(expectedOutputFileName: string, escapeB } const encodedPathSepVar: string = encodeURI(path.sep); + const version: string = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf-8')).version; + return contents.replaceAll('{{PATHSEP}}', pathSepVar) + .replace("{{CORE_VERSION}}", version) .replaceAll(`{{ENCODEDPATHSEP}}`, encodedPathSepVar) .replaceAll('{{RUNDIR}}', runDirVar) .replaceAll('{{ENCODEDRUNDIR}}', encodedRunDir) diff --git a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/multipleViolations.goldfile.html b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/multipleViolations.goldfile.html index 8a84489e..2946fdfd 100644 --- a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/multipleViolations.goldfile.html +++ b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/multipleViolations.goldfile.html @@ -49,7 +49,7 @@ })(); // ==== START OF VIOLATIONS ==== - const data = {"runDir":"{{ESCAPEDRUNDIR}}","violationCounts":{"total":6,"sev1":0,"sev2":1,"sev3":3,"sev4":2,"sev5":0},"versions":{"stubEngine1":"0.0.1","stubEngine2":"0.1.0","stubEngine3":"1.0.0"},"violations":[{"rule":"stub1RuleA","engine":"stubEngine1","severity":4,"tags":["Recommended","CodeStyle"],"primaryLocationIndex":0,"locations":[{"file":"test{{PATHSEP}}config.test.ts","startLine":3,"startColumn":6,"endLine":11,"endColumn":8}],"message":"SomeViolationMessage1","resources":["https://example.com/stub1RuleA"]},{"rule":"stub1RuleA","engine":"stubEngine1","severity":4,"tags":["Recommended","CodeStyle"],"primaryLocationIndex":0,"locations":[{"file":"test{{PATHSEP}}test-data{{PATHSEP}}sample-input-files{{PATHSEP}}subfolder with spaces{{PATHSEP}}some-target-file.ts","startLine":10,"startColumn":4,"endLine":11,"endColumn":2}],"message":"SomeViolationMessage1","resources":["https://example.com/stub1RuleA"]},{"rule":"stub1RuleC","engine":"stubEngine1","severity":3,"tags":["Recommended","Performance","Custom"],"primaryLocationIndex":0,"locations":[{"file":"test{{PATHSEP}}code-analyzer.test.ts","startLine":21,"startColumn":7,"endLine":25,"endColumn":4}],"message":"SomeViolationMessage2","resources":["https://example.com/stub1RuleC","https://example.com/aViolationSpecificUrl1","https://example.com/violationSpecificUrl2"]},{"rule":"stub1RuleE","engine":"stubEngine1","severity":3,"tags":["Performance"],"primaryLocationIndex":0,"locations":[{"file":"test{{PATHSEP}}code-analyzer.test.ts","startLine":56,"startColumn":4}],"message":"Some Violation that contains\na new line in `it` and "various" 'quotes'. Also it has <brackets> that may need to be {escaped}.","resources":["https://example.com/stub1RuleE","https://example.com/stub1RuleE_2"]},{"rule":"stub2RuleC","engine":"stubEngine2","severity":2,"tags":["Recommended","BestPractice"],"primaryLocationIndex":2,"locations":[{"file":"test{{PATHSEP}}stubs.ts","startLine":4,"startColumn":13},{"file":"test{{PATHSEP}}test-helpers.ts","startLine":9,"startColumn":1},{"file":"test{{PATHSEP}}stubs.ts","startLine":76,"startColumn":8}],"message":"SomeViolationMessage3","resources":[]},{"rule":"stub3RuleA","engine":"stubEngine3","severity":3,"tags":["Recommended","ErrorProne"],"primaryLocationIndex":2,"locations":[{"file":"test{{PATHSEP}}stubs.ts","startLine":20,"startColumn":10,"endLine":22,"endColumn":25,"comment":"Comment at location 1"},{"file":"test{{PATHSEP}}test-helpers.ts","startLine":5,"startColumn":10,"comment":"Comment at location 2"},{"file":"test{{PATHSEP}}stubs.ts","startLine":90,"startColumn":1,"endLine":95,"endColumn":10}],"message":"SomeViolationMessage4","resources":[]}]}; + const data = {"runDir":"{{ESCAPEDRUNDIR}}","violationCounts":{"total":6,"sev1":0,"sev2":1,"sev3":3,"sev4":2,"sev5":0},"versions":{"code-analyzer":"{{CORE_VERSION}}","stubEngine1":"0.0.1","stubEngine2":"0.1.0","stubEngine3":"1.0.0"},"violations":[{"rule":"stub1RuleA","engine":"stubEngine1","severity":4,"tags":["Recommended","CodeStyle"],"primaryLocationIndex":0,"locations":[{"file":"test{{PATHSEP}}config.test.ts","startLine":3,"startColumn":6,"endLine":11,"endColumn":8}],"message":"SomeViolationMessage1","resources":["https://example.com/stub1RuleA"]},{"rule":"stub1RuleA","engine":"stubEngine1","severity":4,"tags":["Recommended","CodeStyle"],"primaryLocationIndex":0,"locations":[{"file":"test{{PATHSEP}}test-data{{PATHSEP}}sample-input-files{{PATHSEP}}subfolder with spaces{{PATHSEP}}some-target-file.ts","startLine":10,"startColumn":4,"endLine":11,"endColumn":2}],"message":"SomeViolationMessage1","resources":["https://example.com/stub1RuleA"]},{"rule":"stub1RuleC","engine":"stubEngine1","severity":3,"tags":["Recommended","Performance","Custom"],"primaryLocationIndex":0,"locations":[{"file":"test{{PATHSEP}}code-analyzer.test.ts","startLine":21,"startColumn":7,"endLine":25,"endColumn":4}],"message":"SomeViolationMessage2","resources":["https://example.com/stub1RuleC","https://example.com/aViolationSpecificUrl1","https://example.com/violationSpecificUrl2"]},{"rule":"stub1RuleE","engine":"stubEngine1","severity":3,"tags":["Performance"],"primaryLocationIndex":0,"locations":[{"file":"test{{PATHSEP}}code-analyzer.test.ts","startLine":56,"startColumn":4}],"message":"Some Violation that contains\na new line in `it` and "various" 'quotes'. Also it has <brackets> that may need to be {escaped}.","resources":["https://example.com/stub1RuleE","https://example.com/stub1RuleE_2"]},{"rule":"stub2RuleC","engine":"stubEngine2","severity":2,"tags":["Recommended","BestPractice"],"primaryLocationIndex":2,"locations":[{"file":"test{{PATHSEP}}stubs.ts","startLine":4,"startColumn":13},{"file":"test{{PATHSEP}}test-helpers.ts","startLine":9,"startColumn":1},{"file":"test{{PATHSEP}}stubs.ts","startLine":76,"startColumn":8}],"message":"SomeViolationMessage3","resources":[]},{"rule":"stub3RuleA","engine":"stubEngine3","severity":3,"tags":["Recommended","ErrorProne"],"primaryLocationIndex":2,"locations":[{"file":"test{{PATHSEP}}stubs.ts","startLine":20,"startColumn":10,"endLine":22,"endColumn":25,"comment":"Comment at location 1"},{"file":"test{{PATHSEP}}test-helpers.ts","startLine":5,"startColumn":10,"comment":"Comment at location 2"},{"file":"test{{PATHSEP}}stubs.ts","startLine":90,"startColumn":1,"endLine":95,"endColumn":10}],"message":"SomeViolationMessage4","resources":[]}]}; // ==== END OF VIOLATIONS ==== class Model { diff --git a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/multipleViolations.goldfile.json b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/multipleViolations.goldfile.json index 53b0522e..1806fe68 100644 --- a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/multipleViolations.goldfile.json +++ b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/multipleViolations.goldfile.json @@ -9,6 +9,7 @@ "sev5": 0 }, "versions": { + "code-analyzer": "{{CORE_VERSION}}", "stubEngine1": "0.0.1", "stubEngine2": "0.1.0", "stubEngine3": "1.0.0" diff --git a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/multipleViolations.goldfile.xml b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/multipleViolations.goldfile.xml index a32c30d2..9c15207c 100644 --- a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/multipleViolations.goldfile.xml +++ b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/multipleViolations.goldfile.xml @@ -10,6 +10,7 @@ 0 + {{CORE_VERSION}} 0.0.1 0.1.0 1.0.0 diff --git a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/unexpectedEngineErrorViolation.goldfile.html b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/unexpectedEngineErrorViolation.goldfile.html index fbd5ce03..78a3e204 100644 --- a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/unexpectedEngineErrorViolation.goldfile.html +++ b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/unexpectedEngineErrorViolation.goldfile.html @@ -49,7 +49,7 @@ })(); // ==== START OF VIOLATIONS ==== - const data = {"runDir":"{{ESCAPEDRUNDIR}}","violationCounts":{"total":1,"sev1":1,"sev2":0,"sev3":0,"sev4":0,"sev5":0},"versions":{"throwingEngine":"3.0.0"},"violations":[{"rule":"UnexpectedEngineError","engine":"throwingEngine","severity":1,"tags":[],"primaryLocationIndex":0,"locations":[{}],"message":"The engine with name 'throwingEngine' threw an unexpected error: SomeErrorMessageFromThrowingEngine","resources":[]}]}; + const data = {"runDir":"{{ESCAPEDRUNDIR}}","violationCounts":{"total":1,"sev1":1,"sev2":0,"sev3":0,"sev4":0,"sev5":0},"versions":{"code-analyzer":"{{CORE_VERSION}}","throwingEngine":"3.0.0"},"violations":[{"rule":"UnexpectedEngineError","engine":"throwingEngine","severity":1,"tags":[],"primaryLocationIndex":0,"locations":[{}],"message":"The engine with name 'throwingEngine' threw an unexpected error: SomeErrorMessageFromThrowingEngine","resources":[]}]}; // ==== END OF VIOLATIONS ==== class Model { diff --git a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/unexpectedEngineErrorViolation.goldfile.json b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/unexpectedEngineErrorViolation.goldfile.json index 1d566c26..9b46e90c 100644 --- a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/unexpectedEngineErrorViolation.goldfile.json +++ b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/unexpectedEngineErrorViolation.goldfile.json @@ -9,6 +9,7 @@ "sev5": 0 }, "versions": { + "code-analyzer": "{{CORE_VERSION}}", "throwingEngine": "3.0.0" }, "violations": [ diff --git a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/unexpectedEngineErrorViolation.goldfile.xml b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/unexpectedEngineErrorViolation.goldfile.xml index 2772c19c..977bd0a6 100644 --- a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/unexpectedEngineErrorViolation.goldfile.xml +++ b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/unexpectedEngineErrorViolation.goldfile.xml @@ -10,6 +10,7 @@ 0 + {{CORE_VERSION}} 3.0.0 diff --git a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/zeroViolations.goldfile.html b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/zeroViolations.goldfile.html index 08b0ed9f..ca80daf2 100644 --- a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/zeroViolations.goldfile.html +++ b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/zeroViolations.goldfile.html @@ -49,7 +49,7 @@ })(); // ==== START OF VIOLATIONS ==== - const data = {"runDir":"{{ESCAPEDRUNDIR}}","violationCounts":{"total":0,"sev1":0,"sev2":0,"sev3":0,"sev4":0,"sev5":0},"versions":{},"violations":[]}; + const data = {"runDir":"{{ESCAPEDRUNDIR}}","violationCounts":{"total":0,"sev1":0,"sev2":0,"sev3":0,"sev4":0,"sev5":0},"versions":{"code-analyzer":"{{CORE_VERSION}}"},"violations":[]}; // ==== END OF VIOLATIONS ==== class Model { diff --git a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/zeroViolations.goldfile.json b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/zeroViolations.goldfile.json index 75669f27..14a8fe63 100644 --- a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/zeroViolations.goldfile.json +++ b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/zeroViolations.goldfile.json @@ -8,6 +8,8 @@ "sev4": 0, "sev5": 0 }, - "versions": {}, + "versions": { + "code-analyzer": "{{CORE_VERSION}}" + }, "violations": [] } \ No newline at end of file diff --git a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/zeroViolations.goldfile.xml b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/zeroViolations.goldfile.xml index b9c8ab42..35ae84af 100644 --- a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/zeroViolations.goldfile.xml +++ b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/zeroViolations.goldfile.xml @@ -9,6 +9,8 @@ 0 0 - + + {{CORE_VERSION}} + \ No newline at end of file