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..d9309a02 100644
--- a/packages/code-analyzer-core/src/results.ts
+++ b/packages/code-analyzer-core/src/results.ts
@@ -4,6 +4,8 @@ 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";
+import fsp from "node:fs/promises";
export interface CodeLocation {
getFile(): string | undefined
@@ -32,6 +34,7 @@ export interface EngineRunResults {
export interface RunResults {
getRunDirectory(): string
+ getCoreVersion(): string
getViolationCount(): number
getViolationCountOfSeverity(severity: SeverityLevel): number
getViolations(): Violation[]
@@ -245,10 +248,16 @@ export class RunResultsImpl implements RunResults {
this.runDir = runDir;
}
- getRunDirectory() {
+ getRunDirectory(): string {
return this.runDir;
}
+ getCoreVersion(): string {
+ const pathToPackageJson: string = path.join(__dirname, '..', 'package.json');
+ const packageJson: {version: string} = JSON.parse(fs.readFileSync(pathToPackageJson, 'utf-8'));
+ return packageJson.version;
+ }
+
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