@code-pushup/cli 0.50.0 → 0.52.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))
@@ -2273,11 +2345,11 @@ import { bold as bold4, cyan, cyanBright, green as green2, red } from "ansis";
2273
2345
  function log(msg = "") {
2274
2346
  ui().logger.log(msg);
2275
2347
  }
2276
- function logStdoutSummary(report) {
2348
+ function logStdoutSummary(report, verbose = false) {
2277
2349
  const printCategories = report.categories.length > 0;
2278
2350
  log(reportToHeaderSection(report));
2279
2351
  log();
2280
- logPlugins(report);
2352
+ logPlugins(report.plugins, verbose);
2281
2353
  if (printCategories) {
2282
2354
  logCategories(report);
2283
2355
  }
@@ -2288,36 +2360,49 @@ function reportToHeaderSection(report) {
2288
2360
  const { packageName, version: version3 } = report;
2289
2361
  return `${bold4(REPORT_HEADLINE_TEXT)} - ${packageName}@${version3}`;
2290
2362
  }
2291
- function logPlugins(report) {
2292
- const { plugins } = report;
2363
+ function logPlugins(plugins, verbose) {
2293
2364
  plugins.forEach((plugin) => {
2294
2365
  const { title, audits } = plugin;
2366
+ const filteredAudits = verbose ? audits : audits.filter(({ score }) => score !== 1);
2367
+ const diff = audits.length - filteredAudits.length;
2368
+ logAudits(title, filteredAudits);
2369
+ if (diff > 0) {
2370
+ const notice = filteredAudits.length === 0 ? `... All ${diff} audits have perfect scores ...` : `... ${diff} audits with perfect scores omitted for brevity ...`;
2371
+ logRow(1, notice);
2372
+ }
2295
2373
  log();
2296
- log(bold4.magentaBright(`${title} audits`));
2297
- log();
2298
- audits.forEach((audit) => {
2299
- ui().row([
2300
- {
2301
- text: applyScoreColor({ score: audit.score, text: "\u25CF" }),
2302
- width: 2,
2303
- padding: [0, 1, 0, 0]
2304
- },
2305
- {
2306
- text: audit.title,
2307
- // eslint-disable-next-line no-magic-numbers
2308
- padding: [0, 3, 0, 0]
2309
- },
2310
- {
2311
- text: cyanBright(audit.displayValue || `${audit.value}`),
2312
- // eslint-disable-next-line no-magic-numbers
2313
- width: 20,
2314
- padding: [0, 0, 0, 0]
2315
- }
2316
- ]);
2317
- });
2318
- log();
2319
2374
  });
2320
2375
  }
2376
+ function logAudits(pluginTitle, audits) {
2377
+ log();
2378
+ log(bold4.magentaBright(`${pluginTitle} audits`));
2379
+ log();
2380
+ audits.forEach(({ score, title, displayValue, value }) => {
2381
+ logRow(score, title, displayValue || `${value}`);
2382
+ });
2383
+ }
2384
+ function logRow(score, title, value) {
2385
+ ui().row([
2386
+ {
2387
+ text: applyScoreColor({ score, text: "\u25CF" }),
2388
+ width: 2,
2389
+ padding: [0, 1, 0, 0]
2390
+ },
2391
+ {
2392
+ text: title,
2393
+ // eslint-disable-next-line no-magic-numbers
2394
+ padding: [0, 3, 0, 0]
2395
+ },
2396
+ ...value ? [
2397
+ {
2398
+ text: cyanBright(value),
2399
+ // eslint-disable-next-line no-magic-numbers
2400
+ width: 20,
2401
+ padding: [0, 0, 0, 0]
2402
+ }
2403
+ ] : []
2404
+ ]);
2405
+ }
2321
2406
  function logCategories({ categories, plugins }) {
2322
2407
  const hAlign = (idx) => idx === 0 ? "left" : "right";
2323
2408
  const rows = categories.map(({ title, score, refs, isBinary }) => [
@@ -2463,7 +2548,7 @@ var verboseUtils = (verbose = false) => ({
2463
2548
 
2464
2549
  // packages/core/package.json
2465
2550
  var name = "@code-pushup/core";
2466
- var version = "0.50.0";
2551
+ var version = "0.52.0";
2467
2552
 
2468
2553
  // packages/core/src/lib/implementation/execute-plugin.ts
2469
2554
  import { bold as bold5 } from "ansis";
@@ -2658,10 +2743,8 @@ var PersistError = class extends Error {
2658
2743
  super(`fileName: ${reportPath} could not be saved.`);
2659
2744
  }
2660
2745
  };
2661
- async function persistReport(report, options2) {
2746
+ async function persistReport(report, sortedScoredReport, options2) {
2662
2747
  const { outputDir, filename, format } = options2;
2663
- const sortedScoredReport = sortReport(scoreReport(report));
2664
- logStdoutSummary(sortedScoredReport);
2665
2748
  const results = format.map((reportType) => {
2666
2749
  switch (reportType) {
2667
2750
  case "json":
@@ -2672,7 +2755,7 @@ async function persistReport(report, options2) {
2672
2755
  case "md":
2673
2756
  return {
2674
2757
  format: "md",
2675
- content: generateMdReport(sortedScoredReport)
2758
+ content: generateMdReport(sortedScoredReport, { outputDir })
2676
2759
  };
2677
2760
  }
2678
2761
  });
@@ -2707,7 +2790,13 @@ function logPersistedResults(persistResults) {
2707
2790
  async function collectAndPersistReports(options2) {
2708
2791
  const { exec } = verboseUtils(options2.verbose);
2709
2792
  const report = await collect(options2);
2710
- const persistResults = await persistReport(report, options2.persist);
2793
+ const sortedScoredReport = sortReport(scoreReport(report));
2794
+ const persistResults = await persistReport(
2795
+ report,
2796
+ sortedScoredReport,
2797
+ options2.persist
2798
+ );
2799
+ logStdoutSummary(sortedScoredReport, options2.verbose);
2711
2800
  exec(() => {
2712
2801
  logPersistedResults(persistResults);
2713
2802
  });
@@ -2719,10 +2808,6 @@ async function collectAndPersistReports(options2) {
2719
2808
  // packages/core/src/lib/compare.ts
2720
2809
  import { writeFile as writeFile2 } from "node:fs/promises";
2721
2810
  import { join as join5 } from "node:path";
2722
- import {
2723
- PortalOperationError,
2724
- getPortalComparisonLink
2725
- } from "@code-pushup/portal-client";
2726
2811
 
2727
2812
  // packages/core/src/lib/implementation/compare-scorables.ts
2728
2813
  function compareCategories(reports) {
@@ -2858,6 +2943,18 @@ function selectMeta(meta) {
2858
2943
  };
2859
2944
  }
2860
2945
 
2946
+ // packages/core/src/lib/load-portal-client.ts
2947
+ async function loadPortalClient() {
2948
+ try {
2949
+ return await import("@code-pushup/portal-client");
2950
+ } catch {
2951
+ ui().logger.error(
2952
+ "Optional peer dependency @code-pushup/portal-client is not available. Make sure it is installed to enable upload functionality."
2953
+ );
2954
+ return null;
2955
+ }
2956
+ }
2957
+
2861
2958
  // packages/core/src/lib/compare.ts
2862
2959
  async function compareReportFiles(inputPaths, persistConfig, uploadConfig, label) {
2863
2960
  const { outputDir, filename, format } = persistConfig;
@@ -2922,6 +3019,11 @@ function reportsDiffToFileContent(reportsDiff, format) {
2922
3019
  }
2923
3020
  async function fetchPortalComparisonLink(uploadConfig, commits) {
2924
3021
  const { server, apiKey, organization, project } = uploadConfig;
3022
+ const portalClient = await loadPortalClient();
3023
+ if (!portalClient) {
3024
+ return;
3025
+ }
3026
+ const { PortalOperationError, getPortalComparisonLink } = portalClient;
2925
3027
  try {
2926
3028
  return await getPortalComparisonLink({
2927
3029
  server,
@@ -2944,11 +3046,6 @@ async function fetchPortalComparisonLink(uploadConfig, commits) {
2944
3046
  }
2945
3047
  }
2946
3048
 
2947
- // packages/core/src/lib/upload.ts
2948
- import {
2949
- uploadToPortal
2950
- } from "@code-pushup/portal-client";
2951
-
2952
3049
  // packages/core/src/lib/implementation/report-to-gql.ts
2953
3050
  import {
2954
3051
  CategoryConfigRefType as PortalCategoryRefType,
@@ -3095,10 +3192,15 @@ function tableAlignmentToGQL(alignment) {
3095
3192
  }
3096
3193
 
3097
3194
  // packages/core/src/lib/upload.ts
3098
- async function upload(options2, uploadFn = uploadToPortal) {
3195
+ async function upload(options2) {
3099
3196
  if (options2.upload == null) {
3100
3197
  throw new Error("Upload configuration is not set.");
3101
3198
  }
3199
+ const portalClient = await loadPortalClient();
3200
+ if (!portalClient) {
3201
+ return;
3202
+ }
3203
+ const { uploadToPortal } = portalClient;
3102
3204
  const { apiKey, server, organization, project, timeout } = options2.upload;
3103
3205
  const report = await loadReport({
3104
3206
  ...options2.persist,
@@ -3113,7 +3215,7 @@ async function upload(options2, uploadFn = uploadToPortal) {
3113
3215
  commit: report.commit.hash,
3114
3216
  ...reportToGQL(report)
3115
3217
  };
3116
- return uploadFn({ apiKey, server, data, timeout });
3218
+ return uploadToPortal({ apiKey, server, data, timeout });
3117
3219
  }
3118
3220
 
3119
3221
  // packages/core/src/lib/history.ts
@@ -3296,8 +3398,10 @@ function yargsAutorunCommandObject() {
3296
3398
  renderConfigureCategoriesHint();
3297
3399
  }
3298
3400
  if (options2.upload) {
3299
- const { url } = await upload(options2);
3300
- uploadSuccessfulLog(url);
3401
+ const report = await upload(options2);
3402
+ if (report?.url) {
3403
+ uploadSuccessfulLog(report.url);
3404
+ }
3301
3405
  } else {
3302
3406
  ui().logger.warning("Upload skipped because configuration is not set.");
3303
3407
  renderIntegratePortalHint();
@@ -3398,6 +3502,94 @@ function yargsCompareCommandObject() {
3398
3502
  // packages/cli/src/lib/history/history-command.ts
3399
3503
  import { bold as bold10, gray as gray7 } from "ansis";
3400
3504
 
3505
+ // packages/cli/src/lib/implementation/global.utils.ts
3506
+ import yargs from "yargs";
3507
+
3508
+ // packages/cli/src/lib/implementation/validate-filter-options.utils.ts
3509
+ var OptionValidationError = class extends Error {
3510
+ };
3511
+ function validateFilterOption(option, { plugins, categories }, { itemsToFilter, verbose }) {
3512
+ const itemsToFilterSet = new Set(itemsToFilter);
3513
+ const validItems = isCategoryOption(option) ? categories : isPluginOption(option) ? plugins : [];
3514
+ const invalidItems = itemsToFilter.filter(
3515
+ (item) => !validItems.some(({ slug }) => slug === item)
3516
+ );
3517
+ const message = createValidationMessage(option, invalidItems, validItems);
3518
+ if (isOnlyOption(option) && itemsToFilterSet.size > 0 && itemsToFilterSet.size === invalidItems.length) {
3519
+ throw new OptionValidationError(message);
3520
+ }
3521
+ if (invalidItems.length > 0) {
3522
+ ui().logger.warning(message);
3523
+ }
3524
+ if (isPluginOption(option) && categories.length > 0 && verbose) {
3525
+ const removedCategorySlugs = filterItemRefsBy(
3526
+ categories,
3527
+ ({ plugin }) => isOnlyOption(option) ? !itemsToFilterSet.has(plugin) : itemsToFilterSet.has(plugin)
3528
+ ).map(({ slug }) => slug);
3529
+ if (removedCategorySlugs.length > 0) {
3530
+ ui().logger.info(
3531
+ `The --${option} argument removed the following categories: ${removedCategorySlugs.join(
3532
+ ", "
3533
+ )}.`
3534
+ );
3535
+ }
3536
+ }
3537
+ }
3538
+ function validateFinalState(filteredItems, originalItems) {
3539
+ const { categories: filteredCategories, plugins: filteredPlugins } = filteredItems;
3540
+ const { categories: originalCategories, plugins: originalPlugins } = originalItems;
3541
+ if (filteredCategories.length === 0 && filteredPlugins.length === 0 && (originalPlugins.length > 0 || originalCategories.length > 0)) {
3542
+ const availablePlugins = originalPlugins.map((p) => p.slug).join(", ") || "none";
3543
+ const availableCategories = originalCategories.map((c) => c.slug).join(", ") || "none";
3544
+ throw new OptionValidationError(
3545
+ `Nothing to report. No plugins or categories are available after filtering. Available plugins: ${availablePlugins}. Available categories: ${availableCategories}.`
3546
+ );
3547
+ }
3548
+ }
3549
+ function isCategoryOption(option) {
3550
+ return option.endsWith("Categories");
3551
+ }
3552
+ function isPluginOption(option) {
3553
+ return option.endsWith("Plugins");
3554
+ }
3555
+ function isOnlyOption(option) {
3556
+ return option.startsWith("only");
3557
+ }
3558
+ function getItemType(option, count) {
3559
+ const itemType = isCategoryOption(option) ? "category" : isPluginOption(option) ? "plugin" : "item";
3560
+ return pluralize(itemType, count);
3561
+ }
3562
+ function createValidationMessage(option, invalidItems, validItems) {
3563
+ const invalidItem = getItemType(option, invalidItems.length);
3564
+ const invalidItemText = invalidItems.length === 1 ? `a ${invalidItem} that does not exist:` : `${invalidItem} that do not exist:`;
3565
+ const invalidSlugs = invalidItems.join(", ");
3566
+ const validItem = getItemType(option, validItems.length);
3567
+ const validItemText = validItems.length === 1 ? `The only valid ${validItem} is` : `Valid ${validItem} are`;
3568
+ const validSlugs = validItems.map(({ slug }) => slug).join(", ");
3569
+ return `The --${option} argument references ${invalidItemText} ${invalidSlugs}. ${validItemText} ${validSlugs}.`;
3570
+ }
3571
+ function handleConflictingOptions(type, onlyItems, skipItems) {
3572
+ const conflictingItems = onlyItems.filter((item) => skipItems.includes(item));
3573
+ if (conflictingItems.length > 0) {
3574
+ const conflictSubject = () => {
3575
+ switch (type) {
3576
+ case "categories":
3577
+ return conflictingItems.length > 1 ? "categories are" : "category is";
3578
+ case "plugins":
3579
+ return conflictingItems.length > 1 ? "plugins are" : "plugin is";
3580
+ }
3581
+ };
3582
+ const conflictingSlugs = conflictingItems.join(", ");
3583
+ throw new OptionValidationError(
3584
+ `The following ${conflictSubject()} specified in both --only${capitalize(
3585
+ type
3586
+ )} and --skip${capitalize(
3587
+ type
3588
+ )}: ${conflictingSlugs}. Please choose one option.`
3589
+ );
3590
+ }
3591
+ }
3592
+
3401
3593
  // packages/cli/src/lib/implementation/global.utils.ts
3402
3594
  function filterKebabCaseKeys(obj) {
3403
3595
  return Object.entries(obj).filter(([key]) => !key.includes("-")).reduce(
@@ -3413,9 +3605,15 @@ function logErrorBeforeThrow(fn) {
3413
3605
  try {
3414
3606
  return await fn(...args);
3415
3607
  } catch (error) {
3416
- console.error(error);
3417
- await new Promise((resolve) => process.stdout.write("", resolve));
3418
- throw error;
3608
+ if (error instanceof OptionValidationError) {
3609
+ ui().logger.error(error.message);
3610
+ await new Promise((resolve) => process.stdout.write("", resolve));
3611
+ yargs().exit(1, error);
3612
+ } else {
3613
+ console.error(error);
3614
+ await new Promise((resolve) => process.stdout.write("", resolve));
3615
+ throw error;
3616
+ }
3419
3617
  }
3420
3618
  };
3421
3619
  }
@@ -3423,29 +3621,39 @@ function coerceArray(param) {
3423
3621
  return [...new Set(toArray(param).flatMap((f) => f.split(",")))];
3424
3622
  }
3425
3623
 
3426
- // packages/cli/src/lib/implementation/only-plugins.options.ts
3427
- var onlyPluginsOption = {
3428
- describe: "List of plugins to run. If not set all plugins are run.",
3624
+ // packages/cli/src/lib/implementation/filter.options.ts
3625
+ var skipCategoriesOption = {
3626
+ describe: "List of categories to skip. If not set all categories are run.",
3627
+ type: "array",
3628
+ default: [],
3629
+ coerce: coerceArray
3630
+ };
3631
+ var onlyCategoriesOption = {
3632
+ describe: "List of categories to run. If not set all categories are run.",
3429
3633
  type: "array",
3430
3634
  default: [],
3431
3635
  coerce: coerceArray
3432
3636
  };
3433
- function yargsOnlyPluginsOptionsDefinition() {
3434
- return {
3435
- onlyPlugins: onlyPluginsOption
3436
- };
3437
- }
3438
-
3439
- // packages/cli/src/lib/implementation/skip-plugins.options.ts
3440
3637
  var skipPluginsOption = {
3441
3638
  describe: "List of plugins to skip. If not set all plugins are run.",
3442
3639
  type: "array",
3443
3640
  default: [],
3444
- coerce: coerceArray
3641
+ coerce: coerceArray,
3642
+ alias: "P"
3445
3643
  };
3446
- function yargsSkipPluginsOptionsDefinition() {
3644
+ var onlyPluginsOption = {
3645
+ describe: "List of plugins to run. If not set all plugins are run.",
3646
+ type: "array",
3647
+ default: [],
3648
+ coerce: coerceArray,
3649
+ alias: "p"
3650
+ };
3651
+ function yargsFilterOptionsDefinition() {
3447
3652
  return {
3448
- skipPlugins: skipPluginsOption
3653
+ skipCategories: skipCategoriesOption,
3654
+ onlyCategories: onlyCategoriesOption,
3655
+ skipPlugins: skipPluginsOption,
3656
+ onlyPlugins: onlyPluginsOption
3449
3657
  };
3450
3658
  }
3451
3659
 
@@ -3556,17 +3764,16 @@ function yargsHistoryCommandObject() {
3556
3764
  return {
3557
3765
  command,
3558
3766
  describe: "Collect reports for commit history",
3559
- builder: (yargs2) => {
3560
- yargs2.options({
3767
+ builder: (yargs3) => {
3768
+ yargs3.options({
3561
3769
  ...yargsHistoryOptionsDefinition(),
3562
- ...yargsOnlyPluginsOptionsDefinition(),
3563
- ...yargsSkipPluginsOptionsDefinition()
3770
+ ...yargsFilterOptionsDefinition()
3564
3771
  });
3565
- yargs2.group(
3772
+ yargs3.group(
3566
3773
  Object.keys(yargsHistoryOptionsDefinition()),
3567
3774
  "History Options:"
3568
3775
  );
3569
- return yargs2;
3776
+ return yargs3;
3570
3777
  },
3571
3778
  handler
3572
3779
  };
@@ -3633,8 +3840,10 @@ function yargsUploadCommandObject() {
3633
3840
  renderIntegratePortalHint();
3634
3841
  throw new Error("Upload configuration not set");
3635
3842
  }
3636
- const { url } = await upload(options2);
3637
- uploadSuccessfulLog(url);
3843
+ const report = await upload(options2);
3844
+ if (report?.url) {
3845
+ uploadSuccessfulLog(report.url);
3846
+ }
3638
3847
  }
3639
3848
  };
3640
3849
  }
@@ -3691,102 +3900,98 @@ async function coreConfigMiddleware(processArgs) {
3691
3900
  }
3692
3901
  var normalizeFormats = (formats) => (formats ?? []).flatMap((format) => format.split(","));
3693
3902
 
3694
- // packages/cli/src/lib/implementation/validate-plugin-filter-options.utils.ts
3695
- import { yellow } from "ansis";
3696
- function validatePluginFilterOption(filterOption, {
3697
- plugins,
3698
- categories
3699
- }, {
3700
- pluginsToFilter = [],
3701
- verbose = false
3702
- } = {}) {
3703
- const pluginsToFilterSet = new Set(pluginsToFilter);
3704
- const missingPlugins = pluginsToFilter.filter(
3705
- (plugin) => !plugins.some(({ slug }) => slug === plugin)
3706
- );
3707
- const isSkipOption = filterOption === "skipPlugins";
3708
- 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('", "')}".`
3716
- );
3717
- }
3718
- if (categories.length > 0 && verbose) {
3719
- const removedCategorySlugs = filterItemRefsBy(
3720
- categories,
3721
- ({ plugin }) => filterFunction(plugin)
3722
- ).map(({ slug }) => slug);
3723
- ui().logger.info(
3724
- `The --${filterOption} argument removed categories with "${removedCategorySlugs.join(
3725
- '", "'
3726
- )}" slugs.
3727
- `
3728
- );
3729
- }
3730
- }
3731
-
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
- };
3903
+ // packages/cli/src/lib/implementation/filter.middleware.ts
3904
+ function filterMiddleware(originalProcessArgs) {
3905
+ const {
3906
+ plugins,
3907
+ categories = [],
3908
+ skipCategories = [],
3909
+ onlyCategories = [],
3910
+ skipPlugins = [],
3911
+ onlyPlugins = [],
3912
+ verbose = false
3913
+ } = originalProcessArgs;
3914
+ if (skipCategories.length === 0 && onlyCategories.length === 0 && skipPlugins.length === 0 && onlyPlugins.length === 0) {
3915
+ return { ...originalProcessArgs, categories };
3754
3916
  }
3917
+ handleConflictingOptions("categories", onlyCategories, skipCategories);
3918
+ handleConflictingOptions("plugins", onlyPlugins, skipPlugins);
3919
+ const filteredCategories = applyCategoryFilters(
3920
+ { categories, plugins },
3921
+ skipCategories,
3922
+ onlyCategories,
3923
+ verbose
3924
+ );
3925
+ const filteredPlugins = applyPluginFilters(
3926
+ { categories: filteredCategories, plugins },
3927
+ skipPlugins,
3928
+ onlyPlugins,
3929
+ verbose
3930
+ );
3931
+ const finalCategories = filterItemRefsBy(
3932
+ filteredCategories,
3933
+ (ref) => filteredPlugins.some((plugin) => plugin.slug === ref.plugin)
3934
+ );
3935
+ validateFinalState(
3936
+ { categories: finalCategories, plugins: filteredPlugins },
3937
+ { categories, plugins }
3938
+ );
3755
3939
  return {
3756
3940
  ...originalProcessArgs,
3757
- // if undefined fill categories with empty array
3758
- categories
3941
+ plugins: filteredPlugins,
3942
+ categories: finalCategories
3759
3943
  };
3760
3944
  }
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
- };
3945
+ function applyFilters(items, skipItems, onlyItems, key) {
3946
+ return items.filter((item) => {
3947
+ const itemKey = item[key];
3948
+ return !skipItems.includes(itemKey) && (onlyItems.length === 0 || onlyItems.includes(itemKey));
3949
+ });
3950
+ }
3951
+ function applyCategoryFilters({ categories, plugins }, skipCategories, onlyCategories, verbose) {
3952
+ if (skipCategories.length === 0 && onlyCategories.length === 0) {
3953
+ return categories;
3784
3954
  }
3785
- return {
3786
- ...originalProcessArgs,
3787
- // if undefined fill categories with empty array
3788
- categories
3789
- };
3955
+ validateFilterOption(
3956
+ "skipCategories",
3957
+ { plugins, categories },
3958
+ { itemsToFilter: skipCategories, verbose }
3959
+ );
3960
+ validateFilterOption(
3961
+ "onlyCategories",
3962
+ { plugins, categories },
3963
+ { itemsToFilter: onlyCategories, verbose }
3964
+ );
3965
+ return applyFilters(categories, skipCategories, onlyCategories, "slug");
3966
+ }
3967
+ function applyPluginFilters({ categories, plugins }, skipPlugins, onlyPlugins, verbose) {
3968
+ const filteredPlugins = filterPluginsFromCategories({
3969
+ categories,
3970
+ plugins
3971
+ });
3972
+ if (skipPlugins.length === 0 && onlyPlugins.length === 0) {
3973
+ return filteredPlugins;
3974
+ }
3975
+ validateFilterOption(
3976
+ "skipPlugins",
3977
+ { plugins: filteredPlugins, categories },
3978
+ { itemsToFilter: skipPlugins, verbose }
3979
+ );
3980
+ validateFilterOption(
3981
+ "onlyPlugins",
3982
+ { plugins: filteredPlugins, categories },
3983
+ { itemsToFilter: onlyPlugins, verbose }
3984
+ );
3985
+ return applyFilters(filteredPlugins, skipPlugins, onlyPlugins, "slug");
3986
+ }
3987
+ function filterPluginsFromCategories({
3988
+ categories,
3989
+ plugins
3990
+ }) {
3991
+ const validPluginSlugs = new Set(
3992
+ categories.flatMap((category) => category.refs.map((ref) => ref.plugin))
3993
+ );
3994
+ return categories.length > 0 ? plugins.filter((plugin) => validPluginSlugs.has(plugin.slug)) : plugins;
3790
3995
  }
3791
3996
 
3792
3997
  // packages/cli/src/lib/middlewares.ts
@@ -3796,11 +4001,7 @@ var middlewares = [
3796
4001
  applyBeforeValidation: false
3797
4002
  },
3798
4003
  {
3799
- middlewareFunction: onlyPluginsMiddleware,
3800
- applyBeforeValidation: false
3801
- },
3802
- {
3803
- middlewareFunction: skipPluginsMiddleware,
4004
+ middlewareFunction: filterMiddleware,
3804
4005
  applyBeforeValidation: false
3805
4006
  }
3806
4007
  ];
@@ -3877,14 +4078,12 @@ function yargsGlobalOptionsDefinition() {
3877
4078
  var options = {
3878
4079
  ...yargsGlobalOptionsDefinition(),
3879
4080
  ...yargsCoreConfigOptionsDefinition(),
3880
- ...yargsOnlyPluginsOptionsDefinition(),
3881
- ...yargsSkipPluginsOptionsDefinition()
4081
+ ...yargsFilterOptionsDefinition()
3882
4082
  };
3883
4083
  var groups = {
3884
4084
  "Global Options:": [
3885
4085
  ...Object.keys(yargsGlobalOptionsDefinition()),
3886
- ...Object.keys(yargsOnlyPluginsOptionsDefinition()),
3887
- ...Object.keys(yargsSkipPluginsOptionsDefinition())
4086
+ ...Object.keys(yargsFilterOptionsDefinition())
3888
4087
  ],
3889
4088
  "Persist Options:": Object.keys(yargsPersistConfigOptionsDefinition()),
3890
4089
  "Upload Options:": Object.keys(yargsUploadConfigOptionsDefinition())
@@ -3892,10 +4091,10 @@ var groups = {
3892
4091
 
3893
4092
  // packages/cli/src/lib/yargs-cli.ts
3894
4093
  import { blue, dim as dim2, green as green4 } from "ansis";
3895
- import yargs from "yargs";
4094
+ import yargs2 from "yargs";
3896
4095
 
3897
4096
  // packages/cli/package.json
3898
- var version2 = "0.50.0";
4097
+ var version2 = "0.52.0";
3899
4098
 
3900
4099
  // packages/cli/src/lib/implementation/formatting.ts
3901
4100
  import { bold as bold13, dim, green as green3 } from "ansis";
@@ -3947,7 +4146,7 @@ function yargsCli(argv, cfg) {
3947
4146
  const options2 = cfg.options ?? {};
3948
4147
  const groups2 = cfg.groups ?? {};
3949
4148
  const examples = cfg.examples ?? [];
3950
- const cli2 = yargs(argv);
4149
+ const cli2 = yargs2(argv);
3951
4150
  cli2.updateLocale(yargsDecorator).wrap(Math.max(TERMINAL_WIDTH, cli2.terminalWidth())).help("help", descriptionStyle("Show help")).alias("h", "help").showHelpOnFail(false).version("version", dim2`Show version`, version2).check((args) => {
3952
4151
  const persist = args["persist"];
3953
4152
  return persist == null || validatePersistFormat(persist);