apex-code-coverage-transformer 4.1.0 → 4.3.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 CHANGED
@@ -5,6 +5,20 @@
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
+ ## [4.3.0](https://github.com/mcarvin8/apex-code-coverage-transformer/compare/v4.2.0...v4.3.0) (2026-11-09)
9
+
10
+
11
+ ### Features
12
+
13
+ * publish dependency-free apex-code-coverage-transformer-core package ([#441](https://github.com/mcarvin8/apex-code-coverage-transformer/issues/441)) ([76d7f65](https://github.com/mcarvin8/apex-code-coverage-transformer/commit/76d7f650234bac780b22ffeb22728530e3b78375))
14
+
15
+ ## [4.2.0](https://github.com/mcarvin8/apex-code-coverage-transformer/compare/v4.1.0...v4.2.0) (2026-11-09)
16
+
17
+
18
+ ### Features
19
+
20
+ * add programmatic API for library consumers ([#438](https://github.com/mcarvin8/apex-code-coverage-transformer/issues/438)) ([13679cd](https://github.com/mcarvin8/apex-code-coverage-transformer/commit/13679cd98ea71f3f077ccf17d594cd512b9a0878))
21
+
8
22
  ## [4.1.0](https://github.com/mcarvin8/apex-code-coverage-transformer/compare/v4.0.0...v4.1.0) (2026-11-05)
9
23
 
10
24
 
package/README.md CHANGED
@@ -6,8 +6,6 @@
6
6
  [![License](https://img.shields.io/badge/License-MIT-yellow.svg)](https://raw.githubusercontent.com/mcarvin8/apex-code-coverage-transformer/main/LICENSE.md)
7
7
  [![codecov](https://codecov.io/gh/mcarvin8/apex-code-coverage-transformer/graph/badge.svg?token=4IQ4NWTPQZ)](https://codecov.io/gh/mcarvin8/apex-code-coverage-transformer)
8
8
  [![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fmcarvin8%2Fapex-code-coverage-transformer%2Fmain)](https://dashboard.stryker-mutator.io/reports/github.com/mcarvin8/apex-code-coverage-transformer/main)
9
- <!-- gh-dependents-info-used-by-start -->
10
- [![Generated by github-dependents-info](https://img.shields.io/static/v1?label=Used%20by&message=2&color=informational&logo=slickpic)](https://github.com/mcarvin8/apex-code-coverage-transformer/blob/main/docs/github-dependents-info.md)<!-- gh-dependents-info-used-by-end -->
11
9
 
12
10
  Transforms (and optionally merges) Apex code coverage JSON from Salesforce deployments or test runs into formats used by SonarQube, Codecov, GitHub, GitLab, Azure DevOps, Bitbucket, and other tools, keeping coverage visible across pull requests, CI/CD pipelines, and code quality platforms. Available as a **Salesforce CLI plugin** for any provider, and as a **native GitHub Action** for GitHub Actions users who want to skip installing the CLI.
13
11
 
@@ -26,6 +24,7 @@ Transforms (and optionally merges) Apex code coverage JSON from Salesforce deplo
26
24
  - [Coverage Report Formats](#coverage-report-formats)
27
25
  - [CI/CD Integration](#cicd-integration)
28
26
  - [Automatic Transformation (Hook)](#automatic-transformation-hook)
27
+ - [Programmatic Usage](#programmatic-usage)
29
28
  - [Troubleshooting](#troubleshooting)
30
29
  - [Questions or Issues?](#questions-or-issues)
31
30
  - [License](#license)
@@ -197,7 +196,7 @@ EXAMPLES
197
196
  `sf acc-transformer transform -j "coverage1.json" -j "coverage2.json" -r "coverage.xml" -f "sonar"`
198
197
  ```
199
198
 
200
- _See code: [src/commands/acc-transformer/transform.ts](https://github.com/mcarvin8/apex-code-coverage-transformer/blob/v4.1.0/src/commands/acc-transformer/transform.ts)_
199
+ _See code: [src/commands/acc-transformer/transform.ts](https://github.com/mcarvin8/apex-code-coverage-transformer/blob/v4.3.0/src/commands/acc-transformer/transform.ts)_
201
200
  <!-- commandsstop -->
202
201
 
203
202
  ## Coverage Report Formats
@@ -546,6 +545,52 @@ Sample configs: [Salesforce CLI](https://raw.githubusercontent.com/mcarvin8/apex
546
545
  | `maxAnnotations` | No | Maximum `::warning` annotations emitted when `format` includes `github-actions` (default: `50`). |
547
546
  | `excludePatterns` | No | Comma-separated glob patterns for file paths to exclude (e.g. `**/*Test*,**/mock/**`). Matched against the relative path from the repo root. |
548
547
 
548
+ ## Programmatic Usage
549
+
550
+ Other Node.js tools (CLI plugins, scripts, CI wrappers) can call the transformer directly instead of shelling out to `sf acc-transformer transform`. The API is ESM-only and ships in two npm packages, built from the same source and released with the same version:
551
+
552
+ - **`apex-code-coverage-transformer-core`** (recommended for libraries): the API only, with **no runtime dependencies**.
553
+ - **`apex-code-coverage-transformer`**: the Salesforce CLI plugin package, which also exports the API but installs the plugin's dependencies (`@salesforce/core`, `@oclif/core` and others).
554
+
555
+ ```bash
556
+ npm install apex-code-coverage-transformer-core
557
+ ```
558
+
559
+ The examples below import from `apex-code-coverage-transformer`; with the core package, import from `apex-code-coverage-transformer-core` instead. Everything else is identical.
560
+
561
+ ```ts
562
+ import { transformCoverage, CoverageError } from 'apex-code-coverage-transformer';
563
+
564
+ const { summary, reports, warnings, passed } = await transformCoverage({
565
+ inputs: ['coverage/coverage/coverage.json'], // file paths and/or already-parsed coverage JSON
566
+ formats: ['cobertura', 'markdown'],
567
+ write: false, // return report contents without writing files
568
+ cwd: '/path/to/sfdx/project', // where to look for sfdx-project.json (default: process.cwd())
569
+ });
570
+
571
+ console.log(`${(summary.lineRate * 100).toFixed(2)}% across ${summary.fileCount} files`);
572
+ for (const file of summary.files) {
573
+ console.log(file.path, file.coveredLines, '/', file.totalLines, 'uncovered:', file.uncoveredLines);
574
+ }
575
+ const markdown = reports.find((r) => r.format === 'markdown')?.content;
576
+ ```
577
+
578
+ | Option | Default | Description |
579
+ | -------------------------- | --------------- | ------------------------------------------------------------------------------------------- |
580
+ | `inputs` | (required) | Coverage JSON file paths and/or parsed objects. All must be the same type (deploy or test). |
581
+ | `formats` | `['sonar']` | Output formats. `COVERAGE_FORMATS` lists every supported value. |
582
+ | `outputReport` | `coverage.xml` | Base path for written reports. Multiple formats add a suffix (e.g. `coverage-sonar.xml`). |
583
+ | `write` | `true` | When `false`, nothing is written and each report's `path` is `undefined`. |
584
+ | `cwd` | `process.cwd()` | Directory to search upward from for `sfdx-project.json`. |
585
+ | `ignorePackageDirectories` | `[]` | Package directories to skip. |
586
+ | `excludePatterns` | `[]` | Glob patterns for repo-relative file paths to exclude. |
587
+ | `minCoverage` | none | Sets `passed: false` when overall coverage is below this percentage. Nothing is thrown. |
588
+ | `maxAnnotations` | `50` | Annotation limit for the `github-actions` format. |
589
+
590
+ Known failures throw a `CoverageError` with a `code`: `UNKNOWN_INPUT_FORMAT`, `MIXED_INPUT_TYPES`, `UNSUPPORTED_FORMAT`, or `NO_SFDX_PROJECT`. Unreadable input files are skipped with a warning.
591
+
592
+ Everything exported from the package root follows semver. Deep imports are not supported.
593
+
549
594
  ## Troubleshooting
550
595
 
551
596
  **File not in package directory** — File is omitted from the report:
@@ -4,6 +4,7 @@ import { Flags, SfCommand } from '@salesforce/sf-plugins-core';
4
4
  import { transformCoverageReport } from '../../transformers/coverageTransformer.js';
5
5
  import { DEFAULT_MAX_ANNOTATIONS } from '../../transformers/generators/generateGitHubActions.js';
6
6
  import { formatOptions } from '../../utils/constants.js';
7
+ import { CoverageError } from '../../utils/errors.js';
7
8
  Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
8
9
  const messages = Messages.loadMessages('apex-code-coverage-transformer', 'transformer.transform');
9
10
  export default class TransformerTransform extends SfCommand {
@@ -45,7 +46,6 @@ export default class TransformerTransform extends SfCommand {
45
46
  }),
46
47
  'max-annotations': Flags.integer({
47
48
  summary: messages.getMessage('flags.max-annotations.summary'),
48
- required: false,
49
49
  min: 1,
50
50
  default: DEFAULT_MAX_ANNOTATIONS,
51
51
  }),
@@ -59,18 +59,26 @@ export default class TransformerTransform extends SfCommand {
59
59
  async run() {
60
60
  const { flags } = await this.parse(TransformerTransform);
61
61
  const warnings = [];
62
- const result = await transformCoverageReport(flags['coverage-json'], flags['output-report'], flags['format'] ?? ['sonar'], flags['ignore-package-directory'] ?? [], {
63
- minCoverage: flags['min-coverage'],
64
- maxAnnotations: flags['max-annotations'],
65
- excludePatterns: flags['exclude-pattern'],
66
- });
67
- warnings.push(...result.warnings);
68
- this.log(`The coverage report has been written to: ${result.finalPaths.join(', ')}`);
69
- if (warnings.length > 0) {
70
- warnings.forEach((warning) => {
71
- this.warn(warning);
62
+ let result;
63
+ try {
64
+ result = await transformCoverageReport(flags['coverage-json'], flags['output-report'], flags['format'] ?? ['sonar'], flags['ignore-package-directory'] ?? [], {
65
+ minCoverage: flags['min-coverage'],
66
+ maxAnnotations: flags['max-annotations'],
67
+ excludePatterns: flags['exclude-pattern'],
72
68
  });
73
69
  }
70
+ catch (error) {
71
+ // SfCommand prints an error's `code` in place of the exit code ("Error (UNKNOWN_INPUT_FORMAT)").
72
+ // Rethrow as a plain Error so CLI output stays "Error (1): ..." as documented.
73
+ if (error instanceof CoverageError)
74
+ throw new Error(error.message, { cause: error });
75
+ throw error;
76
+ }
77
+ warnings.push(...result.warnings);
78
+ this.log(`The coverage report has been written to: ${result.finalPaths.join(', ')}`);
79
+ warnings.forEach((warning) => {
80
+ this.warn(warning);
81
+ });
74
82
  if (!result.success) {
75
83
  this.error(`Coverage of ${(result.lineRate * 100).toFixed(2)}% is below the required minimum of ${flags['min-coverage']}%.`);
76
84
  }
@@ -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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"}
package/lib/index.d.ts CHANGED
@@ -1,2 +1,7 @@
1
- declare const _default: {};
2
- export default _default;
1
+ import { CoverageFormat } from './utils/types.js';
2
+ export { transformCoverage } from './transformers/coverageTransformer.js';
3
+ export type { CoverageErrorCode } from './utils/errors.js';
4
+ export { CoverageError } from './utils/errors.js';
5
+ export type { CoverageFormat, CoverageInput, CoverageSummary, DeployCoverageData, FileCoverageSummary, GeneratedReport, TestCoverageData, TransformCoverageOptions, TransformCoverageResult, } from './utils/types.js';
6
+ /** All supported output formats, sorted alphabetically. */
7
+ export declare const COVERAGE_FORMATS: readonly CoverageFormat[];
package/lib/index.js CHANGED
@@ -1,2 +1,13 @@
1
- export default {};
1
+ 'use strict';
2
+ /**
3
+ * Public programmatic API.
4
+ *
5
+ * Everything exported from this module follows semver. Anything imported from deeper paths
6
+ * is internal and may change in any release.
7
+ */
8
+ import { formatOptions } from './utils/constants.js';
9
+ export { transformCoverage } from './transformers/coverageTransformer.js';
10
+ export { CoverageError } from './utils/errors.js';
11
+ /** All supported output formats, sorted alphabetically. */
12
+ export const COVERAGE_FORMATS = Object.freeze([...formatOptions]);
2
13
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAe,EAAE,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb;;;;;GAKG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAE1E,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAalD,2DAA2D;AAC3D,MAAM,CAAC,MAAM,gBAAgB,GAA8B,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,aAAa,CAAqB,CAAC,CAAC"}
@@ -13,23 +13,17 @@ export function createHandlers(formats) {
13
13
  }
14
14
  return handlers;
15
15
  }
16
- async function readSourceFile(absolutePath) {
17
- try {
18
- return await readFile(absolutePath, 'utf-8');
19
- }
20
- catch {
21
- return undefined;
22
- }
23
- }
24
16
  async function loadSourceContent(path, context) {
25
17
  if (!context.handlers.has('html')) {
26
18
  return undefined;
27
19
  }
28
- const content = await readSourceFile(join(context.repoRoot, path));
29
- if (content === undefined) {
20
+ try {
21
+ return await readFile(join(context.repoRoot, path), 'utf-8');
22
+ }
23
+ catch {
30
24
  context.warnings.push(`Could not read source file ${path}; the HTML report will show coverage for this file without line contents.`);
25
+ return undefined;
31
26
  }
32
- return content;
33
27
  }
34
28
  export function countLines(lines) {
35
29
  const totalLines = Object.keys(lines).length;
@@ -40,6 +34,7 @@ async function processEntries(items, context, getName, resolveLines) {
40
34
  let processed = 0;
41
35
  let totalLines = 0;
42
36
  let coveredLines = 0;
37
+ const files = [];
43
38
  await mapLimit(items, context.concurrencyLimit, async (item) => {
44
39
  const formattedName = getName(item).replace(/no-map[\\/]+/, '');
45
40
  const path = findFilePath(formattedName, context.filePathCache);
@@ -54,12 +49,29 @@ async function processEntries(items, context, getName, resolveLines) {
54
49
  const counts = countLines(lines);
55
50
  totalLines += counts.totalLines;
56
51
  coveredLines += counts.coveredLines;
52
+ files.push(summarizeFile(path, formattedName, lines, counts));
57
53
  for (const handler of context.handlers.values()) {
58
54
  handler.processFile(path, formattedName, lines, sourceContent);
59
55
  }
60
56
  processed++;
61
57
  });
62
- return { processed, totalLines, coveredLines };
58
+ files.sort((a, b) => a.path.localeCompare(b.path));
59
+ return { processed, totalLines, coveredLines, files };
60
+ }
61
+ function summarizeFile(path, name, lines, counts) {
62
+ // Line numbers are integer-like keys, which JavaScript always enumerates in ascending
63
+ // numeric order, so no explicit sort is needed.
64
+ const uncoveredLines = Object.entries(lines)
65
+ .filter(([, hit]) => hit !== 1)
66
+ .map(([line]) => Number(line));
67
+ return {
68
+ path,
69
+ name,
70
+ totalLines: counts.totalLines,
71
+ coveredLines: counts.coveredLines,
72
+ lineRate: counts.totalLines > 0 ? counts.coveredLines / counts.totalLines : 0,
73
+ uncoveredLines,
74
+ };
63
75
  }
64
76
  export function processDeployCoverage(data, context) {
65
77
  return processEntries(Object.keys(data), context, (fileName) => fileName, async (fileName, path) => ({
@@ -1 +1 @@
1
- {"version":3,"file":"coverageProcessor.js","sourceRoot":"","sources":["../../src/transformers/coverageProcessor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"coverageProcessor.js","sourceRoot":"","sources":["../../src/transformers/coverageProcessor.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,20 @@
1
- import { TransformOptions } from '../utils/types.js';
1
+ import { TransformCoverageOptions, TransformCoverageResult, TransformOptions } from '../utils/types.js';
2
2
  export { TransformOptions };
3
+ /**
4
+ * Transform Salesforce CLI Apex coverage JSON into one or more report formats.
5
+ *
6
+ * @example
7
+ * const { summary, reports } = await transformCoverage({
8
+ * inputs: ['coverage/coverage/coverage.json'],
9
+ * formats: ['cobertura', 'markdown'],
10
+ * write: false,
11
+ * });
12
+ */
13
+ export declare function transformCoverage(options: TransformCoverageOptions): Promise<TransformCoverageResult>;
14
+ /**
15
+ * Positional-argument wrapper used by the CLI command and GitHub Action.
16
+ * Library consumers should use {@link transformCoverage}.
17
+ */
3
18
  export declare function transformCoverageReport(jsonFilePaths: string[], outputReportPath: string, formats: string[], ignoreDirs: string[], options?: TransformOptions): Promise<{
4
19
  finalPaths: string[];
5
20
  warnings: string[];
@@ -1,32 +1,72 @@
1
1
  /* eslint-disable no-await-in-loop */
2
2
  'use strict';
3
+ import { writeFile } from 'node:fs/promises';
4
+ import { HandlerRegistry } from '../handlers/HandlerRegistry.js';
3
5
  import { buildFilePathCache } from '../utils/buildFilePathCache.js';
6
+ import { CoverageError } from '../utils/errors.js';
4
7
  import { getConcurrencyThreshold } from '../utils/getConcurrencyThreshold.js';
5
8
  import { getPackageDirectories } from '../utils/getPackageDirectories.js';
6
9
  import { tryReadJson } from '../utils/readJson.js';
7
10
  import { checkCoverageDataType } from '../utils/setCoverageDataType.js';
8
11
  import { mergeDeployCoverageData, mergeTestCoverageData } from './coverageDataMerger.js';
9
12
  import { createHandlers, processDeployCoverage, processTestCoverage } from './coverageProcessor.js';
10
- import { generateAndWriteReport } from './reportGenerator.js';
11
- export async function transformCoverageReport(jsonFilePaths, outputReportPath, formats, ignoreDirs, options) {
13
+ import { generateReportContent, getReportPath } from './reportGenerator.js';
14
+ const DEFAULT_OUTPUT_REPORT = 'coverage.xml';
15
+ async function loadInputs(inputs, warnings) {
16
+ const loaded = await Promise.all(inputs.map(async (input) => {
17
+ if (typeof input !== 'string')
18
+ return input;
19
+ const raw = await tryReadJson(input, warnings);
20
+ return raw === null ? null : JSON.parse(raw);
21
+ }));
22
+ return loaded.filter((item) => item !== null);
23
+ }
24
+ function validateFormats(formats) {
25
+ for (const format of formats) {
26
+ if (!HandlerRegistry.has(format)) {
27
+ throw new CoverageError(`Unsupported format: ${format}. Available formats: ${HandlerRegistry.getAvailableFormats().join(', ')}`, { code: 'UNSUPPORTED_FORMAT' });
28
+ }
29
+ }
30
+ }
31
+ function emptyResult(warnings) {
32
+ return {
33
+ reports: [],
34
+ summary: { lineRate: 0, coveredLines: 0, totalLines: 0, fileCount: 0, files: [] },
35
+ warnings,
36
+ passed: true,
37
+ };
38
+ }
39
+ /**
40
+ * Transform Salesforce CLI Apex coverage JSON into one or more report formats.
41
+ *
42
+ * @example
43
+ * const { summary, reports } = await transformCoverage({
44
+ * inputs: ['coverage/coverage/coverage.json'],
45
+ * formats: ['cobertura', 'markdown'],
46
+ * write: false,
47
+ * });
48
+ */
49
+ export async function transformCoverage(options) {
50
+ const formats = options.formats && options.formats.length > 0 ? options.formats : ['sonar'];
51
+ const outputReport = options.outputReport ?? DEFAULT_OUTPUT_REPORT;
52
+ const write = options.write ?? true;
12
53
  const warnings = [];
13
- const finalPaths = [];
14
- const formatAmount = formats.length;
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)
18
- return { finalPaths: [outputReportPath], warnings, lineRate: 0, success: true };
19
- const parsedItems = validData.map((d) => JSON.parse(d));
54
+ validateFormats(formats);
55
+ const parsedItems = await loadInputs(options.inputs, warnings);
56
+ if (parsedItems.length === 0)
57
+ return emptyResult(warnings);
20
58
  const types = parsedItems.map(checkCoverageDataType);
21
59
  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.');
60
+ throw new CoverageError('The provided JSON does not match a known coverage data format from the Salesforce deploy or test command.', { code: 'UNKNOWN_INPUT_FORMAT' });
23
61
  }
24
62
  const uniqueTypes = [...new Set(types)];
25
63
  if (uniqueTypes.length > 1) {
26
- throw new Error('All coverage JSON files must be the same type (deploy or test).');
64
+ throw new CoverageError('All coverage JSON files must be the same type (deploy or test).', {
65
+ code: 'MIXED_INPUT_TYPES',
66
+ });
27
67
  }
28
68
  const commandType = uniqueTypes[0];
29
- const { repoRoot, packageDirectories } = await getPackageDirectories(ignoreDirs);
69
+ const { repoRoot, packageDirectories } = await getPackageDirectories(options.ignorePackageDirectories ?? [], options.cwd);
30
70
  const handlers = createHandlers(formats);
31
71
  const concurrencyLimit = getConcurrencyThreshold();
32
72
  // Build file path cache upfront to avoid O(n*m) directory traversals
@@ -39,7 +79,7 @@ export async function transformCoverageReport(jsonFilePaths, outputReportPath, f
39
79
  concurrencyLimit,
40
80
  warnings,
41
81
  filePathCache,
42
- excludePatterns: options?.excludePatterns ?? [],
82
+ excludePatterns: options.excludePatterns ?? [],
43
83
  };
44
84
  const processResult = commandType === 'DeployCoverageData'
45
85
  ? await processDeployCoverage(mergeDeployCoverageData(parsedItems), context)
@@ -48,13 +88,52 @@ export async function transformCoverageReport(jsonFilePaths, outputReportPath, f
48
88
  warnings.push('None of the files listed in the coverage JSON were processed. The coverage report will be empty.');
49
89
  }
50
90
  const lineRate = processResult.totalLines > 0 ? processResult.coveredLines / processResult.totalLines : 0;
51
- const renderOptions = options?.maxAnnotations !== undefined ? { maxAnnotations: options.maxAnnotations } : undefined;
91
+ const renderOptions = options.maxAnnotations !== undefined ? { maxAnnotations: options.maxAnnotations } : undefined;
92
+ const reports = [];
52
93
  for (const [format, handler] of handlers.entries()) {
53
- const coverageObj = handler.finalize();
54
- const finalPath = await generateAndWriteReport(outputReportPath, coverageObj, format, formatAmount, renderOptions);
55
- finalPaths.push(finalPath);
94
+ const content = generateReportContent(handler.finalize(), format, renderOptions);
95
+ const report = { format: format, content };
96
+ if (write) {
97
+ report.path = getReportPath(outputReport, format, formats.length);
98
+ await writeFile(report.path, content);
99
+ }
100
+ reports.push(report);
56
101
  }
57
- const success = options?.minCoverage === undefined || lineRate * 100 >= options.minCoverage;
58
- return { finalPaths, warnings, lineRate, success };
102
+ return {
103
+ reports,
104
+ summary: {
105
+ lineRate,
106
+ coveredLines: processResult.coveredLines,
107
+ totalLines: processResult.totalLines,
108
+ fileCount: processResult.files.length,
109
+ files: processResult.files,
110
+ },
111
+ warnings,
112
+ passed: options.minCoverage === undefined || lineRate * 100 >= options.minCoverage,
113
+ };
114
+ }
115
+ /**
116
+ * Positional-argument wrapper used by the CLI command and GitHub Action.
117
+ * Library consumers should use {@link transformCoverage}.
118
+ */
119
+ export async function transformCoverageReport(jsonFilePaths, outputReportPath, formats, ignoreDirs, options) {
120
+ const result = await transformCoverage({
121
+ inputs: jsonFilePaths,
122
+ outputReport: outputReportPath,
123
+ formats: formats,
124
+ ignorePackageDirectories: ignoreDirs,
125
+ minCoverage: options?.minCoverage,
126
+ maxAnnotations: options?.maxAnnotations,
127
+ excludePatterns: options?.excludePatterns,
128
+ });
129
+ // The wrapper always writes (write defaults to true), so every report has a path.
130
+ const finalPaths = result.reports.map((r) => r.path);
131
+ return {
132
+ // Preserve historical behavior: report the requested path even when no inputs could be read.
133
+ finalPaths: finalPaths.length > 0 ? finalPaths : [outputReportPath],
134
+ warnings: result.warnings,
135
+ lineRate: result.summary.lineRate,
136
+ success: result.passed,
137
+ };
59
138
  }
60
139
  //# sourceMappingURL=coverageTransformer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"coverageTransformer.js","sourceRoot":"","sources":["../../src/transformers/coverageTransformer.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,YAAY,CAAC;AAEb,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,uBAAuB,EAAE,MAAM,qCAAqC,CAAC;AAC9E,OAAO,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAQxE,OAAO,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AACzF,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AACpG,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAI9D,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,aAAuB,EACvB,gBAAwB,EACxB,OAAiB,EACjB,UAAoB,EACpB,OAA0B;IAE1B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,MAAM,YAAY,GAAW,OAAO,CAAC,MAAM,CAAC;IAE5C,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC5F,MAAM,SAAS,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;IAEvE,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,UAAU,EAAE,CAAC,gBAAgB,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAE5G,MAAM,WAAW,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAkB,CAAC,CAAC;IACzE,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;IAErD,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CACb,2GAA2G,CAC5G,CAAC;IACJ,CAAC;IAED,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;IACxC,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC;IACrF,CAAC;IAED,MAAM,WAAW,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;IAEnC,MAAM,EAAE,QAAQ,EAAE,kBAAkB,EAAE,GAAG,MAAM,qBAAqB,CAAC,UAAU,CAAC,CAAC;IACjF,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IACzC,MAAM,gBAAgB,GAAG,uBAAuB,EAAE,CAAC;IAEnD,qEAAqE;IACrE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,aAAa,EAAE,GAAG,MAAM,kBAAkB,CAAC,kBAAkB,EAAE,QAAQ,CAAC,CAAC;IACjH,QAAQ,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,CAAC;IAEhC,MAAM,OAAO,GAA8B;QACzC,QAAQ;QACR,WAAW,EAAE,kBAAkB;QAC/B,QAAQ;QACR,gBAAgB;QAChB,QAAQ;QACR,aAAa;QACb,eAAe,EAAE,OAAO,EAAE,eAAe,IAAI,EAAE;KAChD,CAAC;IAEF,MAAM,aAAa,GACjB,WAAW,KAAK,oBAAoB;QAClC,CAAC,CAAC,MAAM,qBAAqB,CAAC,uBAAuB,CAAC,WAAmC,CAAC,EAAE,OAAO,CAAC;QACpG,CAAC,CAAC,MAAM,mBAAmB,CAAC,qBAAqB,CAAC,WAAmC,CAAC,EAAE,OAAO,CAAC,CAAC;IAErG,IAAI,aAAa,CAAC,SAAS,KAAK,CAAC,EAAE,CAAC;QAClC,QAAQ,CAAC,IAAI,CAAC,kGAAkG,CAAC,CAAC;IACpH,CAAC;IAED,MAAM,QAAQ,GAAG,aAAa,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,YAAY,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAE1G,MAAM,aAAa,GAAG,OAAO,EAAE,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAErH,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC;QACnD,MAAM,WAAW,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;QACvC,MAAM,SAAS,GAAG,MAAM,sBAAsB,CAAC,gBAAgB,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC;QACnH,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC7B,CAAC;IAED,MAAM,OAAO,GAAG,OAAO,EAAE,WAAW,KAAK,SAAS,IAAI,QAAQ,GAAG,GAAG,IAAI,OAAO,CAAC,WAAW,CAAC;IAE5F,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;AACrD,CAAC"}
1
+ {"version":3,"file":"coverageTransformer.js","sourceRoot":"","sources":["../../src/transformers/coverageTransformer.ts"],"names":[],"mappings":"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"}
@@ -3,4 +3,6 @@ export type ReportRenderOptions = {
3
3
  maxAnnotations?: number;
4
4
  };
5
5
  export declare function generateAndWriteReport(outputPath: string, coverageObj: AnyCoverageObject, format: string, formatAmount: number, options?: ReportRenderOptions): Promise<string>;
6
+ export declare function getReportPath(outputPath: string, format: string, formatAmount: number): string;
7
+ export declare function generateReportContent(coverageObj: AnyCoverageObject, format: string, options?: ReportRenderOptions): string;
6
8
  export declare function getExtensionForFormat(format: string): string;
@@ -29,25 +29,24 @@ const STRING_FORMAT_RENDERERS = {
29
29
  };
30
30
  export async function generateAndWriteReport(outputPath, coverageObj, format, formatAmount, options) {
31
31
  const content = generateReportContent(coverageObj, format, options);
32
+ const filePath = getReportPath(outputPath, format, formatAmount);
33
+ await writeFile(filePath, content);
34
+ return filePath;
35
+ }
36
+ export function getReportPath(outputPath, format, formatAmount) {
32
37
  const extension = HandlerRegistry.getExtension(format);
33
38
  const base = basename(outputPath, extname(outputPath)); // e.g., 'coverage'
34
39
  const dir = dirname(outputPath);
35
40
  const suffix = formatAmount > 1 ? `-${format}` : '';
36
- const filePath = join(dir, `${base}${suffix}${extension}`);
37
- await writeFile(filePath, content, 'utf-8');
38
- return filePath;
41
+ return join(dir, `${base}${suffix}${extension}`);
39
42
  }
40
- function generateReportContent(coverageObj, format, options) {
43
+ export function generateReportContent(coverageObj, format, options) {
41
44
  const renderer = STRING_FORMAT_RENDERERS[format];
42
45
  return renderer ? renderer(coverageObj, options) : generateXmlContent(coverageObj, format);
43
46
  }
44
47
  function generateXmlContent(coverageObj, format) {
45
- const isHeadless = isXmlReportFormat(format);
46
- const builder = new XmlBuilder(builderOptions);
47
- let xml = builder.build(coverageObj);
48
- if (isHeadless) {
49
- xml = xml.replace(/^<\?xml[^>]*>\s*/i, '');
50
- }
48
+ // XmlBuilder never emits an XML declaration, so the format's header is simply prepended.
49
+ const xml = new XmlBuilder(builderOptions).build(coverageObj);
51
50
  return prependXmlHeader(xml, format).trimEnd();
52
51
  }
53
52
  function generateLcov(coverageObj) {
@@ -70,10 +69,8 @@ function generateLcov(coverageObj) {
70
69
  .join('\n');
71
70
  }
72
71
  function prependXmlHeader(xml, format) {
73
- // Remove any existing XML declaration to avoid duplicates
74
- const stripped = xml.replace(/^<\?xml[^>]*>\s*/i, '');
75
72
  const config = isXmlReportFormat(format) ? XML_HEADER_CONFIG[format] : undefined;
76
- return [config?.xmlDecl ?? '<?xml version="1.0"?>', config?.doctype, stripped].filter(Boolean).join('\n');
73
+ return [config?.xmlDecl ?? '<?xml version="1.0"?>', config?.doctype, xml].filter(Boolean).join('\n');
77
74
  }
78
75
  export function getExtensionForFormat(format) {
79
76
  return HandlerRegistry.getExtension(format);
@@ -1 +1 @@
1
- {"version":3,"file":"reportGenerator.js","sourceRoot":"","sources":["../../src/transformers/reportGenerator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"reportGenerator.js","sourceRoot":"","sources":["../../src/transformers/reportGenerator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export type CoverageErrorCode = 'UNKNOWN_INPUT_FORMAT' | 'MIXED_INPUT_TYPES' | 'UNSUPPORTED_FORMAT' | 'NO_SFDX_PROJECT';
2
+ /**
3
+ * Error thrown by the transformer for known failure cases.
4
+ * Check `code` instead of matching on `message`.
5
+ */
6
+ export declare class CoverageError extends Error {
7
+ readonly code: CoverageErrorCode;
8
+ constructor(message: string, options: {
9
+ code: CoverageErrorCode;
10
+ cause?: unknown;
11
+ });
12
+ }
@@ -0,0 +1,14 @@
1
+ 'use strict';
2
+ /**
3
+ * Error thrown by the transformer for known failure cases.
4
+ * Check `code` instead of matching on `message`.
5
+ */
6
+ export class CoverageError extends Error {
7
+ code;
8
+ constructor(message, options) {
9
+ super(message, { cause: options.cause });
10
+ this.name = 'CoverageError';
11
+ this.code = options.code;
12
+ }
13
+ }
14
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAIb;;;GAGG;AACH,MAAM,OAAO,aAAc,SAAQ,KAAK;IACtB,IAAI,CAAoB;IAExC,YAAmB,OAAe,EAAE,OAAqD;QACvF,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;QACzC,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAC3B,CAAC;CACF"}
@@ -1,4 +1,4 @@
1
- export declare function getPackageDirectories(ignoreDirectories: string[]): Promise<{
1
+ export declare function getPackageDirectories(ignoreDirectories: string[], cwd?: string): Promise<{
2
2
  repoRoot: string;
3
3
  packageDirectories: string[];
4
4
  }>;
@@ -2,8 +2,8 @@
2
2
  import { readFile } from 'node:fs/promises';
3
3
  import { resolve } from 'node:path';
4
4
  import { getRepoRoot } from './getRepoRoot.js';
5
- export async function getPackageDirectories(ignoreDirectories) {
6
- const { repoRoot, dxConfigFilePath } = (await getRepoRoot());
5
+ export async function getPackageDirectories(ignoreDirectories, cwd) {
6
+ const { repoRoot, dxConfigFilePath } = (await getRepoRoot(cwd));
7
7
  const sfdxProjectRaw = await readFile(dxConfigFilePath, 'utf-8');
8
8
  const sfdxProject = JSON.parse(sfdxProjectRaw);
9
9
  const packageDirectories = sfdxProject.packageDirectories
@@ -1 +1 @@
1
- {"version":3,"file":"getPackageDirectories.js","sourceRoot":"","sources":["../../src/utils/getPackageDirectories.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG/C,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,iBAA2B;IAE3B,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,GAAG,CAAC,MAAM,WAAW,EAAE,CAG1D,CAAC;IAEF,MAAM,cAAc,GAAW,MAAM,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;IACzE,MAAM,WAAW,GAAgB,IAAI,CAAC,KAAK,CAAC,cAAc,CAAgB,CAAC;IAE3E,MAAM,kBAAkB,GAAG,WAAW,CAAC,kBAAkB;SACtD,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,iBAAiB,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,4BAA4B;SAC/F,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IAEzD,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,CAAC;AAC1C,CAAC"}
1
+ {"version":3,"file":"getPackageDirectories.js","sourceRoot":"","sources":["../../src/utils/getPackageDirectories.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG/C,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,iBAA2B,EAC3B,GAAY;IAEZ,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,GAAG,CAAC,MAAM,WAAW,CAAC,GAAG,CAAC,CAG7D,CAAC;IAEF,MAAM,cAAc,GAAW,MAAM,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;IACzE,MAAM,WAAW,GAAgB,IAAI,CAAC,KAAK,CAAC,cAAc,CAAgB,CAAC;IAE3E,MAAM,kBAAkB,GAAG,WAAW,CAAC,kBAAkB;SACtD,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,iBAAiB,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,4BAA4B;SAC/F,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IAEzD,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,CAAC;AAC1C,CAAC"}
@@ -1,4 +1,4 @@
1
- export declare function getRepoRoot(): Promise<{
1
+ export declare function getRepoRoot(cwd?: string): Promise<{
2
2
  repoRoot: string | undefined;
3
3
  dxConfigFilePath: string | undefined;
4
4
  }>;
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
  import { access } from 'node:fs/promises';
3
- import { dirname, join } from 'node:path';
3
+ import { dirname, join, resolve } from 'node:path';
4
+ import { CoverageError } from './errors.js';
4
5
  async function findRepoRoot(dir) {
5
6
  const filePath = join(dir, 'sfdx-project.json');
6
7
  try {
@@ -12,14 +13,16 @@ async function findRepoRoot(dir) {
12
13
  const parentDir = dirname(dir);
13
14
  if (dir === parentDir) {
14
15
  // Reached the root without finding the file, throw an error
15
- throw new Error('sfdx-project.json not found in any parent directory.', { cause: error });
16
+ throw new CoverageError('sfdx-project.json not found in any parent directory.', {
17
+ code: 'NO_SFDX_PROJECT',
18
+ cause: error,
19
+ });
16
20
  }
17
21
  // Recursively search in the parent directory
18
22
  return findRepoRoot(parentDir);
19
23
  }
20
24
  }
21
- export async function getRepoRoot() {
22
- const currentDir = process.cwd();
23
- return findRepoRoot(currentDir);
25
+ export async function getRepoRoot(cwd) {
26
+ return findRepoRoot(cwd === undefined ? process.cwd() : resolve(cwd));
24
27
  }
25
28
  //# sourceMappingURL=getRepoRoot.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"getRepoRoot.js","sourceRoot":"","sources":["../../src/utils/getRepoRoot.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AACb,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,KAAK,UAAU,YAAY,CAAC,GAAW;IACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,mBAAmB,CAAC,CAAC;IAChD,IAAI,CAAC;QACH,6DAA6D;QAC7D,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;QACvB,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,gBAAgB,EAAE,QAAQ,EAAE,CAAC;IACvD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,4DAA4D;YAC5D,MAAM,IAAI,KAAK,CAAC,sDAAsD,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5F,CAAC;QACD,6CAA6C;QAC7C,OAAO,YAAY,CAAC,SAAS,CAAC,CAAC;IACjC,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IACjC,OAAO,YAAY,CAAC,UAAU,CAAC,CAAC;AAClC,CAAC"}
1
+ {"version":3,"file":"getRepoRoot.js","sourceRoot":"","sources":["../../src/utils/getRepoRoot.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AACb,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,KAAK,UAAU,YAAY,CAAC,GAAW;IACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,mBAAmB,CAAC,CAAC;IAChD,IAAI,CAAC;QACH,6DAA6D;QAC7D,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;QACvB,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,gBAAgB,EAAE,QAAQ,EAAE,CAAC;IACvD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,4DAA4D;YAC5D,MAAM,IAAI,aAAa,CAAC,sDAAsD,EAAE;gBAC9E,IAAI,EAAE,iBAAiB;gBACvB,KAAK,EAAE,KAAK;aACb,CAAC,CAAC;QACL,CAAC;QACD,6CAA6C;QAC7C,OAAO,YAAY,CAAC,SAAS,CAAC,CAAC;IACjC,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,GAAY;IAEZ,OAAO,YAAY,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;AACxE,CAAC"}
@@ -49,14 +49,80 @@ export type LineTotals = {
49
49
  totalLines: number;
50
50
  coveredLines: number;
51
51
  };
52
+ /** Per-file coverage totals returned by the programmatic API. */
53
+ export type FileCoverageSummary = {
54
+ /** Path relative to the repo root, using forward slashes. */
55
+ path: string;
56
+ /** Apex class or trigger name as reported by Salesforce. */
57
+ name: string;
58
+ totalLines: number;
59
+ coveredLines: number;
60
+ /** Covered lines divided by total lines (0-1). 0 when the file has no coverable lines. */
61
+ lineRate: number;
62
+ /** Line numbers with no coverage, ascending. */
63
+ uncoveredLines: number[];
64
+ };
52
65
  export type ProcessResult = {
53
66
  processed: number;
67
+ files: FileCoverageSummary[];
54
68
  } & LineTotals;
55
69
  export type TransformOptions = {
56
70
  minCoverage?: number;
57
71
  maxAnnotations?: number;
58
72
  excludePatterns?: string[];
59
73
  };
74
+ /** Output formats supported by the transformer. */
75
+ export type CoverageFormat = 'clover' | 'cobertura' | 'github-actions' | 'html' | 'jacoco' | 'json' | 'json-summary' | 'lcovonly' | 'markdown' | 'opencover' | 'simplecov' | 'sonar';
76
+ /** Options for {@link transformCoverage}. */
77
+ export type TransformCoverageOptions = {
78
+ /**
79
+ * Coverage inputs: paths to Salesforce CLI coverage JSON files and/or already-parsed JSON.
80
+ * All inputs must be the same type (deploy or test). Covered lines are unioned across inputs.
81
+ */
82
+ inputs: Array<string | CoverageInput>;
83
+ /** Output formats. Defaults to `['sonar']`. */
84
+ formats?: CoverageFormat[];
85
+ /**
86
+ * Base path for written reports. Defaults to `coverage.xml`. With multiple formats, the format
87
+ * name is appended (e.g. `coverage-sonar.xml`). Ignored when `write` is false.
88
+ */
89
+ outputReport?: string;
90
+ /** Write reports to disk. Defaults to true. When false, report contents are only returned. */
91
+ write?: boolean;
92
+ /** Directory to search upward from for `sfdx-project.json`. Defaults to `process.cwd()`. */
93
+ cwd?: string;
94
+ /** Package directory paths (as written in `sfdx-project.json`) to skip. */
95
+ ignorePackageDirectories?: string[];
96
+ /** Glob patterns matched against repo-relative file paths to exclude. */
97
+ excludePatterns?: string[];
98
+ /** Minimum overall line coverage percentage (0-100). Sets `passed` to false when not met. */
99
+ minCoverage?: number;
100
+ /** Maximum annotations for the `github-actions` format. */
101
+ maxAnnotations?: number;
102
+ };
103
+ export type GeneratedReport = {
104
+ format: CoverageFormat;
105
+ /** Path the report was written to. Undefined when `write` is false. */
106
+ path?: string;
107
+ content: string;
108
+ };
109
+ export type CoverageSummary = {
110
+ /** Overall covered lines divided by total lines (0-1). */
111
+ lineRate: number;
112
+ coveredLines: number;
113
+ totalLines: number;
114
+ fileCount: number;
115
+ /** Per-file totals, sorted by path. */
116
+ files: FileCoverageSummary[];
117
+ };
118
+ /** Result of {@link transformCoverage}. */
119
+ export type TransformCoverageResult = {
120
+ reports: GeneratedReport[];
121
+ summary: CoverageSummary;
122
+ warnings: string[];
123
+ /** False only when `minCoverage` is set and overall coverage is below it. */
124
+ passed: boolean;
125
+ };
60
126
  type SonarLine = {
61
127
  '@lineNumber': number;
62
128
  '@covered': boolean;
@@ -91,7 +91,6 @@
91
91
  },
92
92
  "max-annotations": {
93
93
  "name": "max-annotations",
94
- "required": false,
95
94
  "summary": "Maximum number of GitHub Actions ::warning annotations to emit when using --format github-actions. Defaults to 50. Annotations beyond this limit are summarised in a ::notice line.",
96
95
  "default": 50,
97
96
  "hasDynamicHelp": false,
@@ -131,5 +130,5 @@
131
130
  ]
132
131
  }
133
132
  },
134
- "version": "4.1.0"
133
+ "version": "4.3.0"
135
134
  }
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "apex-code-coverage-transformer",
3
3
  "description": "Transform Salesforce Apex code coverage JSON for SonarQube, SonarCloud, GitHub, GitLab, Azure, and Bitbucket",
4
- "version": "4.1.0",
4
+ "version": "4.3.0",
5
5
  "dependencies": {
6
- "@oclif/core": "5.1.10",
6
+ "@oclif/core": "5.1.11",
7
7
  "@salesforce/core": "9.4.4",
8
8
  "@salesforce/sf-plugins-core": "13.0.8"
9
9
  },
@@ -12,12 +12,12 @@
12
12
  "@biomejs/biome": "2.6.5",
13
13
  "@commitlint/cli": "21.2.4",
14
14
  "@commitlint/config-conventional": "21.2.4",
15
- "@salesforce/cli-plugins-testkit": "5.3.83",
15
+ "@salesforce/cli-plugins-testkit": "5.3.90",
16
16
  "@salesforce/dev-config": "4.3.3",
17
17
  "@stryker-mutator/core": "10.1.1",
18
18
  "@stryker-mutator/vitest-runner": "10.1.1",
19
19
  "@types/node": "26.6.6",
20
- "@vitest/coverage-v8": "5.0.5",
20
+ "@vitest/coverage-v8": "5.1.0",
21
21
  "esbuild": "0.28.2",
22
22
  "husky": "9.1.7",
23
23
  "knip": "6.44.1",
@@ -27,7 +27,7 @@
27
27
  "shx": "0.4.0",
28
28
  "tsx": "4.23.15",
29
29
  "typescript": "7.0.2",
30
- "vitest": "5.0.5",
30
+ "vitest": "5.1.0",
31
31
  "wireit": "0.15.0"
32
32
  },
33
33
  "engines": {
@@ -88,6 +88,7 @@
88
88
  "scripts": {
89
89
  "build": "wireit",
90
90
  "build:action": "wireit",
91
+ "build:core": "wireit",
91
92
  "clean": "wireit",
92
93
  "clean:build": "wireit",
93
94
  "clean:package-manager": "wireit",
@@ -145,7 +146,8 @@
145
146
  "dependencies": [
146
147
  "compile",
147
148
  "lint",
148
- "build:action"
149
+ "build:action",
150
+ "build:core"
149
151
  ]
150
152
  },
151
153
  "build:action": {
@@ -162,6 +164,25 @@
162
164
  ],
163
165
  "clean": false
164
166
  },
167
+ "build:core": {
168
+ "command": "tsc -p tsconfig.core.json && shx cp LICENSE.md packages/core/LICENSE.md && node scripts/verify-core-package.mjs",
169
+ "files": [
170
+ "src/**/*.ts",
171
+ "!src/commands/**",
172
+ "!src/hooks/**",
173
+ "!src/action/**",
174
+ "tsconfig.json",
175
+ "tsconfig.core.json",
176
+ "packages/core/package.json",
177
+ "LICENSE.md",
178
+ "scripts/verify-core-package.mjs"
179
+ ],
180
+ "output": [
181
+ "packages/core/lib/**",
182
+ "packages/core/LICENSE.md"
183
+ ],
184
+ "clean": true
185
+ },
165
186
  "compile": {
166
187
  "command": "tsc -p . --pretty --incremental",
167
188
  "files": [
@@ -324,7 +345,14 @@
324
345
  "output": []
325
346
  }
326
347
  },
327
- "exports": "./lib/index.js",
348
+ "exports": {
349
+ ".": {
350
+ "types": "./lib/index.d.ts",
351
+ "import": "./lib/index.js"
352
+ },
353
+ "./package.json": "./package.json"
354
+ },
355
+ "types": "./lib/index.d.ts",
328
356
  "type": "module",
329
357
  "author": "Matt Carvin",
330
358
  "repository": {