@code-pushup/coverage-plugin 0.46.0 → 0.47.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/bin.js CHANGED
@@ -68,7 +68,7 @@ function missingRefsForCategoriesErrorMsg(categories, plugins) {
68
68
  }
69
69
 
70
70
  // packages/models/src/lib/implementation/schemas.ts
71
- var primitiveValueSchema = z.union([z.string(), z.number()]);
71
+ var tableCellValueSchema = z.union([z.string(), z.number(), z.boolean(), z.null()]).default(null);
72
72
  function executionMetaSchema(options = {
73
73
  descriptionDate: "Execution start date and time",
74
74
  descriptionDuration: "Execution duration in ms"
@@ -232,10 +232,10 @@ var tableColumnObjectSchema = z4.object({
232
232
  label: z4.string().optional(),
233
233
  align: tableAlignmentSchema.optional()
234
234
  });
235
- var tableRowObjectSchema = z4.record(primitiveValueSchema, {
235
+ var tableRowObjectSchema = z4.record(tableCellValueSchema, {
236
236
  description: "Object row"
237
237
  });
238
- var tableRowPrimitiveSchema = z4.array(primitiveValueSchema, {
238
+ var tableRowPrimitiveSchema = z4.array(tableCellValueSchema, {
239
239
  description: "Primitive row"
240
240
  });
241
241
  var tableSharedSchema = z4.object({
@@ -792,7 +792,7 @@ function code(text) {
792
792
 
793
793
  // packages/utils/src/lib/text-formats/html/link.ts
794
794
  function link(href, text) {
795
- return `<a href="${href}">${text || href}"</a>`;
795
+ return `<a href="${href}">${text || href}</a>`;
796
796
  }
797
797
 
798
798
  // packages/utils/src/lib/transform.ts
@@ -825,7 +825,7 @@ function toOrdinal(value) {
825
825
  return `${value}th`;
826
826
  }
827
827
 
828
- // packages/utils/src/lib/table.ts
828
+ // packages/utils/src/lib/text-formats/table.ts
829
829
  function rowToStringArray({ rows, columns = [] }) {
830
830
  if (Array.isArray(rows.at(0)) && typeof columns.at(0) === "object") {
831
831
  throw new TypeError(
@@ -838,14 +838,19 @@ function rowToStringArray({ rows, columns = [] }) {
838
838
  }
839
839
  const objectRow = row;
840
840
  if (columns.length === 0 || typeof columns.at(0) === "string") {
841
- return Object.values(objectRow).map(String);
841
+ return Object.values(objectRow).map(
842
+ (value) => value == null ? "" : String(value)
843
+ );
842
844
  }
843
845
  return columns.map(
844
- ({ key }) => String(objectRow[key])
846
+ ({ key }) => objectRow[key] == null ? "" : String(objectRow[key])
845
847
  );
846
848
  });
847
849
  }
848
- function columnsToStringArray({ rows, columns = [] }) {
850
+ function columnsToStringArray({
851
+ rows,
852
+ columns = []
853
+ }) {
849
854
  const firstRow = rows.at(0);
850
855
  const primitiveRows = Array.isArray(firstRow);
851
856
  if (typeof columns.at(0) === "string" && !primitiveRows) {
@@ -885,10 +890,8 @@ function getColumnAlignmentForIndex(targetIdx, columns = []) {
885
890
  return "center";
886
891
  }
887
892
  }
888
- function getColumnAlignments({
889
- rows,
890
- columns = []
891
- }) {
893
+ function getColumnAlignments(tableData) {
894
+ const { rows, columns = [] } = tableData;
892
895
  if (rows.at(0) == null) {
893
896
  throw new Error("first row can`t be undefined.");
894
897
  }
@@ -898,10 +901,17 @@ function getColumnAlignments({
898
901
  (_, idx) => getColumnAlignmentForIndex(idx, columns)
899
902
  );
900
903
  }
901
- const firstObject = rows.at(0);
902
- return Object.keys(firstObject).map(
903
- (key, idx) => getColumnAlignmentForKeyAndIndex(key, idx, columns)
904
- );
904
+ const biggestRow = [...rows].sort((a, b) => Object.keys(a).length - Object.keys(b).length).at(-1);
905
+ if (columns.length > 0) {
906
+ return columns.map(
907
+ (column, idx) => typeof column === "string" ? column : getColumnAlignmentForKeyAndIndex(
908
+ column.key,
909
+ idx,
910
+ columns
911
+ )
912
+ );
913
+ }
914
+ return Object.keys(biggestRow ?? {}).map((_) => "center");
905
915
  }
906
916
 
907
917
  // packages/utils/src/lib/text-formats/html/table.ts
@@ -998,7 +1008,10 @@ function section(...contents) {
998
1008
  return `${lines(...contents)}${NEW_LINE}`;
999
1009
  }
1000
1010
  function lines(...contents) {
1001
- return `${contents.filter(Boolean).join(NEW_LINE)}`;
1011
+ const filteredContent = contents.filter(
1012
+ (value) => value != null && value !== "" && value !== false
1013
+ );
1014
+ return `${filteredContent.join(NEW_LINE)}`;
1002
1015
  }
1003
1016
 
1004
1017
  // packages/utils/src/lib/text-formats/md/table.ts
@@ -1393,12 +1406,12 @@ var recordToStatFunctionMapper = {
1393
1406
  function lcovCoverageToAuditOutput(stat2, coverageType) {
1394
1407
  const coverage = calculateCoverage(stat2.totalHit, stat2.totalFound);
1395
1408
  const MAX_DECIMAL_PLACES = 4;
1396
- const roundedIntValue = toNumberPrecision(coverage * 100, 0);
1409
+ const coveragePercentage = coverage * 100;
1397
1410
  return {
1398
1411
  slug: `${coverageType}-coverage`,
1399
1412
  score: toNumberPrecision(coverage, MAX_DECIMAL_PLACES),
1400
- value: roundedIntValue,
1401
- displayValue: `${roundedIntValue} %`,
1413
+ value: coveragePercentage,
1414
+ displayValue: `${toNumberPrecision(coveragePercentage, 1)} %`,
1402
1415
  details: {
1403
1416
  issues: stat2.issues
1404
1417
  }
package/index.js CHANGED
@@ -67,7 +67,7 @@ function missingRefsForCategoriesErrorMsg(categories, plugins) {
67
67
  }
68
68
 
69
69
  // packages/models/src/lib/implementation/schemas.ts
70
- var primitiveValueSchema = z.union([z.string(), z.number()]);
70
+ var tableCellValueSchema = z.union([z.string(), z.number(), z.boolean(), z.null()]).default(null);
71
71
  function executionMetaSchema(options = {
72
72
  descriptionDate: "Execution start date and time",
73
73
  descriptionDuration: "Execution duration in ms"
@@ -231,10 +231,10 @@ var tableColumnObjectSchema = z4.object({
231
231
  label: z4.string().optional(),
232
232
  align: tableAlignmentSchema.optional()
233
233
  });
234
- var tableRowObjectSchema = z4.record(primitiveValueSchema, {
234
+ var tableRowObjectSchema = z4.record(tableCellValueSchema, {
235
235
  description: "Object row"
236
236
  });
237
- var tableRowPrimitiveSchema = z4.array(primitiveValueSchema, {
237
+ var tableRowPrimitiveSchema = z4.array(tableCellValueSchema, {
238
238
  description: "Primitive row"
239
239
  });
240
240
  var tableSharedSchema = z4.object({
@@ -747,20 +747,12 @@ async function ensureDirectoryExists(baseDir) {
747
747
  }
748
748
  }
749
749
  }
750
- var NoExportError = class extends Error {
751
- constructor(filepath) {
752
- super(`No default export found in ${filepath}`);
750
+ async function importModule(options) {
751
+ const { mod } = await bundleRequire(options);
752
+ if (typeof mod === "object" && "default" in mod) {
753
+ return mod.default;
753
754
  }
754
- };
755
- async function importEsmModule(options) {
756
- const { mod } = await bundleRequire({
757
- format: "esm",
758
- ...options
759
- });
760
- if (!("default" in mod)) {
761
- throw new NoExportError(options.filepath);
762
- }
763
- return mod.default;
755
+ return mod;
764
756
  }
765
757
  function pluginWorkDir(slug) {
766
758
  return join("node_modules", ".code-pushup", slug);
@@ -798,7 +790,7 @@ function code(text) {
798
790
 
799
791
  // packages/utils/src/lib/text-formats/html/link.ts
800
792
  function link(href, text) {
801
- return `<a href="${href}">${text || href}"</a>`;
793
+ return `<a href="${href}">${text || href}</a>`;
802
794
  }
803
795
 
804
796
  // packages/utils/src/lib/transform.ts
@@ -808,7 +800,7 @@ function capitalize(text) {
808
800
  )}`;
809
801
  }
810
802
 
811
- // packages/utils/src/lib/table.ts
803
+ // packages/utils/src/lib/text-formats/table.ts
812
804
  function rowToStringArray({ rows, columns = [] }) {
813
805
  if (Array.isArray(rows.at(0)) && typeof columns.at(0) === "object") {
814
806
  throw new TypeError(
@@ -821,14 +813,19 @@ function rowToStringArray({ rows, columns = [] }) {
821
813
  }
822
814
  const objectRow = row;
823
815
  if (columns.length === 0 || typeof columns.at(0) === "string") {
824
- return Object.values(objectRow).map(String);
816
+ return Object.values(objectRow).map(
817
+ (value) => value == null ? "" : String(value)
818
+ );
825
819
  }
826
820
  return columns.map(
827
- ({ key }) => String(objectRow[key])
821
+ ({ key }) => objectRow[key] == null ? "" : String(objectRow[key])
828
822
  );
829
823
  });
830
824
  }
831
- function columnsToStringArray({ rows, columns = [] }) {
825
+ function columnsToStringArray({
826
+ rows,
827
+ columns = []
828
+ }) {
832
829
  const firstRow = rows.at(0);
833
830
  const primitiveRows = Array.isArray(firstRow);
834
831
  if (typeof columns.at(0) === "string" && !primitiveRows) {
@@ -868,10 +865,8 @@ function getColumnAlignmentForIndex(targetIdx, columns = []) {
868
865
  return "center";
869
866
  }
870
867
  }
871
- function getColumnAlignments({
872
- rows,
873
- columns = []
874
- }) {
868
+ function getColumnAlignments(tableData) {
869
+ const { rows, columns = [] } = tableData;
875
870
  if (rows.at(0) == null) {
876
871
  throw new Error("first row can`t be undefined.");
877
872
  }
@@ -881,10 +876,17 @@ function getColumnAlignments({
881
876
  (_, idx) => getColumnAlignmentForIndex(idx, columns)
882
877
  );
883
878
  }
884
- const firstObject = rows.at(0);
885
- return Object.keys(firstObject).map(
886
- (key, idx) => getColumnAlignmentForKeyAndIndex(key, idx, columns)
887
- );
879
+ const biggestRow = [...rows].sort((a, b) => Object.keys(a).length - Object.keys(b).length).at(-1);
880
+ if (columns.length > 0) {
881
+ return columns.map(
882
+ (column, idx) => typeof column === "string" ? column : getColumnAlignmentForKeyAndIndex(
883
+ column.key,
884
+ idx,
885
+ columns
886
+ )
887
+ );
888
+ }
889
+ return Object.keys(biggestRow ?? {}).map((_) => "center");
888
890
  }
889
891
 
890
892
  // packages/utils/src/lib/text-formats/html/table.ts
@@ -981,7 +983,10 @@ function section(...contents) {
981
983
  return `${lines(...contents)}${NEW_LINE}`;
982
984
  }
983
985
  function lines(...contents) {
984
- return `${contents.filter(Boolean).join(NEW_LINE)}`;
986
+ const filteredContent = contents.filter(
987
+ (value) => value != null && value !== "" && value !== false
988
+ );
989
+ return `${filteredContent.join(NEW_LINE)}`;
985
990
  }
986
991
 
987
992
  // packages/utils/src/lib/text-formats/md/table.ts
@@ -1085,7 +1090,7 @@ import chalk4 from "chalk";
1085
1090
 
1086
1091
  // packages/plugin-coverage/package.json
1087
1092
  var name = "@code-pushup/coverage-plugin";
1088
- var version = "0.46.0";
1093
+ var version = "0.47.0";
1089
1094
 
1090
1095
  // packages/plugin-coverage/src/lib/config.ts
1091
1096
  import { z as z16 } from "zod";
@@ -1212,7 +1217,7 @@ async function coveragePlugin(config) {
1212
1217
 
1213
1218
  // packages/plugin-coverage/src/lib/nx/coverage-paths.ts
1214
1219
  import chalk6 from "chalk";
1215
- import { join as join4 } from "node:path";
1220
+ import { isAbsolute, join as join4 } from "node:path";
1216
1221
  async function getNxCoveragePaths(targets = ["test"], verbose) {
1217
1222
  if (verbose) {
1218
1223
  ui().logger.info(
@@ -1228,12 +1233,7 @@ async function getNxCoveragePaths(targets = ["test"], verbose) {
1228
1233
  );
1229
1234
  return await Promise.all(
1230
1235
  relevantNodes.map(async ({ name: name2, data }) => {
1231
- const targetConfig = data.targets?.[target];
1232
- const coveragePaths = await getCoveragePathsForTarget(
1233
- targetConfig,
1234
- data.root,
1235
- `${target} in ${name2}`
1236
- );
1236
+ const coveragePaths = await getCoveragePathsForTarget(data, target);
1237
1237
  if (verbose) {
1238
1238
  ui().logger.info(`- ${name2}: ${target}`);
1239
1239
  }
@@ -1250,57 +1250,94 @@ async function getNxCoveragePaths(targets = ["test"], verbose) {
1250
1250
  function hasNxTarget(project, target) {
1251
1251
  return project.data.targets != null && target in project.data.targets;
1252
1252
  }
1253
- async function getCoveragePathsForTarget(targetConfig, pathToRoot, targetContext) {
1254
- const { config } = targetConfig.options;
1253
+ async function getCoveragePathsForTarget(project, target) {
1254
+ const targetConfig = project.targets?.[target];
1255
+ if (!targetConfig) {
1256
+ throw new Error(
1257
+ `No configuration found for target ${target} in project ${project.name}`
1258
+ );
1259
+ }
1255
1260
  if (targetConfig.executor?.includes("@nx/vite")) {
1256
- return await getCoveragePathForVitest(config, pathToRoot, targetContext);
1261
+ return getCoveragePathForVitest(
1262
+ targetConfig.options,
1263
+ project,
1264
+ target
1265
+ );
1257
1266
  }
1258
1267
  if (targetConfig.executor?.includes("@nx/jest")) {
1259
- return await getCoveragePathForJest(config, pathToRoot, targetContext);
1268
+ return getCoveragePathForJest(
1269
+ targetConfig.options,
1270
+ project,
1271
+ target
1272
+ );
1260
1273
  }
1261
1274
  throw new Error(
1262
- `Unsupported executor ${targetConfig.executor}. @nx/vite and @nx/jest are currently supported.`
1275
+ `Unsupported executor ${targetConfig.executor}. Only @nx/vite and @nx/jest are currently supported.`
1263
1276
  );
1264
1277
  }
1265
- async function getCoveragePathForVitest(pathToConfig, pathToRoot, context) {
1266
- const vitestConfig = await importEsmModule({
1267
- filepath: pathToConfig,
1278
+ async function getCoveragePathForVitest(options, project, target) {
1279
+ const {
1280
+ default: { normalizeViteConfigFilePathWithTree }
1281
+ } = await import("@nx/vite");
1282
+ const config = normalizeViteConfigFilePathWithTree(
1283
+ // HACK: only tree.exists is called, so injecting existSync from node:fs instead
1284
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, n/no-sync
1285
+ { exists: (await import("node:fs")).existsSync },
1286
+ project.root,
1287
+ options.configFile
1288
+ );
1289
+ if (!config) {
1290
+ throw new Error(
1291
+ `Could not find Vitest config file for target ${target} in project ${project.name}`
1292
+ );
1293
+ }
1294
+ const vitestConfig = await importModule({
1295
+ filepath: config,
1268
1296
  format: "esm"
1269
1297
  });
1270
- const reportsDirectory = vitestConfig.test.coverage?.reportsDirectory;
1298
+ const reportsDirectory = options.reportsDirectory ?? vitestConfig.test.coverage?.reportsDirectory;
1271
1299
  const reporter = vitestConfig.test.coverage?.reporter;
1272
1300
  if (reportsDirectory == null) {
1273
1301
  throw new Error(
1274
- `Vitest coverage configuration at ${pathToConfig} does not include coverage path for target ${context}. Add the path under coverage > reportsDirectory.`
1302
+ `Vitest coverage configuration at ${config} does not include coverage path for target ${target} in project ${project.name}. Add the path under coverage > reportsDirectory.`
1275
1303
  );
1276
1304
  }
1277
1305
  if (!reporter?.includes("lcov")) {
1278
1306
  throw new Error(
1279
- `Vitest coverage configuration at ${pathToConfig} does not include LCOV report format for target ${context}. Add 'lcov' format under coverage > reporter.`
1307
+ `Vitest coverage configuration at ${config} does not include LCOV report format for target ${target} in project ${project.name}. Add 'lcov' format under coverage > reporter.`
1280
1308
  );
1281
1309
  }
1310
+ if (isAbsolute(reportsDirectory)) {
1311
+ return join4(reportsDirectory, "lcov.info");
1312
+ }
1282
1313
  return {
1283
- pathToProject: pathToRoot,
1284
- resultsPath: join4(pathToRoot, reportsDirectory, "lcov.info")
1314
+ pathToProject: project.root,
1315
+ resultsPath: join4(project.root, reportsDirectory, "lcov.info")
1285
1316
  };
1286
1317
  }
1287
- async function getCoveragePathForJest(pathToConfig, pathToRoot, context) {
1288
- const testConfig = await importEsmModule({
1289
- filepath: pathToConfig,
1290
- format: "cjs"
1318
+ async function getCoveragePathForJest(options, project, target) {
1319
+ const { jestConfig } = options;
1320
+ const testConfig = await importModule({
1321
+ filepath: jestConfig
1291
1322
  });
1292
- const coverageDirectory = testConfig.coverageDirectory;
1323
+ const { coverageDirectory, coverageReporters } = {
1324
+ ...testConfig,
1325
+ ...options
1326
+ };
1293
1327
  if (coverageDirectory == null) {
1294
1328
  throw new Error(
1295
- `Jest coverage configuration at ${pathToConfig} does not include coverage path for target ${context}. Add the path under coverageDirectory.`
1329
+ `Jest coverage configuration at ${jestConfig} does not include coverage path for target ${target} in ${project.name}. Add the path under coverageDirectory.`
1296
1330
  );
1297
1331
  }
1298
- if (!testConfig.coverageReporters?.includes("lcov")) {
1332
+ if (!coverageReporters?.includes("lcov") && !("preset" in testConfig)) {
1299
1333
  throw new Error(
1300
- `Jest coverage configuration at ${pathToConfig} does not include LCOV report format for target ${context}. Add 'lcov' format under coverageReporters.`
1334
+ `Jest coverage configuration at ${jestConfig} does not include LCOV report format for target ${target} in ${project.name}. Add 'lcov' format under coverageReporters.`
1301
1335
  );
1302
1336
  }
1303
- return join4(pathToRoot, coverageDirectory, "lcov.info");
1337
+ if (isAbsolute(coverageDirectory)) {
1338
+ return join4(coverageDirectory, "lcov.info");
1339
+ }
1340
+ return join4(project.root, coverageDirectory, "lcov.info");
1304
1341
  }
1305
1342
 
1306
1343
  // packages/plugin-coverage/src/index.ts
package/package.json CHANGED
@@ -1,19 +1,27 @@
1
1
  {
2
2
  "name": "@code-pushup/coverage-plugin",
3
- "version": "0.46.0",
3
+ "version": "0.47.0",
4
4
  "dependencies": {
5
- "@code-pushup/models": "0.46.0",
6
- "@code-pushup/utils": "0.46.0",
5
+ "@code-pushup/models": "0.47.0",
6
+ "@code-pushup/utils": "0.47.0",
7
7
  "parse-lcov": "^1.0.4",
8
8
  "chalk": "^5.3.0",
9
9
  "zod": "^3.22.4"
10
10
  },
11
11
  "peerDependencies": {
12
- "@nx/devkit": "^17.0.0 || ^18.0.0 || ^19.0.0"
12
+ "@nx/devkit": "^17.0.0 || ^18.0.0 || ^19.0.0",
13
+ "@nx/jest": "^17.0.0 || ^18.0.0 || ^19.0.0",
14
+ "@nx/vite": "^17.0.0 || ^18.0.0 || ^19.0.0"
13
15
  },
14
16
  "peerDependenciesMeta": {
15
17
  "@nx/devkit": {
16
18
  "optional": true
19
+ },
20
+ "@nx/jest": {
21
+ "optional": true
22
+ },
23
+ "@nx/vite": {
24
+ "optional": true
17
25
  }
18
26
  },
19
27
  "license": "MIT",
@@ -1,4 +1,6 @@
1
- import type { TargetConfiguration } from '@nx/devkit';
1
+ import type { ProjectConfiguration } from '@nx/devkit';
2
+ import type { JestExecutorOptions } from '@nx/jest/src/executors/jest/schema';
3
+ import type { VitestExecutorOptions } from '@nx/vite/executors';
2
4
  import { CoverageResult } from '../config';
3
5
  /**
4
6
  * @param targets nx targets to be used for measuring coverage, test by default
@@ -17,9 +19,9 @@ export type JestCoverageConfig = {
17
19
  coverageDirectory?: string;
18
20
  coverageReporters?: string[];
19
21
  };
20
- export declare function getCoveragePathsForTarget(targetConfig: TargetConfiguration, pathToRoot: string, targetContext: string): Promise<CoverageResult>;
21
- export declare function getCoveragePathForVitest(pathToConfig: string, pathToRoot: string, context: string): Promise<{
22
+ export declare function getCoveragePathsForTarget(project: ProjectConfiguration, target: string): Promise<CoverageResult>;
23
+ export declare function getCoveragePathForVitest(options: VitestExecutorOptions, project: ProjectConfiguration, target: string): Promise<string | {
22
24
  pathToProject: string;
23
25
  resultsPath: string;
24
26
  }>;
25
- export declare function getCoveragePathForJest(pathToConfig: string, pathToRoot: string, context: string): Promise<string>;
27
+ export declare function getCoveragePathForJest(options: JestExecutorOptions, project: ProjectConfiguration, target: string): Promise<string>;