@code-pushup/cli 0.50.0 → 0.51.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/index.js CHANGED
@@ -163,9 +163,27 @@ function hasNonZeroWeightedRef(refs) {
163
163
  return refs.reduce((acc, { weight }) => weight + acc, 0) !== 0;
164
164
  }
165
165
 
166
- // packages/models/src/lib/audit.ts
166
+ // packages/models/src/lib/source.ts
167
167
  import { z as z2 } from "zod";
168
- var auditSchema = z2.object({
168
+ var sourceFileLocationSchema = z2.object(
169
+ {
170
+ file: filePathSchema.describe("Relative path to source file in Git repo"),
171
+ position: z2.object(
172
+ {
173
+ startLine: positiveIntSchema.describe("Start line"),
174
+ startColumn: positiveIntSchema.describe("Start column").optional(),
175
+ endLine: positiveIntSchema.describe("End line").optional(),
176
+ endColumn: positiveIntSchema.describe("End column").optional()
177
+ },
178
+ { description: "Location in file" }
179
+ ).optional()
180
+ },
181
+ { description: "Source file location" }
182
+ );
183
+
184
+ // packages/models/src/lib/audit.ts
185
+ import { z as z3 } from "zod";
186
+ var auditSchema = z3.object({
169
187
  slug: slugSchema.describe("ID (unique within plugin)")
170
188
  }).merge(
171
189
  metaSchema({
@@ -175,7 +193,7 @@ var auditSchema = z2.object({
175
193
  description: "List of scorable metrics for the given plugin"
176
194
  })
177
195
  );
178
- var pluginAuditsSchema = z2.array(auditSchema, {
196
+ var pluginAuditsSchema = z3.array(auditSchema, {
179
197
  description: "List of audits maintained in a plugin"
180
198
  }).min(1).refine(
181
199
  (auditMetadata) => !getDuplicateSlugsInAudits(auditMetadata),
@@ -194,31 +212,16 @@ function getDuplicateSlugsInAudits(audits) {
194
212
  }
195
213
 
196
214
  // packages/models/src/lib/audit-output.ts
197
- import { z as z5 } from "zod";
215
+ import { z as z6 } from "zod";
198
216
 
199
217
  // packages/models/src/lib/issue.ts
200
- import { z as z3 } from "zod";
201
- var sourceFileLocationSchema = z3.object(
202
- {
203
- file: filePathSchema.describe("Relative path to source file in Git repo"),
204
- position: z3.object(
205
- {
206
- startLine: positiveIntSchema.describe("Start line"),
207
- startColumn: positiveIntSchema.describe("Start column").optional(),
208
- endLine: positiveIntSchema.describe("End line").optional(),
209
- endColumn: positiveIntSchema.describe("End column").optional()
210
- },
211
- { description: "Location in file" }
212
- ).optional()
213
- },
214
- { description: "Source file location" }
215
- );
216
- var issueSeveritySchema = z3.enum(["info", "warning", "error"], {
218
+ import { z as z4 } from "zod";
219
+ var issueSeveritySchema = z4.enum(["info", "warning", "error"], {
217
220
  description: "Severity level"
218
221
  });
219
- var issueSchema = z3.object(
222
+ var issueSchema = z4.object(
220
223
  {
221
- message: z3.string({ description: "Descriptive error message" }).max(MAX_ISSUE_MESSAGE_LENGTH),
224
+ message: z4.string({ description: "Descriptive error message" }).max(MAX_ISSUE_MESSAGE_LENGTH),
222
225
  severity: issueSeveritySchema,
223
226
  source: sourceFileLocationSchema.optional()
224
227
  },
@@ -226,60 +229,60 @@ var issueSchema = z3.object(
226
229
  );
227
230
 
228
231
  // packages/models/src/lib/table.ts
229
- import { z as z4 } from "zod";
230
- var tableAlignmentSchema = z4.enum(["left", "center", "right"], {
232
+ import { z as z5 } from "zod";
233
+ var tableAlignmentSchema = z5.enum(["left", "center", "right"], {
231
234
  description: "Cell alignment"
232
235
  });
233
- var tableColumnObjectSchema = z4.object({
234
- key: z4.string(),
235
- label: z4.string().optional(),
236
+ var tableColumnObjectSchema = z5.object({
237
+ key: z5.string(),
238
+ label: z5.string().optional(),
236
239
  align: tableAlignmentSchema.optional()
237
240
  });
238
- var tableRowObjectSchema = z4.record(tableCellValueSchema, {
241
+ var tableRowObjectSchema = z5.record(tableCellValueSchema, {
239
242
  description: "Object row"
240
243
  });
241
- var tableRowPrimitiveSchema = z4.array(tableCellValueSchema, {
244
+ var tableRowPrimitiveSchema = z5.array(tableCellValueSchema, {
242
245
  description: "Primitive row"
243
246
  });
244
- var tableSharedSchema = z4.object({
245
- title: z4.string().optional().describe("Display title for table")
247
+ var tableSharedSchema = z5.object({
248
+ title: z5.string().optional().describe("Display title for table")
246
249
  });
247
250
  var tablePrimitiveSchema = tableSharedSchema.merge(
248
- z4.object(
251
+ z5.object(
249
252
  {
250
- columns: z4.array(tableAlignmentSchema).optional(),
251
- rows: z4.array(tableRowPrimitiveSchema)
253
+ columns: z5.array(tableAlignmentSchema).optional(),
254
+ rows: z5.array(tableRowPrimitiveSchema)
252
255
  },
253
256
  { description: "Table with primitive rows and optional alignment columns" }
254
257
  )
255
258
  );
256
259
  var tableObjectSchema = tableSharedSchema.merge(
257
- z4.object(
260
+ z5.object(
258
261
  {
259
- columns: z4.union([
260
- z4.array(tableAlignmentSchema),
261
- z4.array(tableColumnObjectSchema)
262
+ columns: z5.union([
263
+ z5.array(tableAlignmentSchema),
264
+ z5.array(tableColumnObjectSchema)
262
265
  ]).optional(),
263
- rows: z4.array(tableRowObjectSchema)
266
+ rows: z5.array(tableRowObjectSchema)
264
267
  },
265
268
  {
266
269
  description: "Table with object rows and optional alignment or object columns"
267
270
  }
268
271
  )
269
272
  );
270
- var tableSchema = (description = "Table information") => z4.union([tablePrimitiveSchema, tableObjectSchema], { description });
273
+ var tableSchema = (description = "Table information") => z5.union([tablePrimitiveSchema, tableObjectSchema], { description });
271
274
 
272
275
  // packages/models/src/lib/audit-output.ts
273
276
  var auditValueSchema = nonnegativeNumberSchema.describe("Raw numeric value");
274
- var auditDisplayValueSchema = z5.string({ description: "Formatted value (e.g. '0.9 s', '2.1 MB')" }).optional();
275
- var auditDetailsSchema = z5.object(
277
+ var auditDisplayValueSchema = z6.string({ description: "Formatted value (e.g. '0.9 s', '2.1 MB')" }).optional();
278
+ var auditDetailsSchema = z6.object(
276
279
  {
277
- issues: z5.array(issueSchema, { description: "List of findings" }).optional(),
280
+ issues: z6.array(issueSchema, { description: "List of findings" }).optional(),
278
281
  table: tableSchema("Table of related findings").optional()
279
282
  },
280
283
  { description: "Detailed information" }
281
284
  );
282
- var auditOutputSchema = z5.object(
285
+ var auditOutputSchema = z6.object(
283
286
  {
284
287
  slug: slugSchema.describe("Reference to audit"),
285
288
  displayValue: auditDisplayValueSchema,
@@ -289,7 +292,7 @@ var auditOutputSchema = z5.object(
289
292
  },
290
293
  { description: "Audit information" }
291
294
  );
292
- var auditOutputsSchema = z5.array(auditOutputSchema, {
295
+ var auditOutputsSchema = z6.array(auditOutputSchema, {
293
296
  description: "List of JSON formatted audit output emitted by the runner process of a plugin"
294
297
  }).refine(
295
298
  (audits) => !getDuplicateSlugsInAudits2(audits),
@@ -306,13 +309,13 @@ function getDuplicateSlugsInAudits2(audits) {
306
309
  }
307
310
 
308
311
  // packages/models/src/lib/category-config.ts
309
- import { z as z6 } from "zod";
312
+ import { z as z7 } from "zod";
310
313
  var categoryRefSchema = weightedRefSchema(
311
314
  "Weighted references to audits and/or groups for the category",
312
315
  "Slug of an audit or group (depending on `type`)"
313
316
  ).merge(
314
- z6.object({
315
- type: z6.enum(["audit", "group"], {
317
+ z7.object({
318
+ type: z7.enum(["audit", "group"], {
316
319
  description: "Discriminant for reference kind, affects where `slug` is looked up"
317
320
  }),
318
321
  plugin: slugSchema.describe(
@@ -333,8 +336,8 @@ var categoryConfigSchema = scorableSchema(
333
336
  description: "Meta info for category"
334
337
  })
335
338
  ).merge(
336
- z6.object({
337
- isBinary: z6.boolean({
339
+ z7.object({
340
+ isBinary: z7.boolean({
338
341
  description: 'Is this a binary category (i.e. only a perfect score considered a "pass")?'
339
342
  }).optional()
340
343
  })
@@ -350,7 +353,7 @@ function getDuplicateRefsInCategoryMetrics(metrics) {
350
353
  metrics.map(({ slug, type, plugin }) => `${type} :: ${plugin} / ${slug}`)
351
354
  );
352
355
  }
353
- var categoriesSchema = z6.array(categoryConfigSchema, {
356
+ var categoriesSchema = z7.array(categoryConfigSchema, {
354
357
  description: "Categorization of individual audits"
355
358
  }).refine(
356
359
  (categoryCfg) => !getDuplicateSlugCategories(categoryCfg),
@@ -369,18 +372,18 @@ function getDuplicateSlugCategories(categories) {
369
372
  }
370
373
 
371
374
  // packages/models/src/lib/commit.ts
372
- import { z as z7 } from "zod";
373
- var commitSchema = z7.object(
375
+ import { z as z8 } from "zod";
376
+ var commitSchema = z8.object(
374
377
  {
375
- hash: z7.string({ description: "Commit SHA (full)" }).regex(
378
+ hash: z8.string({ description: "Commit SHA (full)" }).regex(
376
379
  /^[\da-f]{40}$/,
377
380
  "Commit SHA should be a 40-character hexadecimal string"
378
381
  ),
379
- message: z7.string({ description: "Commit message" }),
380
- date: z7.coerce.date({
382
+ message: z8.string({ description: "Commit message" }),
383
+ date: z8.coerce.date({
381
384
  description: "Date and time when commit was authored"
382
385
  }),
383
- author: z7.string({
386
+ author: z8.string({
384
387
  description: "Commit author name"
385
388
  }).trim()
386
389
  },
@@ -388,22 +391,22 @@ var commitSchema = z7.object(
388
391
  );
389
392
 
390
393
  // packages/models/src/lib/core-config.ts
391
- import { z as z13 } from "zod";
394
+ import { z as z14 } from "zod";
392
395
 
393
396
  // packages/models/src/lib/persist-config.ts
394
- import { z as z8 } from "zod";
395
- var formatSchema = z8.enum(["json", "md"]);
396
- var persistConfigSchema = z8.object({
397
+ import { z as z9 } from "zod";
398
+ var formatSchema = z9.enum(["json", "md"]);
399
+ var persistConfigSchema = z9.object({
397
400
  outputDir: filePathSchema.describe("Artifacts folder").optional(),
398
401
  filename: fileNameSchema.describe("Artifacts file name (without extension)").optional(),
399
- format: z8.array(formatSchema).optional()
402
+ format: z9.array(formatSchema).optional()
400
403
  });
401
404
 
402
405
  // packages/models/src/lib/plugin-config.ts
403
- import { z as z11 } from "zod";
406
+ import { z as z12 } from "zod";
404
407
 
405
408
  // packages/models/src/lib/group.ts
406
- import { z as z9 } from "zod";
409
+ import { z as z10 } from "zod";
407
410
  var groupRefSchema = weightedRefSchema(
408
411
  "Weighted reference to a group",
409
412
  "Reference slug to a group within this plugin (e.g. 'max-lines')"
@@ -420,7 +423,7 @@ var groupSchema = scorableSchema(
420
423
  getDuplicateRefsInGroups,
421
424
  duplicateRefsInGroupsErrorMsg
422
425
  ).merge(groupMetaSchema);
423
- var groupsSchema = z9.array(groupSchema, {
426
+ var groupsSchema = z10.array(groupSchema, {
424
427
  description: "List of groups"
425
428
  }).optional().refine(
426
429
  (groups2) => !getDuplicateSlugsInGroups(groups2),
@@ -448,14 +451,14 @@ function getDuplicateSlugsInGroups(groups2) {
448
451
  }
449
452
 
450
453
  // packages/models/src/lib/runner-config.ts
451
- import { z as z10 } from "zod";
452
- var outputTransformSchema = z10.function().args(z10.unknown()).returns(z10.union([auditOutputsSchema, z10.promise(auditOutputsSchema)]));
453
- var runnerConfigSchema = z10.object(
454
+ import { z as z11 } from "zod";
455
+ var outputTransformSchema = z11.function().args(z11.unknown()).returns(z11.union([auditOutputsSchema, z11.promise(auditOutputsSchema)]));
456
+ var runnerConfigSchema = z11.object(
454
457
  {
455
- command: z10.string({
458
+ command: z11.string({
456
459
  description: "Shell command to execute"
457
460
  }),
458
- args: z10.array(z10.string({ description: "Command arguments" })).optional(),
461
+ args: z11.array(z11.string({ description: "Command arguments" })).optional(),
459
462
  outputFile: filePathSchema.describe("Output path"),
460
463
  outputTransform: outputTransformSchema.optional()
461
464
  },
@@ -463,8 +466,8 @@ var runnerConfigSchema = z10.object(
463
466
  description: "How to execute runner"
464
467
  }
465
468
  );
466
- var onProgressSchema = z10.function().args(z10.unknown()).returns(z10.void());
467
- var runnerFunctionSchema = z10.function().args(onProgressSchema.optional()).returns(z10.union([auditOutputsSchema, z10.promise(auditOutputsSchema)]));
469
+ var onProgressSchema = z11.function().args(z11.unknown()).returns(z11.void());
470
+ var runnerFunctionSchema = z11.function().args(onProgressSchema.optional()).returns(z11.union([auditOutputsSchema, z11.promise(auditOutputsSchema)]));
468
471
 
469
472
  // packages/models/src/lib/plugin-config.ts
470
473
  var pluginMetaSchema = packageVersionSchema().merge(
@@ -475,13 +478,13 @@ var pluginMetaSchema = packageVersionSchema().merge(
475
478
  description: "Plugin metadata"
476
479
  })
477
480
  ).merge(
478
- z11.object({
481
+ z12.object({
479
482
  slug: slugSchema.describe("Unique plugin slug within core config"),
480
483
  icon: materialIconSchema
481
484
  })
482
485
  );
483
- var pluginDataSchema = z11.object({
484
- runner: z11.union([runnerConfigSchema, runnerFunctionSchema]),
486
+ var pluginDataSchema = z12.object({
487
+ runner: z12.union([runnerConfigSchema, runnerFunctionSchema]),
485
488
  audits: pluginAuditsSchema,
486
489
  groups: groupsSchema
487
490
  });
@@ -507,22 +510,22 @@ function getMissingRefsFromGroups(pluginCfg) {
507
510
  }
508
511
 
509
512
  // packages/models/src/lib/upload-config.ts
510
- import { z as z12 } from "zod";
511
- var uploadConfigSchema = z12.object({
513
+ import { z as z13 } from "zod";
514
+ var uploadConfigSchema = z13.object({
512
515
  server: urlSchema.describe("URL of deployed portal API"),
513
- apiKey: z12.string({
516
+ apiKey: z13.string({
514
517
  description: "API key with write access to portal (use `process.env` for security)"
515
518
  }),
516
519
  organization: slugSchema.describe(
517
520
  "Organization slug from Code PushUp portal"
518
521
  ),
519
522
  project: slugSchema.describe("Project slug from Code PushUp portal"),
520
- timeout: z12.number({ description: "Request timeout in minutes (default is 5)" }).positive().int().optional()
523
+ timeout: z13.number({ description: "Request timeout in minutes (default is 5)" }).positive().int().optional()
521
524
  });
522
525
 
523
526
  // packages/models/src/lib/core-config.ts
524
- var unrefinedCoreConfigSchema = z13.object({
525
- plugins: z13.array(pluginConfigSchema, {
527
+ var unrefinedCoreConfigSchema = z14.object({
528
+ plugins: z14.array(pluginConfigSchema, {
526
529
  description: "List of plugins to be used (official, community-provided, or custom)"
527
530
  }).min(1),
528
531
  /** portal configuration for persisting results */
@@ -554,7 +557,7 @@ var DEFAULT_PERSIST_FILENAME = "report";
554
557
  var DEFAULT_PERSIST_FORMAT = ["json", "md"];
555
558
 
556
559
  // packages/models/src/lib/report.ts
557
- import { z as z14 } from "zod";
560
+ import { z as z15 } from "zod";
558
561
  var auditReportSchema = auditSchema.merge(auditOutputSchema);
559
562
  var pluginReportSchema = pluginMetaSchema.merge(
560
563
  executionMetaSchema({
@@ -562,9 +565,9 @@ var pluginReportSchema = pluginMetaSchema.merge(
562
565
  descriptionDuration: "Duration of the plugin run in ms"
563
566
  })
564
567
  ).merge(
565
- z14.object({
566
- audits: z14.array(auditReportSchema).min(1),
567
- groups: z14.array(groupSchema).optional()
568
+ z15.object({
569
+ audits: z15.array(auditReportSchema).min(1),
570
+ groups: z15.array(groupSchema).optional()
568
571
  })
569
572
  ).refine(
570
573
  (pluginReport) => !getMissingRefsFromGroups2(pluginReport.audits, pluginReport.groups ?? []),
@@ -598,10 +601,10 @@ var reportSchema = packageVersionSchema({
598
601
  descriptionDuration: "Duration of the collect run in ms"
599
602
  })
600
603
  ).merge(
601
- z14.object(
604
+ z15.object(
602
605
  {
603
- categories: z14.array(categoryConfigSchema),
604
- plugins: z14.array(pluginReportSchema).min(1),
606
+ categories: z15.array(categoryConfigSchema),
607
+ plugins: z15.array(pluginReportSchema).min(1),
605
608
  commit: commitSchema.describe("Git commit for which report was collected").nullable()
606
609
  },
607
610
  { description: "Collect output data" }
@@ -617,40 +620,40 @@ var reportSchema = packageVersionSchema({
617
620
  );
618
621
 
619
622
  // packages/models/src/lib/reports-diff.ts
620
- import { z as z15 } from "zod";
623
+ import { z as z16 } from "zod";
621
624
  function makeComparisonSchema(schema) {
622
625
  const sharedDescription = schema.description || "Result";
623
- return z15.object({
626
+ return z16.object({
624
627
  before: schema.describe(`${sharedDescription} (source commit)`),
625
628
  after: schema.describe(`${sharedDescription} (target commit)`)
626
629
  });
627
630
  }
628
631
  function makeArraysComparisonSchema(diffSchema, resultSchema, description) {
629
- return z15.object(
632
+ return z16.object(
630
633
  {
631
- changed: z15.array(diffSchema),
632
- unchanged: z15.array(resultSchema),
633
- added: z15.array(resultSchema),
634
- removed: z15.array(resultSchema)
634
+ changed: z16.array(diffSchema),
635
+ unchanged: z16.array(resultSchema),
636
+ added: z16.array(resultSchema),
637
+ removed: z16.array(resultSchema)
635
638
  },
636
639
  { description }
637
640
  );
638
641
  }
639
- var scorableMetaSchema = z15.object({
642
+ var scorableMetaSchema = z16.object({
640
643
  slug: slugSchema,
641
644
  title: titleSchema,
642
645
  docsUrl: docsUrlSchema
643
646
  });
644
647
  var scorableWithPluginMetaSchema = scorableMetaSchema.merge(
645
- z15.object({
648
+ z16.object({
646
649
  plugin: pluginMetaSchema.pick({ slug: true, title: true, docsUrl: true }).describe("Plugin which defines it")
647
650
  })
648
651
  );
649
652
  var scorableDiffSchema = scorableMetaSchema.merge(
650
- z15.object({
653
+ z16.object({
651
654
  scores: makeComparisonSchema(scoreSchema).merge(
652
- z15.object({
653
- diff: z15.number().min(-1).max(1).describe("Score change (`scores.after - scores.before`)")
655
+ z16.object({
656
+ diff: z16.number().min(-1).max(1).describe("Score change (`scores.after - scores.before`)")
654
657
  })
655
658
  ).describe("Score comparison")
656
659
  })
@@ -661,10 +664,10 @@ var scorableWithPluginDiffSchema = scorableDiffSchema.merge(
661
664
  var categoryDiffSchema = scorableDiffSchema;
662
665
  var groupDiffSchema = scorableWithPluginDiffSchema;
663
666
  var auditDiffSchema = scorableWithPluginDiffSchema.merge(
664
- z15.object({
667
+ z16.object({
665
668
  values: makeComparisonSchema(auditValueSchema).merge(
666
- z15.object({
667
- diff: z15.number().int().describe("Value change (`values.after - values.before`)")
669
+ z16.object({
670
+ diff: z16.number().int().describe("Value change (`values.after - values.before`)")
668
671
  })
669
672
  ).describe("Audit `value` comparison"),
670
673
  displayValues: makeComparisonSchema(auditDisplayValueSchema).describe(
@@ -673,18 +676,18 @@ var auditDiffSchema = scorableWithPluginDiffSchema.merge(
673
676
  })
674
677
  );
675
678
  var categoryResultSchema = scorableMetaSchema.merge(
676
- z15.object({ score: scoreSchema })
679
+ z16.object({ score: scoreSchema })
677
680
  );
678
681
  var groupResultSchema = scorableWithPluginMetaSchema.merge(
679
- z15.object({ score: scoreSchema })
682
+ z16.object({ score: scoreSchema })
680
683
  );
681
684
  var auditResultSchema = scorableWithPluginMetaSchema.merge(
682
685
  auditOutputSchema.pick({ score: true, value: true, displayValue: true })
683
686
  );
684
- var reportsDiffSchema = z15.object({
687
+ var reportsDiffSchema = z16.object({
685
688
  commits: makeComparisonSchema(commitSchema).nullable().describe("Commits identifying compared reports"),
686
689
  portalUrl: urlSchema.optional().describe("Link to comparison page in Code PushUp portal"),
687
- label: z15.string().optional().describe("Label (e.g. project name)"),
690
+ label: z16.string().optional().describe("Label (e.g. project name)"),
688
691
  categories: makeArraysComparisonSchema(
689
692
  categoryDiffSchema,
690
693
  categoryResultSchema,
@@ -857,9 +860,17 @@ function severityMarker(severity) {
857
860
  }
858
861
  return "\u2139\uFE0F";
859
862
  }
863
+ var MIN_NON_ZERO_RESULT = 0.1;
864
+ function roundValue(value) {
865
+ const roundedValue = Math.round(value * 10) / 10;
866
+ if (roundedValue === 0 && value !== 0) {
867
+ return MIN_NON_ZERO_RESULT * Math.sign(value);
868
+ }
869
+ return roundedValue;
870
+ }
860
871
  function formatScoreChange(diff) {
861
872
  const marker = getDiffMarker(diff);
862
- const text = formatDiffNumber(Math.round(diff * 1e3) / 10);
873
+ const text = formatDiffNumber(roundValue(diff * 100));
863
874
  return colorByScoreDiff(`${marker} ${text}`, diff);
864
875
  }
865
876
  function formatValueChange({
@@ -867,7 +878,7 @@ function formatValueChange({
867
878
  scores
868
879
  }) {
869
880
  const marker = getDiffMarker(values.diff);
870
- const percentage = values.before === 0 ? values.diff > 0 ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY : Math.round(100 * values.diff / values.before);
881
+ const percentage = values.before === 0 ? values.diff > 0 ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY : roundValue(values.diff / values.before * 100);
871
882
  const text = `${formatDiffNumber(percentage)}\u2009%`;
872
883
  return colorByScoreDiff(`${marker} ${text}`, scores.diff);
873
884
  }
@@ -1595,6 +1606,29 @@ import {
1595
1606
  MarkdownDocument,
1596
1607
  md as md2
1597
1608
  } from "build-md";
1609
+ import { posix as pathPosix } from "node:path";
1610
+
1611
+ // packages/utils/src/lib/reports/ide-environment.ts
1612
+ function getEnvironmentType() {
1613
+ if (isVSCode()) {
1614
+ return "vscode";
1615
+ }
1616
+ if (isGitHub()) {
1617
+ return "github";
1618
+ }
1619
+ return "other";
1620
+ }
1621
+ function isVSCode() {
1622
+ return process.env["TERM_PROGRAM"] === "vscode";
1623
+ }
1624
+ function isGitHub() {
1625
+ return process.env["GITHUB_ACTIONS"] === "true";
1626
+ }
1627
+ function getGitHubBaseUrl() {
1628
+ return `${process.env["GITHUB_SERVER_URL"]}/${process.env["GITHUB_REPOSITORY"]}/blob/${process.env["GITHUB_SHA"]}`;
1629
+ }
1630
+
1631
+ // packages/utils/src/lib/reports/formatting.ts
1598
1632
  function tableSection(tableData, options2) {
1599
1633
  if (tableData.rows.length === 0) {
1600
1634
  return null;
@@ -1632,6 +1666,44 @@ function metaDescription(audit) {
1632
1666
  }
1633
1667
  return "";
1634
1668
  }
1669
+ function linkToLocalSourceForIde(source, options2) {
1670
+ const { file, position } = source;
1671
+ const { outputDir } = options2 ?? {};
1672
+ if (!outputDir) {
1673
+ return md2.code(file);
1674
+ }
1675
+ return md2.link(formatFileLink(file, position, outputDir), md2.code(file));
1676
+ }
1677
+ function formatSourceLine(position) {
1678
+ if (!position) {
1679
+ return "";
1680
+ }
1681
+ const { startLine, endLine } = position;
1682
+ return endLine && startLine !== endLine ? `${startLine}-${endLine}` : `${startLine}`;
1683
+ }
1684
+ function formatGitHubLink(file, position) {
1685
+ const baseUrl = getGitHubBaseUrl();
1686
+ if (!position) {
1687
+ return `${baseUrl}/${file}`;
1688
+ }
1689
+ const { startLine, endLine, startColumn, endColumn } = position;
1690
+ const start = startColumn ? `L${startLine}C${startColumn}` : `L${startLine}`;
1691
+ const end = endLine ? endColumn ? `L${endLine}C${endColumn}` : `L${endLine}` : "";
1692
+ const lineRange = end && start !== end ? `${start}-${end}` : start;
1693
+ return `${baseUrl}/${file}#${lineRange}`;
1694
+ }
1695
+ function formatFileLink(file, position, outputDir) {
1696
+ const relativePath = pathPosix.relative(outputDir, file);
1697
+ const env = getEnvironmentType();
1698
+ switch (env) {
1699
+ case "vscode":
1700
+ return position ? `${relativePath}#L${position.startLine}` : relativePath;
1701
+ case "github":
1702
+ return formatGitHubLink(file, position);
1703
+ default:
1704
+ return relativePath;
1705
+ }
1706
+ }
1635
1707
 
1636
1708
  // packages/utils/src/lib/reports/generate-md-report-categoy-section.ts
1637
1709
  import { MarkdownDocument as MarkdownDocument2, md as md3 } from "build-md";
@@ -1833,16 +1905,16 @@ function auditDetailsAuditValue({
1833
1905
  String(displayValue ?? value)
1834
1906
  )} (score: ${formatReportScore(score)})`;
1835
1907
  }
1836
- function generateMdReport(report) {
1908
+ function generateMdReport(report, options2) {
1837
1909
  return new MarkdownDocument3().heading(HIERARCHY.level_1, REPORT_HEADLINE_TEXT).$if(
1838
1910
  report.categories.length > 0,
1839
1911
  (doc) => doc.$concat(
1840
1912
  categoriesOverviewSection(report),
1841
1913
  categoriesDetailsSection(report)
1842
1914
  )
1843
- ).$concat(auditsSection(report), aboutSection(report)).rule().paragraph(md4`${FOOTER_PREFIX} ${md4.link(README_LINK, "Code PushUp")}`).toString();
1915
+ ).$concat(auditsSection(report, options2), aboutSection(report)).rule().paragraph(md4`${FOOTER_PREFIX} ${md4.link(README_LINK, "Code PushUp")}`).toString();
1844
1916
  }
1845
- function auditDetailsIssues(issues = []) {
1917
+ function auditDetailsIssues(issues = [], options2) {
1846
1918
  if (issues.length === 0) {
1847
1919
  return null;
1848
1920
  }
@@ -1858,39 +1930,36 @@ function auditDetailsIssues(issues = []) {
1858
1930
  if (!source) {
1859
1931
  return [severity, message];
1860
1932
  }
1861
- const file = md4.code(source.file);
1933
+ const file = linkToLocalSourceForIde(source, options2);
1862
1934
  if (!source.position) {
1863
1935
  return [severity, message, file];
1864
1936
  }
1865
- const { startLine, endLine } = source.position;
1866
- const line = `${startLine || ""}${endLine && startLine !== endLine ? `-${endLine}` : ""}`;
1937
+ const line = formatSourceLine(source.position);
1867
1938
  return [severity, message, file, line];
1868
1939
  })
1869
1940
  );
1870
1941
  }
1871
- function auditDetails(audit) {
1942
+ function auditDetails(audit, options2) {
1872
1943
  const { table: table2, issues = [] } = audit.details ?? {};
1873
1944
  const detailsValue = auditDetailsAuditValue(audit);
1874
1945
  if (issues.length === 0 && !table2?.rows.length) {
1875
1946
  return new MarkdownDocument3().paragraph(detailsValue);
1876
1947
  }
1877
1948
  const tableSectionContent = table2 && tableSection(table2);
1878
- const issuesSectionContent = issues.length > 0 && auditDetailsIssues(issues);
1949
+ const issuesSectionContent = issues.length > 0 && auditDetailsIssues(issues, options2);
1879
1950
  return new MarkdownDocument3().details(
1880
1951
  detailsValue,
1881
1952
  new MarkdownDocument3().$concat(tableSectionContent, issuesSectionContent)
1882
1953
  );
1883
1954
  }
1884
- function auditsSection({
1885
- plugins
1886
- }) {
1955
+ function auditsSection({ plugins }, options2) {
1887
1956
  return new MarkdownDocument3().heading(HIERARCHY.level_2, "\u{1F6E1}\uFE0F Audits").$foreach(
1888
1957
  plugins.flatMap(
1889
1958
  (plugin) => plugin.audits.map((audit) => ({ ...audit, plugin }))
1890
1959
  ),
1891
1960
  (doc, { plugin, ...audit }) => {
1892
1961
  const auditTitle = `${audit.title} (${plugin.title})`;
1893
- const detailsContent = auditDetails(audit);
1962
+ const detailsContent = auditDetails(audit, options2);
1894
1963
  const descriptionContent = metaDescription(audit);
1895
1964
  return doc.heading(HIERARCHY.level_3, auditTitle).$concat(detailsContent).paragraph(descriptionContent);
1896
1965
  }
@@ -2152,15 +2221,6 @@ function createDiffCategoriesSection(diff, options2) {
2152
2221
  function createCategoriesTable(diff, options2) {
2153
2222
  const { changed, unchanged, added } = diff.categories;
2154
2223
  const { hasChanges, skipUnchanged } = options2;
2155
- const columns = [
2156
- { heading: "\u{1F3F7}\uFE0F Category", alignment: "left" },
2157
- {
2158
- heading: hasChanges ? "\u2B50 Previous score" : "\u2B50 Score",
2159
- alignment: "center"
2160
- },
2161
- { heading: "\u2B50 Current score", alignment: "center" },
2162
- { heading: "\u{1F504} Score change", alignment: "center" }
2163
- ];
2164
2224
  const rows = [
2165
2225
  ...sortChanges(changed).map((category) => [
2166
2226
  formatTitle(category),
@@ -2183,6 +2243,18 @@ function createCategoriesTable(diff, options2) {
2183
2243
  "\u2013"
2184
2244
  ])
2185
2245
  ];
2246
+ if (rows.length === 0) {
2247
+ return [[], []];
2248
+ }
2249
+ const columns = [
2250
+ { heading: "\u{1F3F7}\uFE0F Category", alignment: "left" },
2251
+ {
2252
+ heading: hasChanges ? "\u2B50 Previous score" : "\u2B50 Score",
2253
+ alignment: "center"
2254
+ },
2255
+ { heading: "\u2B50 Current score", alignment: "center" },
2256
+ { heading: "\u{1F504} Score change", alignment: "center" }
2257
+ ];
2186
2258
  return [
2187
2259
  hasChanges ? columns : columns.slice(0, 2),
2188
2260
  rows.map((row) => hasChanges ? row : row.slice(0, 2))
@@ -2463,7 +2535,7 @@ var verboseUtils = (verbose = false) => ({
2463
2535
 
2464
2536
  // packages/core/package.json
2465
2537
  var name = "@code-pushup/core";
2466
- var version = "0.50.0";
2538
+ var version = "0.51.0";
2467
2539
 
2468
2540
  // packages/core/src/lib/implementation/execute-plugin.ts
2469
2541
  import { bold as bold5 } from "ansis";
@@ -2672,7 +2744,7 @@ async function persistReport(report, options2) {
2672
2744
  case "md":
2673
2745
  return {
2674
2746
  format: "md",
2675
- content: generateMdReport(sortedScoredReport)
2747
+ content: generateMdReport(sortedScoredReport, { outputDir })
2676
2748
  };
2677
2749
  }
2678
2750
  });
@@ -3428,7 +3500,8 @@ var onlyPluginsOption = {
3428
3500
  describe: "List of plugins to run. If not set all plugins are run.",
3429
3501
  type: "array",
3430
3502
  default: [],
3431
- coerce: coerceArray
3503
+ coerce: coerceArray,
3504
+ alias: "p"
3432
3505
  };
3433
3506
  function yargsOnlyPluginsOptionsDefinition() {
3434
3507
  return {
@@ -3441,7 +3514,8 @@ var skipPluginsOption = {
3441
3514
  describe: "List of plugins to skip. If not set all plugins are run.",
3442
3515
  type: "array",
3443
3516
  default: [],
3444
- coerce: coerceArray
3517
+ coerce: coerceArray,
3518
+ alias: "P"
3445
3519
  };
3446
3520
  function yargsSkipPluginsOptionsDefinition() {
3447
3521
  return {
@@ -3692,7 +3766,6 @@ async function coreConfigMiddleware(processArgs) {
3692
3766
  var normalizeFormats = (formats) => (formats ?? []).flatMap((format) => format.split(","));
3693
3767
 
3694
3768
  // packages/cli/src/lib/implementation/validate-plugin-filter-options.utils.ts
3695
- import { yellow } from "ansis";
3696
3769
  function validatePluginFilterOption(filterOption, {
3697
3770
  plugins,
3698
3771
  categories
@@ -3706,13 +3779,9 @@ function validatePluginFilterOption(filterOption, {
3706
3779
  );
3707
3780
  const isSkipOption = filterOption === "skipPlugins";
3708
3781
  const filterFunction = (plugin) => isSkipOption ? pluginsToFilterSet.has(plugin) : !pluginsToFilterSet.has(plugin);
3709
- if (missingPlugins.length > 0 && verbose) {
3710
- ui().logger.info(
3711
- `${yellow(
3712
- "\u26A0"
3713
- )} The --${filterOption} argument references plugins with "${missingPlugins.join(
3714
- '", "'
3715
- )}" slugs, but no such plugins are present in the configuration. Expected one of the following plugin slugs: "${plugins.map(({ slug }) => slug).join('", "')}".`
3782
+ if (missingPlugins.length > 0) {
3783
+ ui().logger.warning(
3784
+ `The --${filterOption} argument references ${missingPlugins.length === 1 ? "a plugin that does" : "plugins that do"} not exist: ${missingPlugins.join(", ")}. The valid plugin ${plugins.length === 1 ? "slug is" : "slugs are"} ${plugins.map(({ slug }) => slug).join(", ")}.`
3716
3785
  );
3717
3786
  }
3718
3787
  if (categories.length > 0 && verbose) {
@@ -3721,71 +3790,53 @@ function validatePluginFilterOption(filterOption, {
3721
3790
  ({ plugin }) => filterFunction(plugin)
3722
3791
  ).map(({ slug }) => slug);
3723
3792
  ui().logger.info(
3724
- `The --${filterOption} argument removed categories with "${removedCategorySlugs.join(
3725
- '", "'
3726
- )}" slugs.
3727
- `
3793
+ `The --${filterOption} argument removed the following categories: ${removedCategorySlugs.join(
3794
+ ", "
3795
+ )}.`
3728
3796
  );
3729
3797
  }
3730
3798
  }
3731
3799
 
3732
- // packages/cli/src/lib/implementation/only-plugins.middleware.ts
3733
- function onlyPluginsMiddleware(originalProcessArgs) {
3734
- const { categories = [], onlyPlugins: originalOnlyPlugins } = originalProcessArgs;
3735
- if (originalOnlyPlugins && originalOnlyPlugins.length > 0) {
3736
- const { verbose, plugins, onlyPlugins = [] } = originalProcessArgs;
3737
- validatePluginFilterOption(
3738
- "onlyPlugins",
3739
- { plugins, categories },
3740
- { pluginsToFilter: onlyPlugins, verbose }
3741
- );
3742
- const validOnlyPlugins = onlyPlugins.filter(
3743
- (oP) => plugins.find((p) => p.slug === oP)
3744
- );
3745
- const onlyPluginsSet = new Set(validOnlyPlugins);
3746
- return {
3747
- ...originalProcessArgs,
3748
- plugins: onlyPluginsSet.size > 0 ? plugins.filter(({ slug }) => onlyPluginsSet.has(slug)) : plugins,
3749
- categories: onlyPluginsSet.size > 0 ? filterItemRefsBy(
3750
- categories,
3751
- ({ plugin }) => onlyPluginsSet.has(plugin)
3752
- ) : categories
3753
- };
3754
- }
3755
- return {
3756
- ...originalProcessArgs,
3757
- // if undefined fill categories with empty array
3758
- categories
3759
- };
3760
- }
3761
-
3762
- // packages/cli/src/lib/implementation/skip-plugins.middleware.ts
3763
- function skipPluginsMiddleware(originalProcessArgs) {
3764
- const { categories = [], skipPlugins: originalSkipPlugins } = originalProcessArgs;
3765
- if (originalSkipPlugins && originalSkipPlugins.length > 0) {
3766
- const { verbose, plugins, skipPlugins = [] } = originalProcessArgs;
3767
- validatePluginFilterOption(
3768
- "skipPlugins",
3769
- { plugins, categories },
3770
- { pluginsToFilter: skipPlugins, verbose }
3771
- );
3772
- const validSkipPlugins = skipPlugins.filter(
3773
- (sP) => plugins.find((p) => p.slug === sP)
3774
- );
3775
- const skipPluginsSet = new Set(validSkipPlugins);
3776
- return {
3777
- ...originalProcessArgs,
3778
- plugins: skipPluginsSet.size > 0 ? plugins.filter(({ slug }) => !skipPluginsSet.has(slug)) : plugins,
3779
- categories: skipPluginsSet.size > 0 ? filterItemRefsBy(
3780
- categories,
3781
- ({ plugin }) => !skipPluginsSet.has(plugin)
3782
- ) : categories
3783
- };
3800
+ // packages/cli/src/lib/implementation/filter-plugins.middleware.ts
3801
+ function filterPluginsMiddleware(originalProcessArgs) {
3802
+ const {
3803
+ plugins,
3804
+ categories = [],
3805
+ skipPlugins = [],
3806
+ onlyPlugins = [],
3807
+ verbose
3808
+ } = originalProcessArgs;
3809
+ if (skipPlugins.length === 0 && onlyPlugins.length === 0) {
3810
+ return { ...originalProcessArgs, categories };
3784
3811
  }
3812
+ validatePluginFilterOption(
3813
+ "skipPlugins",
3814
+ { plugins, categories },
3815
+ { pluginsToFilter: skipPlugins, verbose }
3816
+ );
3817
+ validatePluginFilterOption(
3818
+ "onlyPlugins",
3819
+ { plugins, categories },
3820
+ { pluginsToFilter: onlyPlugins, verbose }
3821
+ );
3822
+ const validSkipPlugins = new Set(
3823
+ skipPlugins.filter((sP) => plugins.some((p) => p.slug === sP))
3824
+ );
3825
+ const pluginsAfterSkip = plugins.filter(
3826
+ ({ slug }) => !validSkipPlugins.has(slug)
3827
+ );
3828
+ const validOnlyPlugins = new Set(
3829
+ onlyPlugins.filter((oP) => pluginsAfterSkip.some((p) => p.slug === oP))
3830
+ );
3831
+ const filteredPlugins = validOnlyPlugins.size > 0 ? pluginsAfterSkip.filter(({ slug }) => validOnlyPlugins.has(slug)) : pluginsAfterSkip;
3832
+ const filteredCategories = filteredPlugins.length > 0 ? filterItemRefsBy(
3833
+ categories,
3834
+ ({ plugin }) => filteredPlugins.some(({ slug }) => slug === plugin)
3835
+ ) : categories;
3785
3836
  return {
3786
3837
  ...originalProcessArgs,
3787
- // if undefined fill categories with empty array
3788
- categories
3838
+ plugins: filteredPlugins,
3839
+ categories: filteredCategories
3789
3840
  };
3790
3841
  }
3791
3842
 
@@ -3796,11 +3847,7 @@ var middlewares = [
3796
3847
  applyBeforeValidation: false
3797
3848
  },
3798
3849
  {
3799
- middlewareFunction: onlyPluginsMiddleware,
3800
- applyBeforeValidation: false
3801
- },
3802
- {
3803
- middlewareFunction: skipPluginsMiddleware,
3850
+ middlewareFunction: filterPluginsMiddleware,
3804
3851
  applyBeforeValidation: false
3805
3852
  }
3806
3853
  ];
@@ -3895,7 +3942,7 @@ import { blue, dim as dim2, green as green4 } from "ansis";
3895
3942
  import yargs from "yargs";
3896
3943
 
3897
3944
  // packages/cli/package.json
3898
- var version2 = "0.50.0";
3945
+ var version2 = "0.51.0";
3899
3946
 
3900
3947
  // packages/cli/src/lib/implementation/formatting.ts
3901
3948
  import { bold as bold13, dim, green as green3 } from "ansis";
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@code-pushup/cli",
3
- "version": "0.50.0",
3
+ "version": "0.51.0",
4
4
  "license": "MIT",
5
5
  "description": "A CLI to run all kinds of code quality measurements to align your team with company goals",
6
6
  "bin": {
7
7
  "code-pushup": "index.js"
8
8
  },
9
9
  "dependencies": {
10
- "@code-pushup/models": "0.50.0",
11
- "@code-pushup/core": "0.50.0",
12
- "@code-pushup/utils": "0.50.0",
10
+ "@code-pushup/models": "0.51.0",
11
+ "@code-pushup/core": "0.51.0",
12
+ "@code-pushup/utils": "0.51.0",
13
13
  "yargs": "^17.7.2",
14
14
  "ansis": "^3.3.0",
15
15
  "simple-git": "^3.20.0"
@@ -0,0 +1,3 @@
1
+ import type { OnlyPluginsOptions } from './only-plugins.model';
2
+ import type { SkipPluginsOptions } from './skip-plugins.model';
3
+ export declare function filterPluginsMiddleware<T extends SkipPluginsOptions & OnlyPluginsOptions>(originalProcessArgs: T): T;
@@ -1,2 +0,0 @@
1
- import type { OnlyPluginsOptions } from './only-plugins.model';
2
- export declare function onlyPluginsMiddleware<T extends OnlyPluginsOptions>(originalProcessArgs: T): T;
@@ -1,2 +0,0 @@
1
- import type { SkipPluginsOptions } from './skip-plugins.model';
2
- export declare function skipPluginsMiddleware<T extends SkipPluginsOptions>(originalProcessArgs: T): T;