apex-code-coverage-transformer 2.22.1 → 2.23.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/CHANGELOG.md +7 -0
- package/README.md +20 -4
- package/lib/commands/acc-transformer/transform.d.ts +1 -1
- package/lib/commands/acc-transformer/transform.js +1 -0
- package/lib/commands/acc-transformer/transform.js.map +1 -1
- package/lib/transformers/coverageDataMerger.d.ts +3 -0
- package/lib/transformers/coverageDataMerger.js +38 -0
- package/lib/transformers/coverageDataMerger.js.map +1 -0
- package/lib/transformers/coverageProcessor.d.ts +6 -0
- package/lib/transformers/coverageProcessor.js +87 -0
- package/lib/transformers/coverageProcessor.js.map +1 -0
- package/lib/transformers/coverageTransformer.d.ts +3 -6
- package/lib/transformers/coverageTransformer.js +21 -108
- package/lib/transformers/coverageTransformer.js.map +1 -1
- package/lib/utils/readJson.d.ts +1 -0
- package/lib/utils/readJson.js +12 -0
- package/lib/utils/readJson.js.map +1 -0
- package/lib/utils/types.d.ts +13 -0
- package/messages/transformer.transform.md +2 -1
- package/oclif.manifest.json +5 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
|
|
6
6
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
7
7
|
|
|
8
|
+
## [2.23.0](https://github.com/mcarvin8/apex-code-coverage-transformer/compare/v2.22.1...v2.23.0) (2026-06-17)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* support multiple coverage JSON inputs with union merge ([#317](https://github.com/mcarvin8/apex-code-coverage-transformer/issues/317)) ([43ccda6](https://github.com/mcarvin8/apex-code-coverage-transformer/commit/43ccda6c2542cbb0be36faba37682a23b76758b8))
|
|
14
|
+
|
|
8
15
|
## [2.22.1](https://github.com/mcarvin8/apex-code-coverage-transformer/compare/v2.22.0...v2.22.1) (2026-06-15)
|
|
9
16
|
|
|
10
17
|
|
package/README.md
CHANGED
|
@@ -69,6 +69,9 @@ sf plugins install apex-code-coverage-transformer@latest
|
|
|
69
69
|
|
|
70
70
|
# Multiple formats at once
|
|
71
71
|
sf acc-transformer transform -j "coverage/test-result-codecoverage.json" -f "sonar" -f "cobertura" -f "jacoco"
|
|
72
|
+
|
|
73
|
+
# Merge multiple coverage JSONs of the same type (e.g. two deploy runs)
|
|
74
|
+
sf acc-transformer transform -j "coverage/coverage/coverage.json" -j "coverage2/coverage/coverage.json" -r "coverage.xml" -f "sonar"
|
|
72
75
|
```
|
|
73
76
|
|
|
74
77
|
3. **Upload to your tool** — see [CI/CD Integration](#cicd-integration).
|
|
@@ -111,11 +114,16 @@ Works with [sfdx-hardis](https://github.com/hardisgroupcom/sfdx-hardis):
|
|
|
111
114
|
|
|
112
115
|
```
|
|
113
116
|
USAGE
|
|
114
|
-
$ sf acc-transformer transform -j <value
|
|
115
|
-
|
|
117
|
+
$ sf acc-transformer transform -j <value>... [-r <value>] [-f <value>] [-i <value>] [-e <value>]
|
|
118
|
+
[--min-coverage <value>] [--max-annotations <value>] [--json]
|
|
116
119
|
|
|
117
120
|
FLAGS
|
|
118
|
-
-j, --coverage-json=<value
|
|
121
|
+
-j, --coverage-json=<value>... Path to a code coverage JSON from deploy or test. Repeat to merge
|
|
122
|
+
multiple files. When the same Apex file appears in more than one
|
|
123
|
+
input, covered lines are unioned across all inputs — if a line is
|
|
124
|
+
covered in any input it is counted as covered in the final report,
|
|
125
|
+
even if it is uncovered in others. All files must be the same type
|
|
126
|
+
(deploy or test).
|
|
119
127
|
-r, --output-report=<value> Output path (e.g. coverage.xml). Default: coverage.[xml|info] by format.
|
|
120
128
|
-f, --format=<value> Output format (repeat for multiple). Default: sonar.
|
|
121
129
|
Multiple formats append to filename, e.g. coverage-sonar.xml.
|
|
@@ -311,6 +319,8 @@ Create `.apexcodecovtransformer.config.json` in the project root to transform co
|
|
|
311
319
|
- `sf hardis project deploy smart` (if sfdx-hardis installed and `COVERAGE_FORMATTER_JSON=true`)
|
|
312
320
|
- `sf hardis org test apex` (if sfdx-hardis installed)
|
|
313
321
|
|
|
322
|
+
> **Note:** The hook fires once per command and always processes a single coverage JSON. It cannot merge multiple inputs. If you need to merge coverage from multiple runs (e.g. split test batches), run the `sf acc-transformer transform` command manually with multiple `-j` flags after all runs complete.
|
|
323
|
+
|
|
314
324
|
Sample configs: [Salesforce CLI](https://raw.githubusercontent.com/mcarvin8/apex-code-coverage-transformer/main/defaults/salesforce-cli/.apexcodecovtransformer.config.json), [SFDX Hardis](https://raw.githubusercontent.com/mcarvin8/apex-code-coverage-transformer/main/defaults/sfdx-hardis/.apexcodecovtransformer.config.json).
|
|
315
325
|
|
|
316
326
|
| Key | Required | Description |
|
|
@@ -346,6 +356,12 @@ Resolve by renaming one of the files or using `--ignore-package-directory` to ex
|
|
|
346
356
|
Warning: None of the files listed in the coverage JSON were processed. The coverage report will be empty.
|
|
347
357
|
```
|
|
348
358
|
|
|
359
|
+
**Mixed coverage types** — All `-j` inputs must be the same type (either all deploy or all test):
|
|
360
|
+
|
|
361
|
+
```
|
|
362
|
+
Error (1): All coverage JSON files must be the same type (deploy or test).
|
|
363
|
+
```
|
|
364
|
+
|
|
349
365
|
**Unknown JSON structure** — Input is not from deploy or test coverage:
|
|
350
366
|
|
|
351
367
|
```
|
|
@@ -388,4 +404,4 @@ The coverage report has been written to: coverage.xml
|
|
|
388
404
|
|
|
389
405
|
## License
|
|
390
406
|
|
|
391
|
-
[MIT](
|
|
407
|
+
[MIT](LICENSE.md)
|
|
@@ -5,7 +5,7 @@ export default class TransformerTransform extends SfCommand<TransformerTransform
|
|
|
5
5
|
static readonly description: string;
|
|
6
6
|
static readonly examples: string[];
|
|
7
7
|
static readonly flags: {
|
|
8
|
-
'coverage-json': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
'coverage-json': import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
|
|
9
9
|
'output-report': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
10
|
format: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
11
|
'ignore-package-directory': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -15,6 +15,7 @@ export default class TransformerTransform extends SfCommand {
|
|
|
15
15
|
summary: messages.getMessage('flags.coverage-json.summary'),
|
|
16
16
|
char: 'j',
|
|
17
17
|
required: true,
|
|
18
|
+
multiple: true,
|
|
18
19
|
}),
|
|
19
20
|
'output-report': Flags.file({
|
|
20
21
|
summary: messages.getMessage('flags.output-report.summary'),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transform.js","sourceRoot":"","sources":["../../../src/commands/acc-transformer/transform.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"transform.js","sourceRoot":"","sources":["../../../src/commands/acc-transformer/transform.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
export function mergeDeployCoverageData(inputs) {
|
|
3
|
+
const merged = {};
|
|
4
|
+
for (const input of inputs) {
|
|
5
|
+
for (const [className, fileInfo] of Object.entries(input)) {
|
|
6
|
+
if (!merged[className]) {
|
|
7
|
+
merged[className] = { ...fileInfo, s: { ...fileInfo.s } };
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
for (const [line, hit] of Object.entries(fileInfo.s)) {
|
|
11
|
+
merged[className].s[line] = Math.max(merged[className].s[line] ?? 0, hit);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return merged;
|
|
17
|
+
}
|
|
18
|
+
export function mergeTestCoverageData(inputs) {
|
|
19
|
+
const byName = new Map();
|
|
20
|
+
for (const input of inputs) {
|
|
21
|
+
for (const entry of input) {
|
|
22
|
+
if (!byName.has(entry.name)) {
|
|
23
|
+
byName.set(entry.name, { ...entry, lines: { ...entry.lines } });
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
const existing = byName.get(entry.name);
|
|
27
|
+
for (const [line, hit] of Object.entries(entry.lines)) {
|
|
28
|
+
existing.lines[line] = Math.max(existing.lines[line] ?? 0, hit);
|
|
29
|
+
}
|
|
30
|
+
existing.totalLines = Object.keys(existing.lines).length;
|
|
31
|
+
existing.totalCovered = Object.values(existing.lines).filter((v) => v === 1).length;
|
|
32
|
+
existing.coveredPercent = existing.totalLines > 0 ? (existing.totalCovered / existing.totalLines) * 100 : 0;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return [...byName.values()];
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=coverageDataMerger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coverageDataMerger.js","sourceRoot":"","sources":["../../src/transformers/coverageDataMerger.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAIb,MAAM,UAAU,uBAAuB,CAAC,MAA4B;IAClE,MAAM,MAAM,GAAuB,EAAE,CAAC;IACtC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,KAAK,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1D,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;gBACvB,MAAM,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC,EAAE,EAAE,GAAG,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC;YAC5D,CAAC;iBAAM,CAAC;gBACN,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;oBACrD,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;gBAC5E,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,MAA4B;IAChE,MAAM,MAAM,GAAG,IAAI,GAAG,EAA4B,CAAC;IACnD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;YAC1B,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC5B,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,EAAE,GAAG,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAClE,CAAC;iBAAM,CAAC;gBACN,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAE,CAAC;gBACzC,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;oBACtD,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;gBAClE,CAAC;gBACD,QAAQ,CAAC,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC;gBACzD,QAAQ,CAAC,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;gBACpF,QAAQ,CAAC,cAAc,GAAG,QAAQ,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,GAAG,QAAQ,CAAC,UAAU,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC9G,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { getCoverageHandler } from '../handlers/getHandler.js';
|
|
2
|
+
import { DeployCoverageData, TestCoverageData, CoverageProcessingContext, LineTotals, ProcessResult } from '../utils/types.js';
|
|
3
|
+
export declare function createHandlers(formats: string[]): Map<string, ReturnType<typeof getCoverageHandler>>;
|
|
4
|
+
export declare function countLines(lines: Record<string, number>): LineTotals;
|
|
5
|
+
export declare function processDeployCoverage(data: DeployCoverageData, context: CoverageProcessingContext): Promise<ProcessResult>;
|
|
6
|
+
export declare function processTestCoverage(data: TestCoverageData[], context: CoverageProcessingContext): Promise<ProcessResult>;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/* eslint-disable no-await-in-loop */
|
|
2
|
+
'use strict';
|
|
3
|
+
import { readFile } from 'node:fs/promises';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { matchesGlob } from '../utils/globMatcher.js';
|
|
6
|
+
import { getCoverageHandler } from '../handlers/getHandler.js';
|
|
7
|
+
import { findFilePath } from '../utils/findFilePath.js';
|
|
8
|
+
import { setCoveredLines } from '../utils/setCoveredLines.js';
|
|
9
|
+
import { mapLimit } from '../utils/mapLimit.js';
|
|
10
|
+
export function createHandlers(formats) {
|
|
11
|
+
const handlers = new Map();
|
|
12
|
+
for (const format of formats) {
|
|
13
|
+
handlers.set(format, getCoverageHandler(format));
|
|
14
|
+
}
|
|
15
|
+
return handlers;
|
|
16
|
+
}
|
|
17
|
+
function hasSourceContent(result) {
|
|
18
|
+
return typeof result === 'object' && result !== null && 'sourceContent' in result;
|
|
19
|
+
}
|
|
20
|
+
async function readSourceFile(absolutePath) {
|
|
21
|
+
try {
|
|
22
|
+
return await readFile(absolutePath, 'utf-8');
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export function countLines(lines) {
|
|
29
|
+
const totalLines = Object.keys(lines).length;
|
|
30
|
+
const coveredLines = Object.values(lines).filter((v) => v === 1).length;
|
|
31
|
+
return { totalLines, coveredLines };
|
|
32
|
+
}
|
|
33
|
+
export async function processDeployCoverage(data, context) {
|
|
34
|
+
let processed = 0;
|
|
35
|
+
let totalLines = 0;
|
|
36
|
+
let coveredLines = 0;
|
|
37
|
+
await mapLimit(Object.keys(data), context.concurrencyLimit, async (fileName) => {
|
|
38
|
+
const fileInfo = data[fileName];
|
|
39
|
+
const formattedName = fileName.replace(/no-map[\\/]+/, '');
|
|
40
|
+
const path = findFilePath(formattedName, context.filePathCache);
|
|
41
|
+
if (!path) {
|
|
42
|
+
context.warnings.push(`The file name ${formattedName} was not found in any package directory.`);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (context.excludePatterns.some((pattern) => matchesGlob(path, pattern, { matchBase: true }))) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const setCoveredResult = await setCoveredLines(path, context.repoRoot, fileInfo.s, context.handlers.has('html'));
|
|
49
|
+
const updatedLines = hasSourceContent(setCoveredResult) ? setCoveredResult.updatedLines : setCoveredResult;
|
|
50
|
+
const sourceContent = hasSourceContent(setCoveredResult) ? setCoveredResult.sourceContent : undefined;
|
|
51
|
+
fileInfo.s = updatedLines;
|
|
52
|
+
const counts = countLines(updatedLines);
|
|
53
|
+
totalLines += counts.totalLines;
|
|
54
|
+
coveredLines += counts.coveredLines;
|
|
55
|
+
for (const handler of context.handlers.values()) {
|
|
56
|
+
handler.processFile(path, formattedName, updatedLines, sourceContent);
|
|
57
|
+
}
|
|
58
|
+
processed++;
|
|
59
|
+
});
|
|
60
|
+
return { processed, totalLines, coveredLines };
|
|
61
|
+
}
|
|
62
|
+
export async function processTestCoverage(data, context) {
|
|
63
|
+
let processed = 0;
|
|
64
|
+
let totalLines = 0;
|
|
65
|
+
let coveredLines = 0;
|
|
66
|
+
await mapLimit(data, context.concurrencyLimit, async (entry) => {
|
|
67
|
+
const formattedName = entry.name.replace(/no-map[\\/]+/, '');
|
|
68
|
+
const path = findFilePath(formattedName, context.filePathCache);
|
|
69
|
+
if (!path) {
|
|
70
|
+
context.warnings.push(`The file name ${formattedName} was not found in any package directory.`);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (context.excludePatterns.some((pattern) => matchesGlob(path, pattern, { matchBase: true }))) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const counts = countLines(entry.lines);
|
|
77
|
+
totalLines += counts.totalLines;
|
|
78
|
+
coveredLines += counts.coveredLines;
|
|
79
|
+
const sourceContent = context.handlers.has('html') ? await readSourceFile(join(context.repoRoot, path)) : undefined;
|
|
80
|
+
for (const handler of context.handlers.values()) {
|
|
81
|
+
handler.processFile(path, formattedName, entry.lines, sourceContent);
|
|
82
|
+
}
|
|
83
|
+
processed++;
|
|
84
|
+
});
|
|
85
|
+
return { processed, totalLines, coveredLines };
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=coverageProcessor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coverageProcessor.js","sourceRoot":"","sources":["../../src/transformers/coverageProcessor.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
excludePatterns?: string[];
|
|
5
|
-
};
|
|
6
|
-
export declare function transformCoverageReport(jsonFilePath: string, outputReportPath: string, formats: string[], ignoreDirs: string[], options?: TransformOptions): Promise<{
|
|
1
|
+
import { TransformOptions } from '../utils/types.js';
|
|
2
|
+
export { TransformOptions };
|
|
3
|
+
export declare function transformCoverageReport(jsonFilePaths: string[], outputReportPath: string, formats: string[], ignoreDirs: string[], options?: TransformOptions): Promise<{
|
|
7
4
|
finalPaths: string[];
|
|
8
5
|
warnings: string[];
|
|
9
6
|
lineRate: number;
|
|
@@ -1,27 +1,33 @@
|
|
|
1
1
|
/* eslint-disable no-await-in-loop */
|
|
2
|
-
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
import { matchesGlob } from '../utils/globMatcher.js';
|
|
5
|
-
import { getCoverageHandler } from '../handlers/getHandler.js';
|
|
2
|
+
'use strict';
|
|
6
3
|
import { getPackageDirectories } from '../utils/getPackageDirectories.js';
|
|
7
|
-
import { findFilePath } from '../utils/findFilePath.js';
|
|
8
4
|
import { buildFilePathCache } from '../utils/buildFilePathCache.js';
|
|
9
|
-
import { setCoveredLines } from '../utils/setCoveredLines.js';
|
|
10
5
|
import { getConcurrencyThreshold } from '../utils/getConcurrencyThreshold.js';
|
|
11
6
|
import { checkCoverageDataType } from '../utils/setCoverageDataType.js';
|
|
12
|
-
import {
|
|
7
|
+
import { tryReadJson } from '../utils/readJson.js';
|
|
13
8
|
import { generateAndWriteReport } from './reportGenerator.js';
|
|
14
|
-
|
|
9
|
+
import { mergeDeployCoverageData, mergeTestCoverageData } from './coverageDataMerger.js';
|
|
10
|
+
import { createHandlers, processDeployCoverage, processTestCoverage } from './coverageProcessor.js';
|
|
11
|
+
export async function transformCoverageReport(jsonFilePaths, outputReportPath, formats, ignoreDirs, options) {
|
|
15
12
|
const warnings = [];
|
|
16
13
|
const finalPaths = [];
|
|
17
14
|
const formatAmount = formats.length;
|
|
18
|
-
const
|
|
19
|
-
|
|
15
|
+
const jsonDataItems = await Promise.all(jsonFilePaths.map((p) => tryReadJson(p, warnings)));
|
|
16
|
+
const validData = jsonDataItems.filter((d) => d !== null);
|
|
17
|
+
if (validData.length === 0)
|
|
20
18
|
return { finalPaths: [outputReportPath], warnings, lineRate: 0 };
|
|
21
|
-
const
|
|
19
|
+
const parsedItems = validData.map((d) => JSON.parse(d));
|
|
20
|
+
const types = parsedItems.map(checkCoverageDataType);
|
|
21
|
+
if (types.some((t) => t === 'Unknown')) {
|
|
22
|
+
throw new Error('The provided JSON does not match a known coverage data format from the Salesforce deploy or test command.');
|
|
23
|
+
}
|
|
24
|
+
const uniqueTypes = [...new Set(types)];
|
|
25
|
+
if (uniqueTypes.length > 1) {
|
|
26
|
+
throw new Error('All coverage JSON files must be the same type (deploy or test).');
|
|
27
|
+
}
|
|
28
|
+
const commandType = uniqueTypes[0];
|
|
22
29
|
const { repoRoot, packageDirectories } = await getPackageDirectories(ignoreDirs);
|
|
23
30
|
const handlers = createHandlers(formats);
|
|
24
|
-
const commandType = checkCoverageDataType(parsedData);
|
|
25
31
|
const concurrencyLimit = getConcurrencyThreshold();
|
|
26
32
|
// Build file path cache upfront to avoid O(n*m) directory traversals
|
|
27
33
|
const { cache: filePathCache, warnings: cacheWarnings } = await buildFilePathCache(packageDirectories, repoRoot);
|
|
@@ -35,16 +41,9 @@ export async function transformCoverageReport(jsonFilePath, outputReportPath, fo
|
|
|
35
41
|
filePathCache,
|
|
36
42
|
excludePatterns: options?.excludePatterns ?? [],
|
|
37
43
|
};
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
else if (commandType === 'TestCoverageData') {
|
|
43
|
-
processResult = await processTestCoverage(parsedData, context);
|
|
44
|
-
}
|
|
45
|
-
else {
|
|
46
|
-
throw new Error('The provided JSON does not match a known coverage data format from the Salesforce deploy or test command.');
|
|
47
|
-
}
|
|
44
|
+
const processResult = commandType === 'DeployCoverageData'
|
|
45
|
+
? await processDeployCoverage(mergeDeployCoverageData(parsedItems), context)
|
|
46
|
+
: await processTestCoverage(mergeTestCoverageData(parsedItems), context);
|
|
48
47
|
if (processResult.processed === 0) {
|
|
49
48
|
warnings.push('None of the files listed in the coverage JSON were processed. The coverage report will be empty.');
|
|
50
49
|
}
|
|
@@ -60,90 +59,4 @@ export async function transformCoverageReport(jsonFilePath, outputReportPath, fo
|
|
|
60
59
|
}
|
|
61
60
|
return { finalPaths, warnings, lineRate };
|
|
62
61
|
}
|
|
63
|
-
function hasSourceContent(result) {
|
|
64
|
-
return typeof result === 'object' && result !== null && 'sourceContent' in result;
|
|
65
|
-
}
|
|
66
|
-
async function readSourceFile(absolutePath) {
|
|
67
|
-
try {
|
|
68
|
-
return await readFile(absolutePath, 'utf-8');
|
|
69
|
-
}
|
|
70
|
-
catch {
|
|
71
|
-
return undefined;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
async function tryReadJson(path, warnings) {
|
|
75
|
-
try {
|
|
76
|
-
return await readFile(path, 'utf-8');
|
|
77
|
-
}
|
|
78
|
-
catch {
|
|
79
|
-
warnings.push(`Failed to read ${path}. Confirm file exists.`);
|
|
80
|
-
return null;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
function createHandlers(formats) {
|
|
84
|
-
const handlers = new Map();
|
|
85
|
-
for (const format of formats) {
|
|
86
|
-
handlers.set(format, getCoverageHandler(format));
|
|
87
|
-
}
|
|
88
|
-
return handlers;
|
|
89
|
-
}
|
|
90
|
-
function countLines(lines) {
|
|
91
|
-
const totalLines = Object.keys(lines).length;
|
|
92
|
-
const coveredLines = Object.values(lines).filter((v) => v === 1).length;
|
|
93
|
-
return { totalLines, coveredLines };
|
|
94
|
-
}
|
|
95
|
-
async function processDeployCoverage(data, context) {
|
|
96
|
-
let processed = 0;
|
|
97
|
-
let totalLines = 0;
|
|
98
|
-
let coveredLines = 0;
|
|
99
|
-
await mapLimit(Object.keys(data), context.concurrencyLimit, async (fileName) => {
|
|
100
|
-
const fileInfo = data[fileName];
|
|
101
|
-
const formattedName = fileName.replace(/no-map[\\/]+/, '');
|
|
102
|
-
const path = findFilePath(formattedName, context.filePathCache);
|
|
103
|
-
if (!path) {
|
|
104
|
-
context.warnings.push(`The file name ${formattedName} was not found in any package directory.`);
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
if (context.excludePatterns.some((pattern) => matchesGlob(path, pattern, { matchBase: true }))) {
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
const setCoveredResult = await setCoveredLines(path, context.repoRoot, fileInfo.s, context.handlers.has('html'));
|
|
111
|
-
const updatedLines = hasSourceContent(setCoveredResult) ? setCoveredResult.updatedLines : setCoveredResult;
|
|
112
|
-
const sourceContent = hasSourceContent(setCoveredResult) ? setCoveredResult.sourceContent : undefined;
|
|
113
|
-
fileInfo.s = updatedLines;
|
|
114
|
-
const counts = countLines(updatedLines);
|
|
115
|
-
totalLines += counts.totalLines;
|
|
116
|
-
coveredLines += counts.coveredLines;
|
|
117
|
-
for (const handler of context.handlers.values()) {
|
|
118
|
-
handler.processFile(path, formattedName, updatedLines, sourceContent);
|
|
119
|
-
}
|
|
120
|
-
processed++;
|
|
121
|
-
});
|
|
122
|
-
return { processed, totalLines, coveredLines };
|
|
123
|
-
}
|
|
124
|
-
async function processTestCoverage(data, context) {
|
|
125
|
-
let processed = 0;
|
|
126
|
-
let totalLines = 0;
|
|
127
|
-
let coveredLines = 0;
|
|
128
|
-
await mapLimit(data, context.concurrencyLimit, async (entry) => {
|
|
129
|
-
const formattedName = entry.name.replace(/no-map[\\/]+/, '');
|
|
130
|
-
const path = findFilePath(formattedName, context.filePathCache);
|
|
131
|
-
if (!path) {
|
|
132
|
-
context.warnings.push(`The file name ${formattedName} was not found in any package directory.`);
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
if (context.excludePatterns.some((pattern) => matchesGlob(path, pattern, { matchBase: true }))) {
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
const counts = countLines(entry.lines);
|
|
139
|
-
totalLines += counts.totalLines;
|
|
140
|
-
coveredLines += counts.coveredLines;
|
|
141
|
-
const sourceContent = context.handlers.has('html') ? await readSourceFile(join(context.repoRoot, path)) : undefined;
|
|
142
|
-
for (const handler of context.handlers.values()) {
|
|
143
|
-
handler.processFile(path, formattedName, entry.lines, sourceContent);
|
|
144
|
-
}
|
|
145
|
-
processed++;
|
|
146
|
-
});
|
|
147
|
-
return { processed, totalLines, coveredLines };
|
|
148
|
-
}
|
|
149
62
|
//# sourceMappingURL=coverageTransformer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coverageTransformer.js","sourceRoot":"","sources":["../../src/transformers/coverageTransformer.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,
|
|
1
|
+
{"version":3,"file":"coverageTransformer.js","sourceRoot":"","sources":["../../src/transformers/coverageTransformer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function tryReadJson(path: string, warnings: string[]): Promise<string | null>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
export async function tryReadJson(path, warnings) {
|
|
4
|
+
try {
|
|
5
|
+
return await readFile(path, 'utf-8');
|
|
6
|
+
}
|
|
7
|
+
catch {
|
|
8
|
+
warnings.push(`Failed to read ${path}. Confirm file exists.`);
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=readJson.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readJson.js","sourceRoot":"","sources":["../../src/utils/readJson.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAY,EAAE,QAAkB;IAChE,IAAI,CAAC;QACH,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,QAAQ,CAAC,IAAI,CAAC,kBAAkB,IAAI,wBAAwB,CAAC,CAAC;QAC9D,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
package/lib/utils/types.d.ts
CHANGED
|
@@ -44,6 +44,19 @@ export type CoverageProcessingContext = {
|
|
|
44
44
|
filePathCache: Map<string, string>;
|
|
45
45
|
excludePatterns: string[];
|
|
46
46
|
};
|
|
47
|
+
export type CoverageInput = DeployCoverageData | TestCoverageData[];
|
|
48
|
+
export type LineTotals = {
|
|
49
|
+
totalLines: number;
|
|
50
|
+
coveredLines: number;
|
|
51
|
+
};
|
|
52
|
+
export type ProcessResult = {
|
|
53
|
+
processed: number;
|
|
54
|
+
} & LineTotals;
|
|
55
|
+
export type TransformOptions = {
|
|
56
|
+
minCoverage?: number;
|
|
57
|
+
maxAnnotations?: number;
|
|
58
|
+
excludePatterns?: string[];
|
|
59
|
+
};
|
|
47
60
|
type SonarLine = {
|
|
48
61
|
'@lineNumber': number;
|
|
49
62
|
'@covered': boolean;
|
|
@@ -15,10 +15,11 @@ Transform Salesforce Apex code coverage JSONs created during deployments and tes
|
|
|
15
15
|
- `sf acc-transformer transform -j "coverage.json" -r "coverage.md" -f "markdown"`
|
|
16
16
|
- `sf acc-transformer transform -j "coverage.json" -r "coverage.txt" -f "github-actions"`
|
|
17
17
|
- `sf acc-transformer transform -j "coverage.json" -i "force-app"`
|
|
18
|
+
- `sf acc-transformer transform -j "coverage1.json" -j "coverage2.json" -r "coverage.xml" -f "sonar"`
|
|
18
19
|
|
|
19
20
|
# flags.coverage-json.summary
|
|
20
21
|
|
|
21
|
-
Path to
|
|
22
|
+
Path to a code coverage JSON file created by the Salesforce CLI deploy or test command. Repeat the flag to merge multiple files. When the same Apex file appears in multiple inputs, covered lines are unioned across all inputs — if a line is covered in any input it is counted as covered in the final report, even if it is uncovered in others.
|
|
22
23
|
|
|
23
24
|
# flags.output-report.summary
|
|
24
25
|
|
package/oclif.manifest.json
CHANGED
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
"`sf acc-transformer transform -j \"coverage.json\" -r \"coverage.info\" -f \"lcovonly\"`",
|
|
12
12
|
"`sf acc-transformer transform -j \"coverage.json\" -r \"coverage.md\" -f \"markdown\"`",
|
|
13
13
|
"`sf acc-transformer transform -j \"coverage.json\" -r \"coverage.txt\" -f \"github-actions\"`",
|
|
14
|
-
"`sf acc-transformer transform -j \"coverage.json\" -i \"force-app\"`"
|
|
14
|
+
"`sf acc-transformer transform -j \"coverage.json\" -i \"force-app\"`",
|
|
15
|
+
"`sf acc-transformer transform -j \"coverage1.json\" -j \"coverage2.json\" -r \"coverage.xml\" -f \"sonar\"`"
|
|
15
16
|
],
|
|
16
17
|
"flags": {
|
|
17
18
|
"json": {
|
|
@@ -33,9 +34,9 @@
|
|
|
33
34
|
"char": "j",
|
|
34
35
|
"name": "coverage-json",
|
|
35
36
|
"required": true,
|
|
36
|
-
"summary": "Path to
|
|
37
|
+
"summary": "Path to a code coverage JSON file created by the Salesforce CLI deploy or test command. Repeat the flag to merge multiple files. When the same Apex file appears in multiple inputs, covered lines are unioned across all inputs — if a line is covered in any input it is counted as covered in the final report, even if it is uncovered in others.",
|
|
37
38
|
"hasDynamicHelp": false,
|
|
38
|
-
"multiple":
|
|
39
|
+
"multiple": true,
|
|
39
40
|
"type": "option"
|
|
40
41
|
},
|
|
41
42
|
"output-report": {
|
|
@@ -130,5 +131,5 @@
|
|
|
130
131
|
]
|
|
131
132
|
}
|
|
132
133
|
},
|
|
133
|
-
"version": "2.
|
|
134
|
+
"version": "2.23.0"
|
|
134
135
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apex-code-coverage-transformer",
|
|
3
3
|
"description": "Transform Salesforce Apex code coverage JSONs into other formats accepted by SonarQube, GitHub, GitLab, Azure, Bitbucket, etc.",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.23.0",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@oclif/core": "4.8.3",
|
|
7
7
|
"@salesforce/core": "8.26.3",
|