@code-pushup/coverage-plugin 0.55.0 → 0.57.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.
Files changed (40) hide show
  1. package/package.json +8 -7
  2. package/src/bin.js +3 -0
  3. package/src/bin.js.map +1 -0
  4. package/src/index.d.ts +3 -3
  5. package/src/index.js +4 -0
  6. package/src/index.js.map +1 -0
  7. package/src/lib/config.js +54 -0
  8. package/src/lib/config.js.map +1 -0
  9. package/src/lib/coverage-plugin.d.ts +1 -1
  10. package/src/lib/coverage-plugin.js +57 -0
  11. package/src/lib/coverage-plugin.js.map +1 -0
  12. package/src/lib/nx/coverage-paths.d.ts +1 -1
  13. package/src/lib/nx/coverage-paths.js +94 -0
  14. package/src/lib/nx/coverage-paths.js.map +1 -0
  15. package/src/lib/runner/constants.js +7 -0
  16. package/src/lib/runner/constants.js.map +1 -0
  17. package/src/lib/runner/index.d.ts +1 -1
  18. package/src/lib/runner/index.js +45 -0
  19. package/src/lib/runner/index.js.map +1 -0
  20. package/src/lib/runner/lcov/lcov-runner.d.ts +1 -1
  21. package/src/lib/runner/lcov/lcov-runner.js +90 -0
  22. package/src/lib/runner/lcov/lcov-runner.js.map +1 -0
  23. package/src/lib/runner/lcov/merge-lcov.js +103 -0
  24. package/src/lib/runner/lcov/merge-lcov.js.map +1 -0
  25. package/src/lib/runner/lcov/parse-lcov.js +5 -0
  26. package/src/lib/runner/lcov/parse-lcov.js.map +1 -0
  27. package/src/lib/runner/lcov/transform.d.ts +2 -2
  28. package/src/lib/runner/lcov/transform.js +111 -0
  29. package/src/lib/runner/lcov/transform.js.map +1 -0
  30. package/src/lib/runner/lcov/types.d.ts +1 -1
  31. package/src/lib/runner/lcov/types.js +2 -0
  32. package/src/lib/runner/lcov/types.js.map +1 -0
  33. package/src/lib/runner/lcov/utils.d.ts +1 -1
  34. package/src/lib/runner/lcov/utils.js +25 -0
  35. package/src/lib/runner/lcov/utils.js.map +1 -0
  36. package/src/lib/utils.d.ts +1 -1
  37. package/src/lib/utils.js +22 -0
  38. package/src/lib/utils.js.map +1 -0
  39. package/bin.js +0 -1236
  40. package/index.js +0 -1065
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-pushup/coverage-plugin",
3
- "version": "0.55.0",
3
+ "version": "0.57.0",
4
4
  "description": "Code PushUp plugin for tracking code coverage ☂",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/code-pushup/cli/tree/main/packages/plugin-coverage#readme",
@@ -33,11 +33,9 @@
33
33
  "access": "public"
34
34
  },
35
35
  "type": "module",
36
- "main": "./index.js",
37
- "types": "./src/index.d.ts",
38
36
  "dependencies": {
39
- "@code-pushup/models": "0.55.0",
40
- "@code-pushup/utils": "0.55.0",
37
+ "@code-pushup/models": "0.57.0",
38
+ "@code-pushup/utils": "0.57.0",
41
39
  "ansis": "^3.3.0",
42
40
  "parse-lcov": "^1.0.4",
43
41
  "zod": "^3.22.4"
@@ -57,5 +55,8 @@
57
55
  "@nx/vite": {
58
56
  "optional": true
59
57
  }
60
- }
61
- }
58
+ },
59
+ "module": "./src/index.js",
60
+ "main": "./src/index.js",
61
+ "types": "./src/index.d.ts"
62
+ }
package/src/bin.js ADDED
@@ -0,0 +1,3 @@
1
+ import { executeRunner } from './lib/runner/index.js';
2
+ await executeRunner();
3
+ //# sourceMappingURL=bin.js.map
package/src/bin.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../../../../packages/plugin-coverage/src/bin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD,MAAM,aAAa,EAAE,CAAC"}
package/src/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { coveragePlugin } from './lib/coverage-plugin';
1
+ import { coveragePlugin } from './lib/coverage-plugin.js';
2
2
  export default coveragePlugin;
3
- export type { CoveragePluginConfig } from './lib/config';
4
- export { getNxCoveragePaths } from './lib/nx/coverage-paths';
3
+ export type { CoveragePluginConfig } from './lib/config.js';
4
+ export { getNxCoveragePaths } from './lib/nx/coverage-paths.js';
package/src/index.js ADDED
@@ -0,0 +1,4 @@
1
+ import { coveragePlugin } from './lib/coverage-plugin.js';
2
+ export default coveragePlugin;
3
+ export { getNxCoveragePaths } from './lib/nx/coverage-paths.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/plugin-coverage/src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE1D,eAAe,cAAc,CAAC;AAE9B,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC"}
@@ -0,0 +1,54 @@
1
+ import { z } from 'zod';
2
+ export const coverageTypeSchema = z.enum(['function', 'branch', 'line']);
3
+ export const coverageResultSchema = z.union([
4
+ z.object({
5
+ resultsPath: z
6
+ .string({
7
+ description: 'Path to coverage results for Nx setup.',
8
+ })
9
+ .includes('lcov'),
10
+ pathToProject: z
11
+ .string({
12
+ description: 'Path from workspace root to project root. Necessary for LCOV reports which provide a relative path.',
13
+ })
14
+ .optional(),
15
+ }),
16
+ z
17
+ .string({
18
+ description: 'Path to coverage results for a single project setup.',
19
+ })
20
+ .includes('lcov'),
21
+ ]);
22
+ export const coveragePluginConfigSchema = z.object({
23
+ coverageToolCommand: z
24
+ .object({
25
+ command: z
26
+ .string({ description: 'Command to run coverage tool.' })
27
+ .min(1),
28
+ args: z
29
+ .array(z.string(), {
30
+ description: 'Arguments to be passed to the coverage tool.',
31
+ })
32
+ .optional(),
33
+ })
34
+ .optional(),
35
+ coverageTypes: z
36
+ .array(coverageTypeSchema, {
37
+ description: 'Coverage types measured. Defaults to all available types.',
38
+ })
39
+ .min(1)
40
+ .default(['function', 'branch', 'line']),
41
+ reports: z
42
+ .array(coverageResultSchema, {
43
+ description: 'Path to all code coverage report files. Only LCOV format is supported for now.',
44
+ })
45
+ .min(1),
46
+ perfectScoreThreshold: z
47
+ .number({
48
+ description: 'Score will be 1 (perfect) for this coverage and above. Score range is 0 - 1.',
49
+ })
50
+ .gt(0)
51
+ .max(1)
52
+ .optional(),
53
+ });
54
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../../../packages/plugin-coverage/src/lib/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;AAGzE,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC;IAC1C,CAAC,CAAC,MAAM,CAAC;QACP,WAAW,EAAE,CAAC;aACX,MAAM,CAAC;YACN,WAAW,EAAE,wCAAwC;SACtD,CAAC;aACD,QAAQ,CAAC,MAAM,CAAC;QACnB,aAAa,EAAE,CAAC;aACb,MAAM,CAAC;YACN,WAAW,EACT,qGAAqG;SACxG,CAAC;aACD,QAAQ,EAAE;KACd,CAAC;IACF,CAAC;SACE,MAAM,CAAC;QACN,WAAW,EAAE,sDAAsD;KACpE,CAAC;SACD,QAAQ,CAAC,MAAM,CAAC;CACpB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC,MAAM,CAAC;IACjD,mBAAmB,EAAE,CAAC;SACnB,MAAM,CAAC;QACN,OAAO,EAAE,CAAC;aACP,MAAM,CAAC,EAAE,WAAW,EAAE,+BAA+B,EAAE,CAAC;aACxD,GAAG,CAAC,CAAC,CAAC;QACT,IAAI,EAAE,CAAC;aACJ,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE;YACjB,WAAW,EAAE,8CAA8C;SAC5D,CAAC;aACD,QAAQ,EAAE;KACd,CAAC;SACD,QAAQ,EAAE;IACb,aAAa,EAAE,CAAC;SACb,KAAK,CAAC,kBAAkB,EAAE;QACzB,WAAW,EAAE,2DAA2D;KACzE,CAAC;SACD,GAAG,CAAC,CAAC,CAAC;SACN,OAAO,CAAC,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC1C,OAAO,EAAE,CAAC;SACP,KAAK,CAAC,oBAAoB,EAAE;QAC3B,WAAW,EACT,gFAAgF;KACnF,CAAC;SACD,GAAG,CAAC,CAAC,CAAC;IACT,qBAAqB,EAAE,CAAC;SACrB,MAAM,CAAC;QACN,WAAW,EACT,8EAA8E;KACjF,CAAC;SACD,EAAE,CAAC,CAAC,CAAC;SACL,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,EAAE;CACd,CAAC,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import type { PluginConfig } from '@code-pushup/models';
2
- import { type CoveragePluginConfig } from './config';
2
+ import { type CoveragePluginConfig } from './config.js';
3
3
  /**
4
4
  * Instantiates Code PushUp code coverage plugin for core config.
5
5
  *
@@ -0,0 +1,57 @@
1
+ import { createRequire } from 'node:module';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { capitalize } from '@code-pushup/utils';
5
+ import { coveragePluginConfigSchema, } from './config.js';
6
+ import { createRunnerConfig } from './runner/index.js';
7
+ import { coverageDescription, coverageTypeWeightMapper } from './utils.js';
8
+ /**
9
+ * Instantiates Code PushUp code coverage plugin for core config.
10
+ *
11
+ * @example
12
+ * import coveragePlugin from '@code-pushup/coverage-plugin'
13
+ *
14
+ * export default {
15
+ * // ... core config ...
16
+ * plugins: [
17
+ * // ... other plugins ...
18
+ * await coveragePlugin({
19
+ * reports: [{ resultsPath: 'coverage/cli/lcov.info', pathToProject: 'packages/cli' }]
20
+ * })
21
+ * ]
22
+ * }
23
+ *
24
+ * @returns Plugin configuration.
25
+ */
26
+ export async function coveragePlugin(config) {
27
+ const coverageConfig = coveragePluginConfigSchema.parse(config);
28
+ const audits = coverageConfig.coverageTypes.map((type) => ({
29
+ slug: `${type}-coverage`,
30
+ title: `${capitalize(type)} coverage`,
31
+ description: coverageDescription[type],
32
+ }));
33
+ const group = {
34
+ slug: 'coverage',
35
+ title: 'Code coverage metrics',
36
+ description: 'Group containing all defined coverage types as audits.',
37
+ refs: audits.map(audit => ({
38
+ ...audit,
39
+ weight: coverageTypeWeightMapper[audit.slug.slice(0, audit.slug.indexOf('-'))],
40
+ })),
41
+ };
42
+ const runnerScriptPath = path.join(fileURLToPath(path.dirname(import.meta.url)), '..', 'bin.js');
43
+ const packageJson = createRequire(import.meta.url)('../../package.json');
44
+ return {
45
+ slug: 'coverage',
46
+ title: 'Code coverage',
47
+ icon: 'folder-coverage-open',
48
+ description: 'Official Code PushUp code coverage plugin.',
49
+ docsUrl: 'https://www.npmjs.com/package/@code-pushup/coverage-plugin/',
50
+ packageName: packageJson.name,
51
+ version: packageJson.version,
52
+ audits,
53
+ groups: [group],
54
+ runner: await createRunnerConfig(runnerScriptPath, coverageConfig),
55
+ };
56
+ }
57
+ //# sourceMappingURL=coverage-plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coverage-plugin.js","sourceRoot":"","sources":["../../../../../packages/plugin-coverage/src/lib/coverage-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAGL,0BAA0B,GAC3B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAE3E;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,MAA4B;IAE5B,MAAM,cAAc,GAAG,0BAA0B,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAEhE,MAAM,MAAM,GAAG,cAAc,CAAC,aAAa,CAAC,GAAG,CAC7C,CAAC,IAAI,EAAS,EAAE,CAAC,CAAC;QAChB,IAAI,EAAE,GAAG,IAAI,WAAW;QACxB,KAAK,EAAE,GAAG,UAAU,CAAC,IAAI,CAAC,WAAW;QACrC,WAAW,EAAE,mBAAmB,CAAC,IAAI,CAAC;KACvC,CAAC,CACH,CAAC;IAEF,MAAM,KAAK,GAAU;QACnB,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,uBAAuB;QAC9B,WAAW,EAAE,wDAAwD;QACrE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACzB,GAAG,KAAK;YACR,MAAM,EACJ,wBAAwB,CACtB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAiB,CAC7D;SACJ,CAAC,CAAC;KACJ,CAAC;IAEF,MAAM,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAChC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAC5C,IAAI,EACJ,QAAQ,CACT,CAAC;IAEF,MAAM,WAAW,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAChD,oBAAoB,CACkB,CAAC;IAEzC,OAAO;QACL,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,eAAe;QACtB,IAAI,EAAE,sBAAsB;QAC5B,WAAW,EAAE,4CAA4C;QACzD,OAAO,EAAE,6DAA6D;QACtE,WAAW,EAAE,WAAW,CAAC,IAAI;QAC7B,OAAO,EAAE,WAAW,CAAC,OAAO;QAC5B,MAAM;QACN,MAAM,EAAE,CAAC,KAAK,CAAC;QACf,MAAM,EAAE,MAAM,kBAAkB,CAAC,gBAAgB,EAAE,cAAc,CAAC;KACnE,CAAC;AACJ,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import type { ProjectConfiguration } from '@nx/devkit';
2
2
  import type { JestExecutorOptions } from '@nx/jest/src/executors/jest/schema';
3
3
  import type { VitestExecutorOptions } from '@nx/vite/executors';
4
- import type { CoverageResult } from '../config';
4
+ import type { CoverageResult } from '../config.js';
5
5
  /**
6
6
  * @param targets nx targets to be used for measuring coverage, test by default
7
7
  * @returns An array of coverage result information for the coverage plugin.
@@ -0,0 +1,94 @@
1
+ import { bold } from 'ansis';
2
+ import path from 'node:path';
3
+ import { importModule, ui } from '@code-pushup/utils';
4
+ /**
5
+ * @param targets nx targets to be used for measuring coverage, test by default
6
+ * @returns An array of coverage result information for the coverage plugin.
7
+ */
8
+ export async function getNxCoveragePaths(targets = ['test'], verbose) {
9
+ if (verbose) {
10
+ ui().logger.info(bold('💡 Gathering coverage from the following nx projects:'));
11
+ }
12
+ const { createProjectGraphAsync } = await import('@nx/devkit');
13
+ const { nodes } = await createProjectGraphAsync({ exitOnError: false });
14
+ const coverageResults = await Promise.all(targets.map(async (target) => {
15
+ const relevantNodes = Object.values(nodes).filter(graph => hasNxTarget(graph, target));
16
+ return await Promise.all(relevantNodes.map(async ({ name, data }) => {
17
+ const coveragePaths = await getCoveragePathsForTarget(data, target);
18
+ if (verbose) {
19
+ ui().logger.info(`- ${name}: ${target}`);
20
+ }
21
+ return coveragePaths;
22
+ }));
23
+ }));
24
+ if (verbose) {
25
+ ui().logger.info('\n');
26
+ }
27
+ return coverageResults.flat();
28
+ }
29
+ function hasNxTarget(project, target) {
30
+ return project.data.targets != null && target in project.data.targets;
31
+ }
32
+ export async function getCoveragePathsForTarget(project, target) {
33
+ const targetConfig = project.targets?.[target];
34
+ if (!targetConfig) {
35
+ throw new Error(`No configuration found for target ${target} in project ${project.name}`);
36
+ }
37
+ if (targetConfig.executor?.includes('@nx/vite')) {
38
+ return getCoveragePathForVitest(targetConfig.options, project, target);
39
+ }
40
+ if (targetConfig.executor?.includes('@nx/jest')) {
41
+ return getCoveragePathForJest(targetConfig.options, project, target);
42
+ }
43
+ throw new Error(`Unsupported executor ${targetConfig.executor}. Only @nx/vite and @nx/jest are currently supported.`);
44
+ }
45
+ export async function getCoveragePathForVitest(options, project, target) {
46
+ const { default: { normalizeViteConfigFilePathWithTree }, } = await import('@nx/vite');
47
+ const config = normalizeViteConfigFilePathWithTree(
48
+ // HACK: only tree.exists is called, so injecting existSync from node:fs instead
49
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
50
+ { exists: (await import('node:fs')).existsSync }, project.root, options.configFile);
51
+ if (!config) {
52
+ throw new Error(`Could not find Vitest config file for target ${target} in project ${project.name}`);
53
+ }
54
+ const vitestConfig = await importModule({
55
+ filepath: config,
56
+ format: 'esm',
57
+ });
58
+ const reportsDirectory = options.reportsDirectory ?? vitestConfig.test.coverage?.reportsDirectory;
59
+ const reporter = vitestConfig.test.coverage?.reporter;
60
+ if (reportsDirectory == null) {
61
+ throw new Error(`Vitest coverage configuration at ${config} does not include coverage path for target ${target} in project ${project.name}. Add the path under coverage > reportsDirectory.`);
62
+ }
63
+ if (!reporter?.includes('lcov')) {
64
+ throw new Error(`Vitest coverage configuration at ${config} does not include LCOV report format for target ${target} in project ${project.name}. Add 'lcov' format under coverage > reporter.`);
65
+ }
66
+ if (path.isAbsolute(reportsDirectory)) {
67
+ return path.join(reportsDirectory, 'lcov.info');
68
+ }
69
+ return {
70
+ pathToProject: project.root,
71
+ resultsPath: path.join(project.root, reportsDirectory, 'lcov.info'),
72
+ };
73
+ }
74
+ export async function getCoveragePathForJest(options, project, target) {
75
+ const { jestConfig } = options;
76
+ const testConfig = await importModule({
77
+ filepath: jestConfig,
78
+ });
79
+ const { coverageDirectory, coverageReporters } = {
80
+ ...testConfig,
81
+ ...options,
82
+ };
83
+ if (coverageDirectory == null) {
84
+ throw new Error(`Jest coverage configuration at ${jestConfig} does not include coverage path for target ${target} in ${project.name}. Add the path under coverageDirectory.`);
85
+ }
86
+ if (!coverageReporters?.includes('lcov') && !('preset' in testConfig)) {
87
+ throw new Error(`Jest coverage configuration at ${jestConfig} does not include LCOV report format for target ${target} in ${project.name}. Add 'lcov' format under coverageReporters.`);
88
+ }
89
+ if (path.isAbsolute(coverageDirectory)) {
90
+ return path.join(coverageDirectory, 'lcov.info');
91
+ }
92
+ return path.join(project.root, coverageDirectory, 'lcov.info');
93
+ }
94
+ //# sourceMappingURL=coverage-paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coverage-paths.js","sourceRoot":"","sources":["../../../../../../packages/plugin-coverage/src/lib/nx/coverage-paths.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC;AAC7B,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,oBAAoB,CAAC;AAGtD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,UAAoB,CAAC,MAAM,CAAC,EAC5B,OAAiB;IAEjB,IAAI,OAAO,EAAE,CAAC;QACZ,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CACd,IAAI,CAAC,uDAAuD,CAAC,CAC9D,CAAC;IACJ,CAAC;IAED,MAAM,EAAE,uBAAuB,EAAE,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;IAC/D,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,uBAAuB,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;IAExE,MAAM,eAAe,GAAG,MAAM,OAAO,CAAC,GAAG,CACvC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAC,MAAM,EAAC,EAAE;QACzB,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CACxD,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,CAC3B,CAAC;QAEF,OAAO,MAAM,OAAO,CAAC,GAAG,CACtB,aAAa,CAAC,GAAG,CAA0B,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE;YAClE,MAAM,aAAa,GAAG,MAAM,yBAAyB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YACpE,IAAI,OAAO,EAAE,CAAC;gBACZ,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,IAAI,KAAK,MAAM,EAAE,CAAC,CAAC;YAC3C,CAAC;YACD,OAAO,aAAa,CAAC;QACvB,CAAC,CAAC,CACH,CAAC;IACJ,CAAC,CAAC,CACH,CAAC;IAEF,IAAI,OAAO,EAAE,CAAC;QACZ,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;IAED,OAAO,eAAe,CAAC,IAAI,EAAE,CAAC;AAChC,CAAC;AAED,SAAS,WAAW,CAClB,OAAgC,EAChC,MAAc;IAEd,OAAO,OAAO,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,IAAI,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;AACxE,CAAC;AAgBD,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,OAA6B,EAC7B,MAAc;IAEd,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC;IAE/C,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CACb,qCAAqC,MAAM,eAAe,OAAO,CAAC,IAAI,EAAE,CACzE,CAAC;IACJ,CAAC;IAED,IAAI,YAAY,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QAChD,OAAO,wBAAwB,CAC7B,YAAY,CAAC,OAAgC,EAC7C,OAAO,EACP,MAAM,CACP,CAAC;IACJ,CAAC;IAED,IAAI,YAAY,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QAChD,OAAO,sBAAsB,CAC3B,YAAY,CAAC,OAA8B,EAC3C,OAAO,EACP,MAAM,CACP,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,KAAK,CACb,wBAAwB,YAAY,CAAC,QAAQ,uDAAuD,CACrG,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,OAA8B,EAC9B,OAA6B,EAC7B,MAAc;IAEd,MAAM,EACJ,OAAO,EAAE,EAAE,mCAAmC,EAAE,GACjD,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;IAC7B,MAAM,MAAM,GAAG,mCAAmC;IAChD,gFAAgF;IAChF,yEAAyE;IACzE,EAAE,MAAM,EAAE,CAAC,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,EAAU,EACxD,OAAO,CAAC,IAAI,EACZ,OAAO,CAAC,UAAU,CACnB,CAAC;IACF,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,gDAAgD,MAAM,eAAe,OAAO,CAAC,IAAI,EAAE,CACpF,CAAC;IACJ,CAAC;IAED,MAAM,YAAY,GAAG,MAAM,YAAY,CAAuB;QAC5D,QAAQ,EAAE,MAAM;QAChB,MAAM,EAAE,KAAK;KACd,CAAC,CAAC;IAEH,MAAM,gBAAgB,GACpB,OAAO,CAAC,gBAAgB,IAAI,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,gBAAgB,CAAC;IAC3E,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAEtD,IAAI,gBAAgB,IAAI,IAAI,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CACb,oCAAoC,MAAM,8CAA8C,MAAM,eAAe,OAAO,CAAC,IAAI,mDAAmD,CAC7K,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CACb,oCAAoC,MAAM,mDAAmD,MAAM,eAAe,OAAO,CAAC,IAAI,gDAAgD,CAC/K,CAAC;IACJ,CAAC;IAED,IAAI,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACtC,OAAO,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,WAAW,CAAC,CAAC;IAClD,CAAC;IACD,OAAO;QACL,aAAa,EAAE,OAAO,CAAC,IAAI;QAC3B,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,gBAAgB,EAAE,WAAW,CAAC;KACpE,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,OAA4B,EAC5B,OAA6B,EAC7B,MAAc;IAEd,MAAM,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;IAE/B,MAAM,UAAU,GAAG,MAAM,YAAY,CAAqB;QACxD,QAAQ,EAAE,UAAU;KACrB,CAAC,CAAC;IACH,MAAM,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,GAAG;QAC/C,GAAG,UAAU;QACb,GAAG,OAAO;KACX,CAAC;IAEF,IAAI,iBAAiB,IAAI,IAAI,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CACb,kCAAkC,UAAU,8CAA8C,MAAM,OAAO,OAAO,CAAC,IAAI,yCAAyC,CAC7J,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,IAAI,UAAU,CAAC,EAAE,CAAC;QACtE,MAAM,IAAI,KAAK,CACb,kCAAkC,UAAU,mDAAmD,MAAM,OAAO,OAAO,CAAC,IAAI,8CAA8C,CACvK,CAAC;IACJ,CAAC;IAED,IAAI,IAAI,CAAC,UAAU,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACvC,OAAO,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,WAAW,CAAC,CAAC;IACnD,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,iBAAiB,EAAE,WAAW,CAAC,CAAC;AACjE,CAAC"}
@@ -0,0 +1,7 @@
1
+ import path from 'node:path';
2
+ import { pluginWorkDir } from '@code-pushup/utils';
3
+ export const WORKDIR = pluginWorkDir('coverage');
4
+ export const RUNNER_OUTPUT_PATH = path.join(WORKDIR, 'runner-output.json');
5
+ export const PLUGIN_CONFIG_PATH = path.join(process.cwd(), WORKDIR, 'plugin-config.json');
6
+ export const INVALID_FUNCTION_NAME = '(empty-report)';
7
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../../../../packages/plugin-coverage/src/lib/runner/constants.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,MAAM,CAAC,MAAM,OAAO,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;AACjD,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,oBAAoB,CAAC,CAAC;AAC3E,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC,IAAI,CACzC,OAAO,CAAC,GAAG,EAAE,EACb,OAAO,EACP,oBAAoB,CACrB,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAG,gBAAgB,CAAC"}
@@ -1,4 +1,4 @@
1
1
  import type { RunnerConfig } from '@code-pushup/models';
2
- import type { FinalCoveragePluginConfig } from '../config';
2
+ import type { FinalCoveragePluginConfig } from '../config.js';
3
3
  export declare function executeRunner(): Promise<void>;
4
4
  export declare function createRunnerConfig(scriptPath: string, config: FinalCoveragePluginConfig): Promise<RunnerConfig>;
@@ -0,0 +1,45 @@
1
+ import { bold } from 'ansis';
2
+ import { writeFile } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { ProcessError, ensureDirectoryExists, executeProcess, filePathToCliArg, readJsonFile, ui, } from '@code-pushup/utils';
5
+ import { applyMaxScoreAboveThreshold } from '../utils.js';
6
+ import { PLUGIN_CONFIG_PATH, RUNNER_OUTPUT_PATH } from './constants.js';
7
+ import { lcovResultsToAuditOutputs } from './lcov/lcov-runner.js';
8
+ export async function executeRunner() {
9
+ const { reports, coverageToolCommand, coverageTypes } = await readJsonFile(PLUGIN_CONFIG_PATH);
10
+ // Run coverage tool if provided
11
+ if (coverageToolCommand != null) {
12
+ const { command, args } = coverageToolCommand;
13
+ try {
14
+ await executeProcess({ command, args });
15
+ }
16
+ catch (error) {
17
+ if (error instanceof ProcessError) {
18
+ ui().logger.error(bold('stdout from failed coverage tool process:'));
19
+ ui().logger.error(error.stdout);
20
+ ui().logger.error(bold('stderr from failed coverage tool process:'));
21
+ ui().logger.error(error.stderr);
22
+ }
23
+ throw new Error('Coverage plugin: Running coverage tool failed. Make sure all your provided tests are passing.');
24
+ }
25
+ }
26
+ // Calculate coverage from LCOV results
27
+ const auditOutputs = await lcovResultsToAuditOutputs(reports, coverageTypes);
28
+ await ensureDirectoryExists(path.dirname(RUNNER_OUTPUT_PATH));
29
+ await writeFile(RUNNER_OUTPUT_PATH, JSON.stringify(auditOutputs));
30
+ }
31
+ export async function createRunnerConfig(scriptPath, config) {
32
+ // Create JSON config for executeRunner
33
+ await ensureDirectoryExists(path.dirname(PLUGIN_CONFIG_PATH));
34
+ await writeFile(PLUGIN_CONFIG_PATH, JSON.stringify(config));
35
+ const threshold = config.perfectScoreThreshold;
36
+ return {
37
+ command: 'node',
38
+ args: [filePathToCliArg(scriptPath)],
39
+ outputFile: RUNNER_OUTPUT_PATH,
40
+ ...(threshold != null && {
41
+ outputTransform: outputs => applyMaxScoreAboveThreshold(outputs, threshold),
42
+ }),
43
+ };
44
+ }
45
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../packages/plugin-coverage/src/lib/runner/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EACL,YAAY,EACZ,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,EAAE,GACH,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAElE,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,MAAM,EAAE,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,GACnD,MAAM,YAAY,CAA4B,kBAAkB,CAAC,CAAC;IAEpE,gCAAgC;IAChC,IAAI,mBAAmB,IAAI,IAAI,EAAE,CAAC;QAChC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,mBAAmB,CAAC;QAC9C,IAAI,CAAC;YACH,MAAM,cAAc,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,YAAY,EAAE,CAAC;gBAClC,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAC,CAAC;gBACrE,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;gBAChC,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAC,CAAC;gBACrE,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAClC,CAAC;YAED,MAAM,IAAI,KAAK,CACb,+FAA+F,CAChG,CAAC;QACJ,CAAC;IACH,CAAC;IAED,uCAAuC;IACvC,MAAM,YAAY,GAAG,MAAM,yBAAyB,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IAE7E,MAAM,qBAAqB,CAAC,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC;IAC9D,MAAM,SAAS,CAAC,kBAAkB,EAAE,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC;AACpE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,UAAkB,EAClB,MAAiC;IAEjC,uCAAuC;IACvC,MAAM,qBAAqB,CAAC,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC;IAC9D,MAAM,SAAS,CAAC,kBAAkB,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IAE5D,MAAM,SAAS,GAAG,MAAM,CAAC,qBAAqB,CAAC;IAE/C,OAAO;QACL,OAAO,EAAE,MAAM;QACf,IAAI,EAAE,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC;QACpC,UAAU,EAAE,kBAAkB;QAC9B,GAAG,CAAC,SAAS,IAAI,IAAI,IAAI;YACvB,eAAe,EAAE,OAAO,CAAC,EAAE,CACzB,2BAA2B,CAAC,OAAuB,EAAE,SAAS,CAAC;SAClE,CAAC;KACH,CAAC;AACJ,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import type { LCOVRecord } from 'parse-lcov';
2
2
  import type { AuditOutputs } from '@code-pushup/models';
3
- import type { CoverageResult, CoverageType } from '../../config';
3
+ import type { CoverageResult, CoverageType } from '../../config.js';
4
4
  /**
5
5
  *
6
6
  * @param results Paths to LCOV results
@@ -0,0 +1,90 @@
1
+ import path from 'node:path';
2
+ import { exists, readTextFile, toUnixNewlines, ui } from '@code-pushup/utils';
3
+ import { mergeLcovResults } from './merge-lcov.js';
4
+ import { parseLcov } from './parse-lcov.js';
5
+ import { lcovCoverageToAuditOutput, recordToStatFunctionMapper, } from './transform.js';
6
+ // Note: condition or statement coverage is not supported in LCOV
7
+ // https://stackoverflow.com/questions/48260434/is-it-possible-to-check-condition-coverage-with-gcov
8
+ /**
9
+ *
10
+ * @param results Paths to LCOV results
11
+ * @param coverageTypes types of coverage to be considered
12
+ * @returns Audit outputs with complete coverage data.
13
+ */
14
+ export async function lcovResultsToAuditOutputs(results, coverageTypes) {
15
+ // Parse lcov files
16
+ const lcovResults = await parseLcovFiles(results);
17
+ // Merge multiple coverage reports for the same file
18
+ const mergedResults = mergeLcovResults(lcovResults);
19
+ // Calculate code coverage from all coverage results
20
+ const totalCoverageStats = getTotalCoverageFromLcovRecords(mergedResults, coverageTypes);
21
+ return coverageTypes
22
+ .map(coverageType => {
23
+ const stats = totalCoverageStats[coverageType];
24
+ if (!stats) {
25
+ return null;
26
+ }
27
+ return lcovCoverageToAuditOutput(stats, coverageType);
28
+ })
29
+ .filter(exists);
30
+ }
31
+ /**
32
+ *
33
+ * @param results Paths to LCOV results
34
+ * @returns Array of parsed LCOVRecords.
35
+ */
36
+ export async function parseLcovFiles(results) {
37
+ const parsedResults = await Promise.all(results.map(async (result) => {
38
+ const resultsPath = typeof result === 'string' ? result : result.resultsPath;
39
+ const lcovFileContent = await readTextFile(resultsPath);
40
+ if (lcovFileContent.trim() === '') {
41
+ ui().logger.warning(`Coverage plugin: Empty lcov report file detected at ${resultsPath}.`);
42
+ }
43
+ const parsedRecords = parseLcov(toUnixNewlines(lcovFileContent));
44
+ return parsedRecords.map(record => ({
45
+ ...record,
46
+ file: typeof result === 'string' || result.pathToProject == null
47
+ ? record.file
48
+ : path.join(result.pathToProject, record.file),
49
+ }));
50
+ }));
51
+ if (parsedResults.length !== results.length) {
52
+ throw new Error('Some provided LCOV results were not valid.');
53
+ }
54
+ const flatResults = parsedResults.flat();
55
+ if (flatResults.length === 0) {
56
+ throw new Error('All provided results are empty.');
57
+ }
58
+ return flatResults;
59
+ }
60
+ /**
61
+ *
62
+ * @param records This function aggregates coverage stats from all coverage files
63
+ * @param coverageTypes Types of coverage to be gathered
64
+ * @returns Complete coverage stats for all defined types of coverage.
65
+ */
66
+ function getTotalCoverageFromLcovRecords(records, coverageTypes) {
67
+ return records.reduce((acc, report) => Object.fromEntries([
68
+ ...Object.entries(acc),
69
+ ...Object.entries(getCoverageStatsFromLcovRecord(report, coverageTypes)).map(([type, stats]) => [
70
+ type,
71
+ {
72
+ totalFound: (acc[type]?.totalFound ?? 0) + stats.totalFound,
73
+ totalHit: (acc[type]?.totalHit ?? 0) + stats.totalHit,
74
+ issues: [...(acc[type]?.issues ?? []), ...stats.issues],
75
+ },
76
+ ]),
77
+ ]), {});
78
+ }
79
+ /**
80
+ * @param record record file data
81
+ * @param coverageTypes types of coverage to be gathered
82
+ * @returns Relevant coverage data from one lcov record file.
83
+ */
84
+ function getCoverageStatsFromLcovRecord(record, coverageTypes) {
85
+ return Object.fromEntries(coverageTypes.map((coverageType) => [
86
+ coverageType,
87
+ recordToStatFunctionMapper[coverageType](record),
88
+ ]));
89
+ }
90
+ //# sourceMappingURL=lcov-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lcov-runner.js","sourceRoot":"","sources":["../../../../../../../packages/plugin-coverage/src/lib/runner/lcov/lcov-runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,103 @@
1
+ export function mergeLcovResults(records) {
2
+ // Skip if there are no files with multiple records
3
+ const allFilenames = records.map(record => record.file);
4
+ if (allFilenames.length === new Set(allFilenames).size) {
5
+ return records;
6
+ }
7
+ return records.reduce((accMerged, currRecord, currIndex) => {
8
+ const filePath = currRecord.file;
9
+ const lines = currRecord.lines.found;
10
+ const duplicates = records.reduce((acc, candidateRecord, candidateIndex) => {
11
+ if (candidateRecord.file === filePath &&
12
+ candidateRecord.lines.found === lines &&
13
+ candidateIndex !== currIndex) {
14
+ return [...acc, [candidateRecord, candidateIndex]];
15
+ }
16
+ return acc;
17
+ }, []);
18
+ // This is not the first time the record has been identified as a duplicate
19
+ if (duplicates.map(duplicate => duplicate[1]).some(index => index < currIndex)) {
20
+ return accMerged;
21
+ }
22
+ // Unique record
23
+ if (duplicates.length === 0) {
24
+ return [...accMerged, currRecord];
25
+ }
26
+ return [
27
+ ...accMerged,
28
+ mergeDuplicateLcovRecords([
29
+ currRecord,
30
+ ...duplicates.map(duplicate => duplicate[0]),
31
+ ]),
32
+ ];
33
+ }, []);
34
+ }
35
+ export function mergeDuplicateLcovRecords(records) {
36
+ const linesDetails = mergeLcovLineDetails(records.map(record => record.lines.details));
37
+ const linesHit = linesDetails.reduce((acc, line) => acc + (line.hit > 0 ? 1 : 0), 0);
38
+ const branchesDetails = mergeLcovBranchesDetails(records.map(record => record.branches.details));
39
+ const branchesHit = branchesDetails.reduce((acc, branch) => acc + (branch.taken > 0 ? 1 : 0), 0);
40
+ const functionsDetails = mergeLcovFunctionsDetails(records.map(record => record.functions.details));
41
+ const functionsHit = functionsDetails.reduce((acc, func) => acc + (func.hit != null && func.hit > 0 ? 1 : 0), 0);
42
+ const mergedRecord = {
43
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
44
+ file: records[0].file,
45
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
46
+ title: records[0].title,
47
+ lines: {
48
+ found: linesDetails.length,
49
+ hit: linesHit,
50
+ details: linesDetails,
51
+ },
52
+ branches: {
53
+ found: branchesDetails.length,
54
+ hit: branchesHit,
55
+ details: branchesDetails,
56
+ },
57
+ functions: {
58
+ found: functionsDetails.length,
59
+ hit: functionsHit,
60
+ details: functionsDetails,
61
+ },
62
+ };
63
+ return mergedRecord;
64
+ }
65
+ export function mergeLcovLineDetails(details) {
66
+ const flatDetails = details.flat();
67
+ const uniqueLines = [
68
+ ...new Set(flatDetails.map(flatDetail => flatDetail.line)),
69
+ ];
70
+ return uniqueLines.map(line => {
71
+ const hitSum = flatDetails
72
+ .filter(lineDetail => lineDetail.line === line)
73
+ .reduce((acc, lineDetail) => acc + lineDetail.hit, 0);
74
+ return { line, hit: hitSum };
75
+ });
76
+ }
77
+ export function mergeLcovBranchesDetails(details) {
78
+ const flatDetails = details.flat();
79
+ const uniqueBranches = [
80
+ ...new Set(flatDetails.map(({ line, block, branch }) => JSON.stringify({ line, block, branch }))),
81
+ ].map(functionJSON => JSON.parse(functionJSON));
82
+ return uniqueBranches.map(({ line, block, branch }) => {
83
+ const takenSum = flatDetails
84
+ .filter(branchDetail => branchDetail.line === line &&
85
+ branchDetail.block === block &&
86
+ branchDetail.branch === branch)
87
+ .reduce((acc, branchDetail) => acc + branchDetail.taken, 0);
88
+ return { line, block, branch, taken: takenSum };
89
+ });
90
+ }
91
+ export function mergeLcovFunctionsDetails(details) {
92
+ const flatDetails = details.flat();
93
+ const uniqueFunctions = [
94
+ ...new Set(flatDetails.map(({ line, name }) => JSON.stringify({ line, name }))),
95
+ ].map(functionJSON => JSON.parse(functionJSON));
96
+ return uniqueFunctions.map(({ line, name }) => {
97
+ const hitSum = flatDetails
98
+ .filter(functionDetail => functionDetail.line === line && functionDetail.name === name)
99
+ .reduce((acc, functionDetail) => acc + (functionDetail.hit ?? 0), 0);
100
+ return { line, name, hit: hitSum };
101
+ });
102
+ }
103
+ //# sourceMappingURL=merge-lcov.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-lcov.js","sourceRoot":"","sources":["../../../../../../../packages/plugin-coverage/src/lib/runner/lcov/merge-lcov.ts"],"names":[],"mappings":"AAOA,MAAM,UAAU,gBAAgB,CAAC,OAAqB;IACpD,mDAAmD;IACnD,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACxD,IAAI,YAAY,CAAC,MAAM,KAAK,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC,IAAI,EAAE,CAAC;QACvD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,OAAO,OAAO,CAAC,MAAM,CAAe,CAAC,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE;QACvE,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC;QACjC,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC;QAErC,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAC/B,CAAC,GAAG,EAAE,eAAe,EAAE,cAAc,EAAE,EAAE;YACvC,IACE,eAAe,CAAC,IAAI,KAAK,QAAQ;gBACjC,eAAe,CAAC,KAAK,CAAC,KAAK,KAAK,KAAK;gBACrC,cAAc,KAAK,SAAS,EAC5B,CAAC;gBACD,OAAO,CAAC,GAAG,GAAG,EAAE,CAAC,eAAe,EAAE,cAAc,CAAC,CAAC,CAAC;YACrD,CAAC;YACD,OAAO,GAAG,CAAC;QACb,CAAC,EACD,EAAE,CACH,CAAC;QAEF,2EAA2E;QAC3E,IACE,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,GAAG,SAAS,CAAC,EAC1E,CAAC;YACD,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,gBAAgB;QAChB,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5B,OAAO,CAAC,GAAG,SAAS,EAAE,UAAU,CAAC,CAAC;QACpC,CAAC;QAED,OAAO;YACL,GAAG,SAAS;YACZ,yBAAyB,CAAC;gBACxB,UAAU;gBACV,GAAG,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;aAC7C,CAAC;SACH,CAAC;IACJ,CAAC,EAAE,EAAE,CAAC,CAAC;AACT,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,OAAqB;IAC7D,MAAM,YAAY,GAAG,oBAAoB,CACvC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAC5C,CAAC;IACF,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,CAClC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAC3C,CAAC,CACF,CAAC;IAEF,MAAM,eAAe,GAAG,wBAAwB,CAC9C,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAC/C,CAAC;IACF,MAAM,WAAW,GAAG,eAAe,CAAC,MAAM,CACxC,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EACjD,CAAC,CACF,CAAC;IAEF,MAAM,gBAAgB,GAAG,yBAAyB,CAChD,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,CAChD,CAAC;IAEF,MAAM,YAAY,GAAG,gBAAgB,CAAC,MAAM,CAC1C,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAC/D,CAAC,CACF,CAAC;IAEF,MAAM,YAAY,GAAe;QAC/B,oEAAoE;QACpE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAE,CAAC,IAAI;QACtB,oEAAoE;QACpE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAE,CAAC,KAAK;QACxB,KAAK,EAAE;YACL,KAAK,EAAE,YAAY,CAAC,MAAM;YAC1B,GAAG,EAAE,QAAQ;YACb,OAAO,EAAE,YAAY;SACtB;QACD,QAAQ,EAAE;YACR,KAAK,EAAE,eAAe,CAAC,MAAM;YAC7B,GAAG,EAAE,WAAW;YAChB,OAAO,EAAE,eAAe;SACzB;QACD,SAAS,EAAE;YACT,KAAK,EAAE,gBAAgB,CAAC,MAAM;YAC9B,GAAG,EAAE,YAAY;YACjB,OAAO,EAAE,gBAAgB;SAC1B;KACF,CAAC;IACF,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,OAAyB;IAEzB,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAEnC,MAAM,WAAW,GAAG;QAClB,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;KAC3D,CAAC;IAEF,OAAO,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;QAC5B,MAAM,MAAM,GAAG,WAAW;aACvB,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,KAAK,IAAI,CAAC;aAC9C,MAAM,CAAC,CAAC,GAAG,EAAE,UAAU,EAAE,EAAE,CAAC,GAAG,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAExD,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;IAC/B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,OAA4B;IAE5B,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAEnC,MAAM,cAAc,GAAG;QACrB,GAAG,IAAI,GAAG,CACR,WAAW,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAC1C,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CACxC,CACF;KACF,CAAC,GAAG,CACH,YAAY,CAAC,EAAE,CACb,IAAI,CAAC,KAAK,CAAC,YAAY,CAGtB,CACJ,CAAC;IAEF,OAAO,cAAc,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE;QACpD,MAAM,QAAQ,GAAG,WAAW;aACzB,MAAM,CACL,YAAY,CAAC,EAAE,CACb,YAAY,CAAC,IAAI,KAAK,IAAI;YAC1B,YAAY,CAAC,KAAK,KAAK,KAAK;YAC5B,YAAY,CAAC,MAAM,KAAK,MAAM,CACjC;aACA,MAAM,CAAC,CAAC,GAAG,EAAE,YAAY,EAAE,EAAE,CAAC,GAAG,GAAG,YAAY,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAE9D,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClD,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,yBAAyB,CACvC,OAA6B;IAE7B,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAEnC,MAAM,eAAe,GAAG;QACtB,GAAG,IAAI,GAAG,CACR,WAAW,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CACpE;KACF,CAAC,GAAG,CACH,YAAY,CAAC,EAAE,CACb,IAAI,CAAC,KAAK,CAAC,YAAY,CAA4C,CACtE,CAAC;IAEF,OAAO,eAAe,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE;QAC5C,MAAM,MAAM,GAAG,WAAW;aACvB,MAAM,CACL,cAAc,CAAC,EAAE,CACf,cAAc,CAAC,IAAI,KAAK,IAAI,IAAI,cAAc,CAAC,IAAI,KAAK,IAAI,CAC/D;aACA,MAAM,CAAC,CAAC,GAAG,EAAE,cAAc,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAEvE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;IACrC,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,5 @@
1
+ import parseLcovExport from 'parse-lcov';
2
+ // the parse-lcov export is inconsistent (sometimes it's .default, sometimes it's .default.default)
3
+ const godKnows = parseLcovExport;
4
+ export const parseLcov = 'default' in godKnows ? godKnows.default : godKnows;
5
+ //# sourceMappingURL=parse-lcov.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-lcov.js","sourceRoot":"","sources":["../../../../../../../packages/plugin-coverage/src/lib/runner/lcov/parse-lcov.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,MAAM,YAAY,CAAC;AAIzC,mGAAmG;AACnG,MAAM,QAAQ,GAAG,eAEW,CAAC;AAE7B,MAAM,CAAC,MAAM,SAAS,GACpB,SAAS,IAAI,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import type { LCOVRecord } from 'parse-lcov';
2
2
  import type { AuditOutput } from '@code-pushup/models';
3
- import type { CoverageType } from '../../config';
4
- import type { LCOVStat } from './types';
3
+ import type { CoverageType } from '../../config.js';
4
+ import type { LCOVStat } from './types.js';
5
5
  export declare function lcovReportToFunctionStat(record: LCOVRecord): LCOVStat;
6
6
  export declare function lcovReportToLineStat(record: LCOVRecord): LCOVStat;
7
7
  export declare function lcovReportToBranchStat(record: LCOVRecord): LCOVStat;