@code-pushup/cli 0.26.1 → 0.28.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/README.md +15 -0
- package/index.js +1088 -336
- package/package.json +2 -3
- package/src/lib/autorun/autorun-command.d.ts +0 -1
- package/src/lib/compare/compare-command.d.ts +7 -0
- package/src/lib/implementation/compare.model.d.ts +2 -0
- package/src/lib/implementation/compare.options.d.ts +3 -0
- package/src/lib/implementation/logging.d.ts +0 -4
- package/src/lib/implementation/only-plugins.middleware.d.ts +1 -3
- package/src/lib/implementation/only-plugins.model.d.ts +5 -2
- package/src/lib/implementation/only-plugins.utils.d.ts +5 -9
- package/src/lib/options.d.ts +6 -0
- package/src/lib/print-config/print-config-command.d.ts +0 -3
- package/src/lib/yargs-cli.d.ts +4 -0
package/index.js
CHANGED
|
@@ -57,7 +57,7 @@ function getMissingRefsForCategories(categories, plugins) {
|
|
|
57
57
|
({ refs }) => refs.filter(({ type }) => type === "group").map(({ plugin, slug }) => `${plugin}#${slug} (group)`)
|
|
58
58
|
);
|
|
59
59
|
const groupRefsFromPlugins = plugins.flatMap(
|
|
60
|
-
({ groups, slug: pluginSlug }) => Array.isArray(
|
|
60
|
+
({ groups: groups2, slug: pluginSlug }) => Array.isArray(groups2) ? groups2.map(({ slug }) => `${pluginSlug}#${slug} (group)`) : []
|
|
61
61
|
);
|
|
62
62
|
const missingGroupRefs = hasMissingStrings(
|
|
63
63
|
groupRefsFromCategory,
|
|
@@ -92,6 +92,9 @@ var descriptionSchema = z.string({ description: "Description (markdown)" }).max(
|
|
|
92
92
|
var urlSchema = z.string().url();
|
|
93
93
|
var docsUrlSchema = urlSchema.optional().or(z.literal("")).describe("Documentation site");
|
|
94
94
|
var titleSchema = z.string({ description: "Descriptive name" }).max(MAX_TITLE_LENGTH);
|
|
95
|
+
var scoreSchema = z.number({
|
|
96
|
+
description: "Value between 0 and 1"
|
|
97
|
+
}).min(0).max(1);
|
|
95
98
|
function metaSchema(options2) {
|
|
96
99
|
const {
|
|
97
100
|
descriptionDescription,
|
|
@@ -223,6 +226,8 @@ var issueSchema = z3.object(
|
|
|
223
226
|
);
|
|
224
227
|
|
|
225
228
|
// packages/models/src/lib/audit-output.ts
|
|
229
|
+
var auditValueSchema = nonnegativeIntSchema.describe("Raw numeric value");
|
|
230
|
+
var auditDisplayValueSchema = z4.string({ description: "Formatted value (e.g. '0.9 s', '2.1 MB')" }).optional();
|
|
226
231
|
var auditDetailsSchema = z4.object(
|
|
227
232
|
{
|
|
228
233
|
issues: z4.array(issueSchema, { description: "List of findings" })
|
|
@@ -232,11 +237,9 @@ var auditDetailsSchema = z4.object(
|
|
|
232
237
|
var auditOutputSchema = z4.object(
|
|
233
238
|
{
|
|
234
239
|
slug: slugSchema.describe("Reference to audit"),
|
|
235
|
-
displayValue:
|
|
236
|
-
value:
|
|
237
|
-
score:
|
|
238
|
-
description: "Value between 0 and 1"
|
|
239
|
-
}).min(0).max(1),
|
|
240
|
+
displayValue: auditDisplayValueSchema,
|
|
241
|
+
value: auditValueSchema,
|
|
242
|
+
score: scoreSchema,
|
|
240
243
|
details: auditDetailsSchema.optional()
|
|
241
244
|
},
|
|
242
245
|
{ description: "Audit information" }
|
|
@@ -375,28 +378,28 @@ var groupSchema = scorableSchema(
|
|
|
375
378
|
var groupsSchema = z8.array(groupSchema, {
|
|
376
379
|
description: "List of groups"
|
|
377
380
|
}).optional().refine(
|
|
378
|
-
(
|
|
379
|
-
(
|
|
380
|
-
message: duplicateSlugsInGroupsErrorMsg(
|
|
381
|
+
(groups2) => !getDuplicateSlugsInGroups(groups2),
|
|
382
|
+
(groups2) => ({
|
|
383
|
+
message: duplicateSlugsInGroupsErrorMsg(groups2)
|
|
381
384
|
})
|
|
382
385
|
);
|
|
383
|
-
function duplicateRefsInGroupsErrorMsg(
|
|
384
|
-
const duplicateRefs = getDuplicateRefsInGroups(
|
|
386
|
+
function duplicateRefsInGroupsErrorMsg(groups2) {
|
|
387
|
+
const duplicateRefs = getDuplicateRefsInGroups(groups2);
|
|
385
388
|
return `In plugin groups the following references are not unique: ${errorItems(
|
|
386
389
|
duplicateRefs
|
|
387
390
|
)}`;
|
|
388
391
|
}
|
|
389
|
-
function getDuplicateRefsInGroups(
|
|
390
|
-
return hasDuplicateStrings(
|
|
392
|
+
function getDuplicateRefsInGroups(groups2) {
|
|
393
|
+
return hasDuplicateStrings(groups2.map(({ slug: ref }) => ref).filter(exists));
|
|
391
394
|
}
|
|
392
|
-
function duplicateSlugsInGroupsErrorMsg(
|
|
393
|
-
const duplicateRefs = getDuplicateSlugsInGroups(
|
|
395
|
+
function duplicateSlugsInGroupsErrorMsg(groups2) {
|
|
396
|
+
const duplicateRefs = getDuplicateSlugsInGroups(groups2);
|
|
394
397
|
return `In groups the following slugs are not unique: ${errorItems(
|
|
395
398
|
duplicateRefs
|
|
396
399
|
)}`;
|
|
397
400
|
}
|
|
398
|
-
function getDuplicateSlugsInGroups(
|
|
399
|
-
return Array.isArray(
|
|
401
|
+
function getDuplicateSlugsInGroups(groups2) {
|
|
402
|
+
return Array.isArray(groups2) ? hasDuplicateStrings(groups2.map(({ slug }) => slug)) : false;
|
|
400
403
|
}
|
|
401
404
|
|
|
402
405
|
// packages/models/src/lib/runner-config.ts
|
|
@@ -501,9 +504,9 @@ var CONFIG_FILE_NAME = "code-pushup.config";
|
|
|
501
504
|
var SUPPORTED_CONFIG_FILE_FORMATS = ["ts", "mjs", "js"];
|
|
502
505
|
|
|
503
506
|
// packages/models/src/lib/implementation/constants.ts
|
|
504
|
-
var
|
|
505
|
-
var
|
|
506
|
-
var
|
|
507
|
+
var DEFAULT_PERSIST_OUTPUT_DIR = ".code-pushup";
|
|
508
|
+
var DEFAULT_PERSIST_FILENAME = "report";
|
|
509
|
+
var DEFAULT_PERSIST_FORMAT = ["json", "md"];
|
|
507
510
|
|
|
508
511
|
// packages/models/src/lib/report.ts
|
|
509
512
|
import { z as z13 } from "zod";
|
|
@@ -527,15 +530,15 @@ var pluginReportSchema = pluginMetaSchema.merge(
|
|
|
527
530
|
)
|
|
528
531
|
})
|
|
529
532
|
);
|
|
530
|
-
function missingRefsFromGroupsErrorMsg2(audits,
|
|
531
|
-
const missingRefs = getMissingRefsFromGroups2(audits,
|
|
533
|
+
function missingRefsFromGroupsErrorMsg2(audits, groups2) {
|
|
534
|
+
const missingRefs = getMissingRefsFromGroups2(audits, groups2);
|
|
532
535
|
return `group references need to point to an existing audit in this plugin report: ${errorItems(
|
|
533
536
|
missingRefs
|
|
534
537
|
)}`;
|
|
535
538
|
}
|
|
536
|
-
function getMissingRefsFromGroups2(audits,
|
|
539
|
+
function getMissingRefsFromGroups2(audits, groups2) {
|
|
537
540
|
return hasMissingStrings(
|
|
538
|
-
|
|
541
|
+
groups2.flatMap(
|
|
539
542
|
({ refs: auditRefs }) => auditRefs.map(({ slug: ref }) => ref)
|
|
540
543
|
),
|
|
541
544
|
audits.map(({ slug }) => slug)
|
|
@@ -568,6 +571,138 @@ var reportSchema = packageVersionSchema({
|
|
|
568
571
|
})
|
|
569
572
|
);
|
|
570
573
|
|
|
574
|
+
// packages/models/src/lib/reports-diff.ts
|
|
575
|
+
import { z as z14 } from "zod";
|
|
576
|
+
function makeComparisonSchema(schema) {
|
|
577
|
+
const sharedDescription = schema.description || "Result";
|
|
578
|
+
return z14.object({
|
|
579
|
+
before: schema.describe(`${sharedDescription} (source commit)`),
|
|
580
|
+
after: schema.describe(`${sharedDescription} (target commit)`)
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
function makeArraysComparisonSchema(diffSchema, resultSchema, description) {
|
|
584
|
+
return z14.object(
|
|
585
|
+
{
|
|
586
|
+
changed: z14.array(diffSchema),
|
|
587
|
+
unchanged: z14.array(resultSchema),
|
|
588
|
+
added: z14.array(resultSchema),
|
|
589
|
+
removed: z14.array(resultSchema)
|
|
590
|
+
},
|
|
591
|
+
{ description }
|
|
592
|
+
);
|
|
593
|
+
}
|
|
594
|
+
var scorableMetaSchema = z14.object({ slug: slugSchema, title: titleSchema });
|
|
595
|
+
var scorableWithPluginMetaSchema = scorableMetaSchema.merge(
|
|
596
|
+
z14.object({
|
|
597
|
+
plugin: pluginMetaSchema.pick({ slug: true, title: true }).describe("Plugin which defines it")
|
|
598
|
+
})
|
|
599
|
+
);
|
|
600
|
+
var scorableDiffSchema = scorableMetaSchema.merge(
|
|
601
|
+
z14.object({
|
|
602
|
+
scores: makeComparisonSchema(scoreSchema).merge(
|
|
603
|
+
z14.object({
|
|
604
|
+
diff: z14.number().min(-1).max(1).describe("Score change (`scores.after - scores.before`)")
|
|
605
|
+
})
|
|
606
|
+
).describe("Score comparison")
|
|
607
|
+
})
|
|
608
|
+
);
|
|
609
|
+
var scorableWithPluginDiffSchema = scorableDiffSchema.merge(
|
|
610
|
+
scorableWithPluginMetaSchema
|
|
611
|
+
);
|
|
612
|
+
var categoryDiffSchema = scorableDiffSchema;
|
|
613
|
+
var groupDiffSchema = scorableWithPluginDiffSchema;
|
|
614
|
+
var auditDiffSchema = scorableWithPluginDiffSchema.merge(
|
|
615
|
+
z14.object({
|
|
616
|
+
values: makeComparisonSchema(auditValueSchema).merge(
|
|
617
|
+
z14.object({
|
|
618
|
+
diff: z14.number().int().describe("Value change (`values.after - values.before`)")
|
|
619
|
+
})
|
|
620
|
+
).describe("Audit `value` comparison"),
|
|
621
|
+
displayValues: makeComparisonSchema(auditDisplayValueSchema).describe(
|
|
622
|
+
"Audit `displayValue` comparison"
|
|
623
|
+
)
|
|
624
|
+
})
|
|
625
|
+
);
|
|
626
|
+
var categoryResultSchema = scorableMetaSchema.merge(
|
|
627
|
+
z14.object({ score: scoreSchema })
|
|
628
|
+
);
|
|
629
|
+
var groupResultSchema = scorableWithPluginMetaSchema.merge(
|
|
630
|
+
z14.object({ score: scoreSchema })
|
|
631
|
+
);
|
|
632
|
+
var auditResultSchema = scorableWithPluginMetaSchema.merge(
|
|
633
|
+
auditOutputSchema.pick({ score: true, value: true, displayValue: true })
|
|
634
|
+
);
|
|
635
|
+
var reportsDiffSchema = z14.object({
|
|
636
|
+
commits: makeComparisonSchema(commitSchema).nullable().describe("Commits identifying compared reports"),
|
|
637
|
+
categories: makeArraysComparisonSchema(
|
|
638
|
+
categoryDiffSchema,
|
|
639
|
+
categoryResultSchema,
|
|
640
|
+
"Changes affecting categories"
|
|
641
|
+
),
|
|
642
|
+
groups: makeArraysComparisonSchema(
|
|
643
|
+
groupDiffSchema,
|
|
644
|
+
groupResultSchema,
|
|
645
|
+
"Changes affecting groups"
|
|
646
|
+
),
|
|
647
|
+
audits: makeArraysComparisonSchema(
|
|
648
|
+
auditDiffSchema,
|
|
649
|
+
auditResultSchema,
|
|
650
|
+
"Changes affecting audits"
|
|
651
|
+
)
|
|
652
|
+
}).merge(
|
|
653
|
+
packageVersionSchema({
|
|
654
|
+
versionDescription: "NPM version of the CLI (when `compare` was run)",
|
|
655
|
+
required: true
|
|
656
|
+
})
|
|
657
|
+
).merge(
|
|
658
|
+
executionMetaSchema({
|
|
659
|
+
descriptionDate: "Start date and time of the compare run",
|
|
660
|
+
descriptionDuration: "Duration of the compare run in ms"
|
|
661
|
+
})
|
|
662
|
+
);
|
|
663
|
+
|
|
664
|
+
// packages/utils/src/lib/diff.ts
|
|
665
|
+
function matchArrayItemsByKey({
|
|
666
|
+
before,
|
|
667
|
+
after,
|
|
668
|
+
key
|
|
669
|
+
}) {
|
|
670
|
+
const pairs = [];
|
|
671
|
+
const added = [];
|
|
672
|
+
const afterKeys = /* @__PURE__ */ new Set();
|
|
673
|
+
const keyFn = typeof key === "function" ? key : (item) => item[key];
|
|
674
|
+
for (const afterItem of after) {
|
|
675
|
+
const afterKey = keyFn(afterItem);
|
|
676
|
+
afterKeys.add(afterKey);
|
|
677
|
+
const match = before.find((beforeItem) => keyFn(beforeItem) === afterKey);
|
|
678
|
+
if (match) {
|
|
679
|
+
pairs.push({ before: match, after: afterItem });
|
|
680
|
+
} else {
|
|
681
|
+
added.push(afterItem);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
const removed = before.filter(
|
|
685
|
+
(beforeItem) => !afterKeys.has(keyFn(beforeItem))
|
|
686
|
+
);
|
|
687
|
+
return {
|
|
688
|
+
pairs,
|
|
689
|
+
added,
|
|
690
|
+
removed
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
function comparePairs(pairs, equalsFn) {
|
|
694
|
+
return pairs.reduce(
|
|
695
|
+
(acc, pair) => ({
|
|
696
|
+
...acc,
|
|
697
|
+
...equalsFn(pair) ? { unchanged: [...acc.unchanged, pair.after] } : { changed: [...acc.changed, pair] }
|
|
698
|
+
}),
|
|
699
|
+
{
|
|
700
|
+
changed: [],
|
|
701
|
+
unchanged: []
|
|
702
|
+
}
|
|
703
|
+
);
|
|
704
|
+
}
|
|
705
|
+
|
|
571
706
|
// packages/utils/src/lib/execute-process.ts
|
|
572
707
|
import { spawn } from "node:child_process";
|
|
573
708
|
|
|
@@ -576,13 +711,25 @@ import { join } from "node:path";
|
|
|
576
711
|
|
|
577
712
|
// packages/utils/src/lib/file-system.ts
|
|
578
713
|
import { bundleRequire } from "bundle-require";
|
|
579
|
-
import
|
|
714
|
+
import chalk2 from "chalk";
|
|
580
715
|
import { mkdir, readFile, readdir, rm, stat } from "node:fs/promises";
|
|
581
716
|
|
|
582
717
|
// packages/utils/src/lib/formatting.ts
|
|
583
718
|
function slugify(text) {
|
|
584
719
|
return text.trim().toLowerCase().replace(/\s+|\//g, "-").replace(/[^a-z\d-]/g, "");
|
|
585
720
|
}
|
|
721
|
+
function pluralize(text, amount) {
|
|
722
|
+
if (amount != null && Math.abs(amount) === 1) {
|
|
723
|
+
return text;
|
|
724
|
+
}
|
|
725
|
+
if (text.endsWith("y")) {
|
|
726
|
+
return `${text.slice(0, -1)}ies`;
|
|
727
|
+
}
|
|
728
|
+
if (text.endsWith("s")) {
|
|
729
|
+
return `${text}es`;
|
|
730
|
+
}
|
|
731
|
+
return `${text}s`;
|
|
732
|
+
}
|
|
586
733
|
function formatBytes(bytes, decimals = 2) {
|
|
587
734
|
const positiveBytes = Math.max(bytes, 0);
|
|
588
735
|
if (positiveBytes === 0) {
|
|
@@ -594,6 +741,9 @@ function formatBytes(bytes, decimals = 2) {
|
|
|
594
741
|
const i = Math.floor(Math.log(positiveBytes) / Math.log(k));
|
|
595
742
|
return `${Number.parseFloat((positiveBytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
|
|
596
743
|
}
|
|
744
|
+
function pluralizeToken(token, times) {
|
|
745
|
+
return `${times} ${Math.abs(times) === 1 ? token : pluralize(token)}`;
|
|
746
|
+
}
|
|
597
747
|
function formatDuration(duration) {
|
|
598
748
|
if (duration < 1e3) {
|
|
599
749
|
return `${duration} ms`;
|
|
@@ -621,33 +771,106 @@ function isPromiseRejectedResult(result) {
|
|
|
621
771
|
return result.status === "rejected";
|
|
622
772
|
}
|
|
623
773
|
|
|
774
|
+
// packages/utils/src/lib/logging.ts
|
|
775
|
+
import isaacs_cliui from "@isaacs/cliui";
|
|
776
|
+
import { cliui } from "@poppinss/cliui";
|
|
777
|
+
import chalk from "chalk";
|
|
778
|
+
|
|
779
|
+
// packages/utils/src/lib/reports/constants.ts
|
|
780
|
+
var TERMINAL_WIDTH = 80;
|
|
781
|
+
var NEW_LINE = "\n";
|
|
782
|
+
var SCORE_COLOR_RANGE = {
|
|
783
|
+
GREEN_MIN: 0.9,
|
|
784
|
+
YELLOW_MIN: 0.5
|
|
785
|
+
};
|
|
786
|
+
var FOOTER_PREFIX = "Made with \u2764 by";
|
|
787
|
+
var CODE_PUSHUP_DOMAIN = "code-pushup.dev";
|
|
788
|
+
var README_LINK = "https://github.com/flowup/quality-metrics-cli#readme";
|
|
789
|
+
var reportHeadlineText = "Code PushUp Report";
|
|
790
|
+
var reportOverviewTableHeaders = [
|
|
791
|
+
"\u{1F3F7} Category",
|
|
792
|
+
"\u2B50 Score",
|
|
793
|
+
"\u{1F6E1} Audits"
|
|
794
|
+
];
|
|
795
|
+
var reportRawOverviewTableHeaders = ["Category", "Score", "Audits"];
|
|
796
|
+
var reportMetaTableHeaders = [
|
|
797
|
+
"Commit",
|
|
798
|
+
"Version",
|
|
799
|
+
"Duration",
|
|
800
|
+
"Plugins",
|
|
801
|
+
"Categories",
|
|
802
|
+
"Audits"
|
|
803
|
+
];
|
|
804
|
+
var pluginMetaTableHeaders = [
|
|
805
|
+
"Plugin",
|
|
806
|
+
"Audits",
|
|
807
|
+
"Version",
|
|
808
|
+
"Duration"
|
|
809
|
+
];
|
|
810
|
+
var detailsTableHeaders = [
|
|
811
|
+
"Severity",
|
|
812
|
+
"Message",
|
|
813
|
+
"Source file",
|
|
814
|
+
"Line(s)"
|
|
815
|
+
];
|
|
816
|
+
|
|
817
|
+
// packages/utils/src/lib/logging.ts
|
|
818
|
+
var singletonUiInstance;
|
|
819
|
+
function ui() {
|
|
820
|
+
if (singletonUiInstance === void 0) {
|
|
821
|
+
singletonUiInstance = cliui();
|
|
822
|
+
}
|
|
823
|
+
return {
|
|
824
|
+
...singletonUiInstance,
|
|
825
|
+
row: (args) => {
|
|
826
|
+
logListItem(args);
|
|
827
|
+
}
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
var singletonisaacUi;
|
|
831
|
+
function logListItem(args) {
|
|
832
|
+
if (singletonisaacUi === void 0) {
|
|
833
|
+
singletonisaacUi = isaacs_cliui({ width: TERMINAL_WIDTH });
|
|
834
|
+
}
|
|
835
|
+
singletonisaacUi.div(...args);
|
|
836
|
+
const content = singletonisaacUi.toString();
|
|
837
|
+
singletonisaacUi.rows = [];
|
|
838
|
+
singletonUiInstance?.logger.log(content);
|
|
839
|
+
}
|
|
840
|
+
function link(text) {
|
|
841
|
+
return chalk.underline(chalk.blueBright(text));
|
|
842
|
+
}
|
|
843
|
+
|
|
624
844
|
// packages/utils/src/lib/log-results.ts
|
|
625
|
-
function logMultipleResults(results, messagePrefix,
|
|
626
|
-
if (
|
|
845
|
+
function logMultipleResults(results, messagePrefix, succeededTransform, failedTransform) {
|
|
846
|
+
if (succeededTransform) {
|
|
627
847
|
const succeededResults = results.filter(isPromiseFulfilledResult);
|
|
628
848
|
logPromiseResults(
|
|
629
849
|
succeededResults,
|
|
630
850
|
`${messagePrefix} successfully: `,
|
|
631
|
-
|
|
851
|
+
succeededTransform
|
|
632
852
|
);
|
|
633
853
|
}
|
|
634
|
-
if (
|
|
854
|
+
if (failedTransform) {
|
|
635
855
|
const failedResults = results.filter(isPromiseRejectedResult);
|
|
636
856
|
logPromiseResults(
|
|
637
857
|
failedResults,
|
|
638
858
|
`${messagePrefix} failed: `,
|
|
639
|
-
|
|
859
|
+
failedTransform
|
|
640
860
|
);
|
|
641
861
|
}
|
|
642
862
|
}
|
|
643
|
-
function logPromiseResults(results, logMessage,
|
|
863
|
+
function logPromiseResults(results, logMessage, getMsg) {
|
|
644
864
|
if (results.length > 0) {
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
}
|
|
650
|
-
|
|
865
|
+
const log2 = results[0]?.status === "fulfilled" ? (m) => {
|
|
866
|
+
ui().logger.success(m);
|
|
867
|
+
} : (m) => {
|
|
868
|
+
ui().logger.warning(m);
|
|
869
|
+
};
|
|
870
|
+
log2(logMessage);
|
|
871
|
+
results.forEach((result) => {
|
|
872
|
+
log2(getMsg(result));
|
|
873
|
+
});
|
|
651
874
|
}
|
|
652
875
|
}
|
|
653
876
|
|
|
@@ -681,26 +904,24 @@ async function ensureDirectoryExists(baseDir) {
|
|
|
681
904
|
await mkdir(baseDir, { recursive: true });
|
|
682
905
|
return;
|
|
683
906
|
} catch (error) {
|
|
684
|
-
|
|
907
|
+
ui().logger.error(error.message);
|
|
685
908
|
if (error.code !== "EEXIST") {
|
|
686
909
|
throw error;
|
|
687
910
|
}
|
|
688
911
|
}
|
|
689
912
|
}
|
|
690
913
|
function logMultipleFileResults(fileResults, messagePrefix) {
|
|
691
|
-
const
|
|
914
|
+
const succeededTransform = (result) => {
|
|
692
915
|
const [fileName, size] = result.value;
|
|
693
|
-
const formattedSize = size ? ` (${
|
|
694
|
-
|
|
695
|
-
};
|
|
696
|
-
const failedCallback = (result) => {
|
|
697
|
-
console.warn(`- ${chalk.bold(result.reason)}`);
|
|
916
|
+
const formattedSize = size ? ` (${chalk2.gray(formatBytes(size))})` : "";
|
|
917
|
+
return `- ${chalk2.bold(fileName)}${formattedSize}`;
|
|
698
918
|
};
|
|
919
|
+
const failedTransform = (result) => `- ${chalk2.bold(result.reason)}`;
|
|
699
920
|
logMultipleResults(
|
|
700
921
|
fileResults,
|
|
701
922
|
messagePrefix,
|
|
702
|
-
|
|
703
|
-
|
|
923
|
+
succeededTransform,
|
|
924
|
+
failedTransform
|
|
704
925
|
);
|
|
705
926
|
}
|
|
706
927
|
var NoExportError = class extends Error {
|
|
@@ -719,48 +940,105 @@ async function importEsmModule(options2) {
|
|
|
719
940
|
return mod.default;
|
|
720
941
|
}
|
|
721
942
|
|
|
722
|
-
// packages/utils/src/lib/reports/
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
943
|
+
// packages/utils/src/lib/reports/md/details.ts
|
|
944
|
+
function details(title, content, cfg = { open: false }) {
|
|
945
|
+
return `<details${cfg.open ? " open" : ""}>
|
|
946
|
+
<summary>${title}</summary>
|
|
947
|
+
|
|
948
|
+
${content}
|
|
949
|
+
|
|
950
|
+
</details>
|
|
951
|
+
`;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
// packages/utils/src/lib/reports/md/font-style.ts
|
|
955
|
+
var stylesMap = {
|
|
956
|
+
i: "_",
|
|
957
|
+
// italic
|
|
958
|
+
b: "**",
|
|
959
|
+
// bold
|
|
960
|
+
s: "~",
|
|
961
|
+
// strike through
|
|
962
|
+
c: "`"
|
|
963
|
+
// code
|
|
728
964
|
};
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
"
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
965
|
+
function style(text, styles = ["b"]) {
|
|
966
|
+
return styles.reduce((t, s) => `${stylesMap[s]}${t}${stylesMap[s]}`, text);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
// packages/utils/src/lib/reports/md/headline.ts
|
|
970
|
+
function headline(text, hierarchy = 1) {
|
|
971
|
+
return `${"#".repeat(hierarchy)} ${text}`;
|
|
972
|
+
}
|
|
973
|
+
function h1(text) {
|
|
974
|
+
return headline(text, 1);
|
|
975
|
+
}
|
|
976
|
+
function h2(text) {
|
|
977
|
+
return headline(text, 2);
|
|
978
|
+
}
|
|
979
|
+
function h3(text) {
|
|
980
|
+
return headline(text, 3);
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
// packages/utils/src/lib/reports/md/image.ts
|
|
984
|
+
function image(src, alt) {
|
|
985
|
+
return ``;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
// packages/utils/src/lib/reports/md/link.ts
|
|
989
|
+
function link2(href, text) {
|
|
990
|
+
return `[${text || href}](${href})`;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
// packages/utils/src/lib/reports/md/list.ts
|
|
994
|
+
function li(text, order = "unordered") {
|
|
995
|
+
const style2 = order === "unordered" ? "-" : "- [ ]";
|
|
996
|
+
return `${style2} ${text}`;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
// packages/utils/src/lib/reports/md/paragraphs.ts
|
|
1000
|
+
function paragraphs(...sections) {
|
|
1001
|
+
return sections.filter(Boolean).join("\n\n");
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
// packages/utils/src/lib/reports/md/table.ts
|
|
1005
|
+
var alignString = /* @__PURE__ */ new Map([
|
|
1006
|
+
["l", ":--"],
|
|
1007
|
+
["c", ":--:"],
|
|
1008
|
+
["r", "--:"]
|
|
1009
|
+
]);
|
|
1010
|
+
function tableMd(data, align) {
|
|
1011
|
+
if (data.length === 0) {
|
|
1012
|
+
throw new Error("Data can't be empty");
|
|
1013
|
+
}
|
|
1014
|
+
const alignmentSetting = align ?? data[0]?.map(() => "c");
|
|
1015
|
+
const tableContent = data.map((arr) => `|${arr.join("|")}|`);
|
|
1016
|
+
const alignmentRow = `|${alignmentSetting?.map((s) => alignString.get(s)).join("|")}|`;
|
|
1017
|
+
return tableContent[0] + NEW_LINE + alignmentRow + NEW_LINE + tableContent.slice(1).join(NEW_LINE);
|
|
1018
|
+
}
|
|
1019
|
+
function tableHtml(data) {
|
|
1020
|
+
if (data.length === 0) {
|
|
1021
|
+
throw new Error("Data can't be empty");
|
|
1022
|
+
}
|
|
1023
|
+
const tableContent = data.map((arr, index) => {
|
|
1024
|
+
if (index === 0) {
|
|
1025
|
+
const headerRow = arr.map((s) => `<th>${s}</th>`).join("");
|
|
1026
|
+
return `<tr>${headerRow}</tr>`;
|
|
1027
|
+
}
|
|
1028
|
+
const row = arr.map((s) => `<td>${s}</td>`).join("");
|
|
1029
|
+
return `<tr>${row}</tr>`;
|
|
1030
|
+
});
|
|
1031
|
+
return `<table>${tableContent.join("")}</table>`;
|
|
1032
|
+
}
|
|
759
1033
|
|
|
760
1034
|
// packages/utils/src/lib/reports/utils.ts
|
|
761
1035
|
function formatReportScore(score) {
|
|
762
1036
|
return Math.round(score * 100).toString();
|
|
763
1037
|
}
|
|
1038
|
+
function formatScoreWithColor(score, options2) {
|
|
1039
|
+
const styledNumber = options2?.skipBold ? formatReportScore(score) : style(formatReportScore(score));
|
|
1040
|
+
return `${getRoundScoreMarker(score)} ${styledNumber}`;
|
|
1041
|
+
}
|
|
764
1042
|
function getRoundScoreMarker(score) {
|
|
765
1043
|
if (score >= SCORE_COLOR_RANGE.GREEN_MIN) {
|
|
766
1044
|
return "\u{1F7E2}";
|
|
@@ -779,6 +1057,30 @@ function getSquaredScoreMarker(score) {
|
|
|
779
1057
|
}
|
|
780
1058
|
return "\u{1F7E5}";
|
|
781
1059
|
}
|
|
1060
|
+
function getDiffMarker(diff) {
|
|
1061
|
+
if (diff > 0) {
|
|
1062
|
+
return "\u2191";
|
|
1063
|
+
}
|
|
1064
|
+
if (diff < 0) {
|
|
1065
|
+
return "\u2193";
|
|
1066
|
+
}
|
|
1067
|
+
return "";
|
|
1068
|
+
}
|
|
1069
|
+
function colorByScoreDiff(text, diff) {
|
|
1070
|
+
const color = diff > 0 ? "green" : diff < 0 ? "red" : "gray";
|
|
1071
|
+
return shieldsBadge(text, color);
|
|
1072
|
+
}
|
|
1073
|
+
function shieldsBadge(text, color) {
|
|
1074
|
+
return image(
|
|
1075
|
+
`https://img.shields.io/badge/${encodeURIComponent(text)}-${color}`,
|
|
1076
|
+
text
|
|
1077
|
+
);
|
|
1078
|
+
}
|
|
1079
|
+
function formatDiffNumber(diff) {
|
|
1080
|
+
const number = Math.abs(diff) === Number.POSITIVE_INFINITY ? "\u221E" : `${Math.abs(diff)}`;
|
|
1081
|
+
const sign = diff < 0 ? "\u2212" : "+";
|
|
1082
|
+
return `${sign}${number}`;
|
|
1083
|
+
}
|
|
782
1084
|
function getSeverityIcon(severity) {
|
|
783
1085
|
if (severity === "error") {
|
|
784
1086
|
return "\u{1F6A8}";
|
|
@@ -789,7 +1091,7 @@ function getSeverityIcon(severity) {
|
|
|
789
1091
|
return "\u2139\uFE0F";
|
|
790
1092
|
}
|
|
791
1093
|
function calcDuration(start, stop) {
|
|
792
|
-
return Math.
|
|
1094
|
+
return Math.round((stop ?? performance.now()) - start);
|
|
793
1095
|
}
|
|
794
1096
|
function countCategoryAudits(refs, plugins) {
|
|
795
1097
|
const groupLookup = plugins.reduce(
|
|
@@ -952,7 +1254,7 @@ var ProcessError = class extends Error {
|
|
|
952
1254
|
}
|
|
953
1255
|
};
|
|
954
1256
|
function executeProcess(cfg) {
|
|
955
|
-
const { observer, cwd, command, args } = cfg;
|
|
1257
|
+
const { observer, cwd, command, args, alwaysResolve = false } = cfg;
|
|
956
1258
|
const { onStdout, onError, onComplete } = observer ?? {};
|
|
957
1259
|
const date = (/* @__PURE__ */ new Date()).toISOString();
|
|
958
1260
|
const start = performance.now();
|
|
@@ -972,7 +1274,7 @@ function executeProcess(cfg) {
|
|
|
972
1274
|
});
|
|
973
1275
|
process2.on("close", (code) => {
|
|
974
1276
|
const timings = { date, duration: calcDuration(start) };
|
|
975
|
-
if (code === 0) {
|
|
1277
|
+
if (code === 0 || alwaysResolve) {
|
|
976
1278
|
onComplete?.();
|
|
977
1279
|
resolve({ code, stdout, stderr, ...timings });
|
|
978
1280
|
} else {
|
|
@@ -984,6 +1286,14 @@ function executeProcess(cfg) {
|
|
|
984
1286
|
});
|
|
985
1287
|
}
|
|
986
1288
|
|
|
1289
|
+
// packages/utils/src/lib/filter.ts
|
|
1290
|
+
function filterItemRefsBy(items, refFilterFn) {
|
|
1291
|
+
return items.map((item) => ({
|
|
1292
|
+
...item,
|
|
1293
|
+
refs: item.refs.filter(refFilterFn)
|
|
1294
|
+
})).filter((item) => item.refs.length);
|
|
1295
|
+
}
|
|
1296
|
+
|
|
987
1297
|
// packages/utils/src/lib/git.ts
|
|
988
1298
|
import { isAbsolute, join as join2, relative } from "node:path";
|
|
989
1299
|
import { simpleGit } from "simple-git";
|
|
@@ -992,6 +1302,9 @@ import { simpleGit } from "simple-git";
|
|
|
992
1302
|
function toArray(val) {
|
|
993
1303
|
return Array.isArray(val) ? val : [val];
|
|
994
1304
|
}
|
|
1305
|
+
function objectToEntries(obj) {
|
|
1306
|
+
return Object.entries(obj);
|
|
1307
|
+
}
|
|
995
1308
|
function deepClone(obj) {
|
|
996
1309
|
return obj == null || typeof obj !== "object" ? obj : structuredClone(obj);
|
|
997
1310
|
}
|
|
@@ -1001,14 +1314,14 @@ function toUnixPath(path) {
|
|
|
1001
1314
|
|
|
1002
1315
|
// packages/utils/src/lib/git.ts
|
|
1003
1316
|
async function getLatestCommit(git = simpleGit()) {
|
|
1004
|
-
const
|
|
1317
|
+
const log2 = await git.log({
|
|
1005
1318
|
maxCount: 1,
|
|
1006
1319
|
format: { hash: "%H", message: "%s", author: "%an", date: "%aI" }
|
|
1007
1320
|
});
|
|
1008
|
-
if (!
|
|
1321
|
+
if (!log2.latest) {
|
|
1009
1322
|
return null;
|
|
1010
1323
|
}
|
|
1011
|
-
return commitSchema.parse(
|
|
1324
|
+
return commitSchema.parse(log2.latest);
|
|
1012
1325
|
}
|
|
1013
1326
|
function getGitRoot(git = simpleGit()) {
|
|
1014
1327
|
return git.revparse("--show-toplevel");
|
|
@@ -1027,12 +1340,6 @@ function groupByStatus(results) {
|
|
|
1027
1340
|
);
|
|
1028
1341
|
}
|
|
1029
1342
|
|
|
1030
|
-
// packages/utils/src/lib/logging.ts
|
|
1031
|
-
import chalk2 from "chalk";
|
|
1032
|
-
function link(text) {
|
|
1033
|
-
return chalk2.underline(chalk2.blueBright(text));
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
1343
|
// packages/utils/src/lib/progress.ts
|
|
1037
1344
|
import chalk3 from "chalk";
|
|
1038
1345
|
import { MultiProgressBars } from "multi-progress-bars";
|
|
@@ -1088,80 +1395,16 @@ function getProgressBar(taskName) {
|
|
|
1088
1395
|
};
|
|
1089
1396
|
}
|
|
1090
1397
|
|
|
1091
|
-
// packages/utils/src/lib/reports/
|
|
1092
|
-
function
|
|
1093
|
-
return
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
</details>
|
|
1097
|
-
`;
|
|
1098
|
-
}
|
|
1099
|
-
|
|
1100
|
-
// packages/utils/src/lib/reports/md/font-style.ts
|
|
1101
|
-
var stylesMap = {
|
|
1102
|
-
i: "_",
|
|
1103
|
-
// italic
|
|
1104
|
-
b: "**",
|
|
1105
|
-
// bold
|
|
1106
|
-
s: "~",
|
|
1107
|
-
// strike through
|
|
1108
|
-
c: "`"
|
|
1109
|
-
// code
|
|
1110
|
-
};
|
|
1111
|
-
function style(text, styles = ["b"]) {
|
|
1112
|
-
return styles.reduce((t, s) => `${stylesMap[s]}${t}${stylesMap[s]}`, text);
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
// packages/utils/src/lib/reports/md/headline.ts
|
|
1116
|
-
function headline(text, hierarchy = 1) {
|
|
1117
|
-
return `${"#".repeat(hierarchy)} ${text}`;
|
|
1118
|
-
}
|
|
1119
|
-
function h2(text) {
|
|
1120
|
-
return headline(text, 2);
|
|
1121
|
-
}
|
|
1122
|
-
function h3(text) {
|
|
1123
|
-
return headline(text, 3);
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1126
|
-
// packages/utils/src/lib/reports/md/link.ts
|
|
1127
|
-
function link2(href, text) {
|
|
1128
|
-
return `[${text || href}](${href})`;
|
|
1129
|
-
}
|
|
1130
|
-
|
|
1131
|
-
// packages/utils/src/lib/reports/md/list.ts
|
|
1132
|
-
function li(text, order = "unordered") {
|
|
1133
|
-
const style2 = order === "unordered" ? "-" : "- [ ]";
|
|
1134
|
-
return `${style2} ${text}`;
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1137
|
-
// packages/utils/src/lib/reports/md/table.ts
|
|
1138
|
-
var alignString = /* @__PURE__ */ new Map([
|
|
1139
|
-
["l", ":--"],
|
|
1140
|
-
["c", ":--:"],
|
|
1141
|
-
["r", "--:"]
|
|
1142
|
-
]);
|
|
1143
|
-
function tableMd(data, align) {
|
|
1144
|
-
if (data.length === 0) {
|
|
1145
|
-
throw new Error("Data can't be empty");
|
|
1146
|
-
}
|
|
1147
|
-
const alignmentSetting = align ?? data[0]?.map(() => "c");
|
|
1148
|
-
const tableContent = data.map((arr) => `|${arr.join("|")}|`);
|
|
1149
|
-
const alignmentRow = `|${alignmentSetting?.map((s) => alignString.get(s)).join("|")}|`;
|
|
1150
|
-
return tableContent[0] + NEW_LINE + alignmentRow + NEW_LINE + tableContent.slice(1).join(NEW_LINE);
|
|
1398
|
+
// packages/utils/src/lib/reports/flatten-plugins.ts
|
|
1399
|
+
function listGroupsFromAllPlugins(report) {
|
|
1400
|
+
return report.plugins.flatMap(
|
|
1401
|
+
(plugin) => plugin.groups?.map((group) => ({ plugin, group })) ?? []
|
|
1402
|
+
);
|
|
1151
1403
|
}
|
|
1152
|
-
function
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
const tableContent = data.map((arr, index) => {
|
|
1157
|
-
if (index === 0) {
|
|
1158
|
-
const headerRow = arr.map((s) => `<th>${s}</th>`).join("");
|
|
1159
|
-
return `<tr>${headerRow}</tr>`;
|
|
1160
|
-
}
|
|
1161
|
-
const row = arr.map((s) => `<td>${s}</td>`).join("");
|
|
1162
|
-
return `<tr>${row}</tr>`;
|
|
1163
|
-
});
|
|
1164
|
-
return `<table>${tableContent.join("")}</table>`;
|
|
1404
|
+
function listAuditsFromAllPlugins(report) {
|
|
1405
|
+
return report.plugins.flatMap(
|
|
1406
|
+
(plugin) => plugin.audits.map((audit) => ({ plugin, audit }))
|
|
1407
|
+
);
|
|
1165
1408
|
}
|
|
1166
1409
|
|
|
1167
1410
|
// packages/utils/src/lib/reports/generate-md-report.ts
|
|
@@ -1327,54 +1570,285 @@ function reportToAboutSection(report) {
|
|
|
1327
1570
|
h2("About") + NEW_LINE + NEW_LINE + `Report was created by [Code PushUp](${README_LINK}) on ${date}.` + NEW_LINE + NEW_LINE + tableMd(reportMetaTable, ["l", "c", "c", "c", "c", "c"]) + NEW_LINE + NEW_LINE + "The following plugins were run:" + NEW_LINE + NEW_LINE + tableMd(pluginMetaTable, ["l", "c", "c", "c"])
|
|
1328
1571
|
);
|
|
1329
1572
|
}
|
|
1330
|
-
function getDocsAndDescription({
|
|
1331
|
-
docsUrl,
|
|
1332
|
-
description
|
|
1573
|
+
function getDocsAndDescription({
|
|
1574
|
+
docsUrl,
|
|
1575
|
+
description
|
|
1576
|
+
}) {
|
|
1577
|
+
if (docsUrl) {
|
|
1578
|
+
const docsLink = link2(docsUrl, "\u{1F4D6} Docs");
|
|
1579
|
+
if (!description) {
|
|
1580
|
+
return docsLink + NEW_LINE + NEW_LINE;
|
|
1581
|
+
}
|
|
1582
|
+
if (description.endsWith("```")) {
|
|
1583
|
+
return description + NEW_LINE + NEW_LINE + docsLink + NEW_LINE + NEW_LINE;
|
|
1584
|
+
}
|
|
1585
|
+
return `${description} ${docsLink}${NEW_LINE}${NEW_LINE}`;
|
|
1586
|
+
}
|
|
1587
|
+
if (description) {
|
|
1588
|
+
return description + NEW_LINE + NEW_LINE;
|
|
1589
|
+
}
|
|
1590
|
+
return "";
|
|
1591
|
+
}
|
|
1592
|
+
function getAuditResult(audit, isHtml = false) {
|
|
1593
|
+
const { displayValue, value } = audit;
|
|
1594
|
+
return isHtml ? `<b>${displayValue || value}</b>` : style(String(displayValue || value));
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
// packages/utils/src/lib/reports/generate-md-reports-diff.ts
|
|
1598
|
+
var MAX_ROWS = 100;
|
|
1599
|
+
function generateMdReportsDiff(diff) {
|
|
1600
|
+
return paragraphs(
|
|
1601
|
+
formatDiffHeaderSection(diff),
|
|
1602
|
+
formatDiffCategoriesSection(diff),
|
|
1603
|
+
formatDiffGroupsSection(diff),
|
|
1604
|
+
formatDiffAuditsSection(diff)
|
|
1605
|
+
);
|
|
1606
|
+
}
|
|
1607
|
+
function formatDiffHeaderSection(diff) {
|
|
1608
|
+
const outcomeTexts = {
|
|
1609
|
+
positive: `\u{1F973} Code PushUp report has ${style("improved")}`,
|
|
1610
|
+
negative: `\u{1F61F} Code PushUp report has ${style("regressed")}`,
|
|
1611
|
+
mixed: `\u{1F928} Code PushUp report has both ${style(
|
|
1612
|
+
"improvements and regressions"
|
|
1613
|
+
)}`,
|
|
1614
|
+
unchanged: `\u{1F610} Code PushUp report is ${style("unchanged")}`
|
|
1615
|
+
};
|
|
1616
|
+
const outcome = mergeDiffOutcomes(
|
|
1617
|
+
changesToDiffOutcomes([
|
|
1618
|
+
...diff.categories.changed,
|
|
1619
|
+
...diff.groups.changed,
|
|
1620
|
+
...diff.audits.changed
|
|
1621
|
+
])
|
|
1622
|
+
);
|
|
1623
|
+
const styleCommit = (commit) => style(commit.hash.slice(0, 7), ["c"]);
|
|
1624
|
+
const styleCommits = (commits) => {
|
|
1625
|
+
const src = styleCommit(commits.before);
|
|
1626
|
+
const tgt = styleCommit(commits.after);
|
|
1627
|
+
return `compared target commit ${tgt} with source commit ${src}`;
|
|
1628
|
+
};
|
|
1629
|
+
return paragraphs(
|
|
1630
|
+
h1("Code PushUp"),
|
|
1631
|
+
diff.commits ? `${outcomeTexts[outcome]} \u2013 ${styleCommits(diff.commits)}.` : `${outcomeTexts[outcome]}.`
|
|
1632
|
+
);
|
|
1633
|
+
}
|
|
1634
|
+
function formatDiffCategoriesSection(diff) {
|
|
1635
|
+
const { changed, unchanged, added } = diff.categories;
|
|
1636
|
+
const categoriesCount = changed.length + unchanged.length + added.length;
|
|
1637
|
+
const hasChanges = unchanged.length < categoriesCount;
|
|
1638
|
+
if (categoriesCount === 0) {
|
|
1639
|
+
return "";
|
|
1640
|
+
}
|
|
1641
|
+
return paragraphs(
|
|
1642
|
+
h2("\u{1F3F7}\uFE0F Categories"),
|
|
1643
|
+
categoriesCount > 0 && tableMd(
|
|
1644
|
+
[
|
|
1645
|
+
[
|
|
1646
|
+
"\u{1F3F7}\uFE0F Category",
|
|
1647
|
+
hasChanges ? "\u2B50 Current score" : "\u2B50 Score",
|
|
1648
|
+
"\u2B50 Previous score",
|
|
1649
|
+
"\u{1F504} Score change"
|
|
1650
|
+
],
|
|
1651
|
+
...sortChanges(changed).map((category) => [
|
|
1652
|
+
category.title,
|
|
1653
|
+
formatScoreWithColor(category.scores.after),
|
|
1654
|
+
formatScoreWithColor(category.scores.before, { skipBold: true }),
|
|
1655
|
+
formatScoreChange(category.scores.diff)
|
|
1656
|
+
]),
|
|
1657
|
+
...added.map((category) => [
|
|
1658
|
+
category.title,
|
|
1659
|
+
formatScoreWithColor(category.score),
|
|
1660
|
+
style("n/a (\\*)", ["i"]),
|
|
1661
|
+
style("n/a (\\*)", ["i"])
|
|
1662
|
+
]),
|
|
1663
|
+
...unchanged.map((category) => [
|
|
1664
|
+
category.title,
|
|
1665
|
+
formatScoreWithColor(category.score),
|
|
1666
|
+
formatScoreWithColor(category.score, { skipBold: true }),
|
|
1667
|
+
"\u2013"
|
|
1668
|
+
])
|
|
1669
|
+
].map((row) => hasChanges ? row : row.slice(0, 2)),
|
|
1670
|
+
hasChanges ? ["l", "c", "c", "c"] : ["l", "c"]
|
|
1671
|
+
),
|
|
1672
|
+
added.length > 0 && style("(\\*) New category.", ["i"])
|
|
1673
|
+
);
|
|
1674
|
+
}
|
|
1675
|
+
function formatDiffGroupsSection(diff) {
|
|
1676
|
+
if (diff.groups.changed.length + diff.groups.unchanged.length === 0) {
|
|
1677
|
+
return "";
|
|
1678
|
+
}
|
|
1679
|
+
return paragraphs(
|
|
1680
|
+
h2("\u{1F397}\uFE0F Groups"),
|
|
1681
|
+
formatGroupsOrAuditsDetails("group", diff.groups, {
|
|
1682
|
+
headings: [
|
|
1683
|
+
"\u{1F50C} Plugin",
|
|
1684
|
+
"\u{1F5C3}\uFE0F Group",
|
|
1685
|
+
"\u2B50 Current score",
|
|
1686
|
+
"\u2B50 Previous score",
|
|
1687
|
+
"\u{1F504} Score change"
|
|
1688
|
+
],
|
|
1689
|
+
rows: sortChanges(diff.groups.changed).map((group) => [
|
|
1690
|
+
group.plugin.title,
|
|
1691
|
+
group.title,
|
|
1692
|
+
formatScoreWithColor(group.scores.after),
|
|
1693
|
+
formatScoreWithColor(group.scores.before, { skipBold: true }),
|
|
1694
|
+
formatScoreChange(group.scores.diff)
|
|
1695
|
+
]),
|
|
1696
|
+
align: ["l", "l", "c", "c", "c"]
|
|
1697
|
+
})
|
|
1698
|
+
);
|
|
1699
|
+
}
|
|
1700
|
+
function formatDiffAuditsSection(diff) {
|
|
1701
|
+
return paragraphs(
|
|
1702
|
+
h2("\u{1F6E1}\uFE0F Audits"),
|
|
1703
|
+
formatGroupsOrAuditsDetails("audit", diff.audits, {
|
|
1704
|
+
headings: [
|
|
1705
|
+
"\u{1F50C} Plugin",
|
|
1706
|
+
"\u{1F6E1}\uFE0F Audit",
|
|
1707
|
+
"\u{1F4CF} Current value",
|
|
1708
|
+
"\u{1F4CF} Previous value",
|
|
1709
|
+
"\u{1F504} Value change"
|
|
1710
|
+
],
|
|
1711
|
+
rows: sortChanges(diff.audits.changed).map((audit) => [
|
|
1712
|
+
audit.plugin.title,
|
|
1713
|
+
audit.title,
|
|
1714
|
+
`${getSquaredScoreMarker(audit.scores.after)} ${style(
|
|
1715
|
+
audit.displayValues.after || audit.values.after.toString()
|
|
1716
|
+
)}`,
|
|
1717
|
+
`${getSquaredScoreMarker(audit.scores.before)} ${audit.displayValues.before || audit.values.before.toString()}`,
|
|
1718
|
+
formatValueChange(audit)
|
|
1719
|
+
]),
|
|
1720
|
+
align: ["l", "l", "c", "c", "c"]
|
|
1721
|
+
})
|
|
1722
|
+
);
|
|
1723
|
+
}
|
|
1724
|
+
function formatGroupsOrAuditsDetails(token, { changed, unchanged }, table) {
|
|
1725
|
+
return changed.length === 0 ? summarizeUnchanged(token, { changed, unchanged }) : details(
|
|
1726
|
+
summarizeDiffOutcomes(changesToDiffOutcomes(changed), token),
|
|
1727
|
+
paragraphs(
|
|
1728
|
+
tableMd(
|
|
1729
|
+
[table.headings, ...table.rows.slice(0, MAX_ROWS)],
|
|
1730
|
+
table.align
|
|
1731
|
+
),
|
|
1732
|
+
changed.length > MAX_ROWS && style(
|
|
1733
|
+
`Only the ${MAX_ROWS} most affected ${pluralize(
|
|
1734
|
+
token
|
|
1735
|
+
)} are listed above for brevity.`,
|
|
1736
|
+
["i"]
|
|
1737
|
+
),
|
|
1738
|
+
unchanged.length > 0 && summarizeUnchanged(token, { changed, unchanged })
|
|
1739
|
+
)
|
|
1740
|
+
);
|
|
1741
|
+
}
|
|
1742
|
+
function formatScoreChange(diff) {
|
|
1743
|
+
const marker = getDiffMarker(diff);
|
|
1744
|
+
const text = formatDiffNumber(Math.round(diff * 100));
|
|
1745
|
+
return colorByScoreDiff(`${marker} ${text}`, diff);
|
|
1746
|
+
}
|
|
1747
|
+
function formatValueChange({
|
|
1748
|
+
values,
|
|
1749
|
+
scores
|
|
1333
1750
|
}) {
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1751
|
+
const marker = getDiffMarker(values.diff);
|
|
1752
|
+
const percentage = values.before === 0 ? values.diff > 0 ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY : Math.round(100 * values.diff / values.before);
|
|
1753
|
+
const text = `${formatDiffNumber(percentage)}\u2009%`;
|
|
1754
|
+
return colorByScoreDiff(`${marker} ${text}`, scores.diff);
|
|
1755
|
+
}
|
|
1756
|
+
function summarizeUnchanged(token, { changed, unchanged }) {
|
|
1757
|
+
return [
|
|
1758
|
+
changed.length > 0 ? pluralizeToken(`other ${token}`, unchanged.length) : `All of ${pluralizeToken(token, unchanged.length)}`,
|
|
1759
|
+
unchanged.length === 1 ? "is" : "are",
|
|
1760
|
+
"unchanged."
|
|
1761
|
+
].join(" ");
|
|
1762
|
+
}
|
|
1763
|
+
function summarizeDiffOutcomes(outcomes, token) {
|
|
1764
|
+
return objectToEntries(countDiffOutcomes(outcomes)).filter(
|
|
1765
|
+
(entry) => entry[0] !== "unchanged" && entry[1] > 0
|
|
1766
|
+
).map(([outcome, count]) => {
|
|
1767
|
+
const formattedCount = `<strong>${count}</strong> ${pluralize(
|
|
1768
|
+
token,
|
|
1769
|
+
count
|
|
1770
|
+
)}`;
|
|
1771
|
+
switch (outcome) {
|
|
1772
|
+
case "positive":
|
|
1773
|
+
return `\u{1F44D} ${formattedCount} improved`;
|
|
1774
|
+
case "negative":
|
|
1775
|
+
return `\u{1F44E} ${formattedCount} regressed`;
|
|
1776
|
+
case "mixed":
|
|
1777
|
+
return `${formattedCount} changed without impacting score`;
|
|
1338
1778
|
}
|
|
1339
|
-
|
|
1340
|
-
|
|
1779
|
+
}).join(", ");
|
|
1780
|
+
}
|
|
1781
|
+
function sortChanges(changes) {
|
|
1782
|
+
return [...changes].sort(
|
|
1783
|
+
(a, b) => Math.abs(b.scores.diff) - Math.abs(a.scores.diff) || Math.abs(b.values?.diff ?? 0) - Math.abs(a.values?.diff ?? 0)
|
|
1784
|
+
);
|
|
1785
|
+
}
|
|
1786
|
+
function changesToDiffOutcomes(changes) {
|
|
1787
|
+
return changes.map((change) => {
|
|
1788
|
+
if (change.scores.diff > 0) {
|
|
1789
|
+
return "positive";
|
|
1341
1790
|
}
|
|
1342
|
-
|
|
1791
|
+
if (change.scores.diff < 0) {
|
|
1792
|
+
return "negative";
|
|
1793
|
+
}
|
|
1794
|
+
if (change.values != null && change.values.diff !== 0) {
|
|
1795
|
+
return "mixed";
|
|
1796
|
+
}
|
|
1797
|
+
return "unchanged";
|
|
1798
|
+
});
|
|
1799
|
+
}
|
|
1800
|
+
function mergeDiffOutcomes(outcomes) {
|
|
1801
|
+
if (outcomes.every((outcome) => outcome === "unchanged")) {
|
|
1802
|
+
return "unchanged";
|
|
1343
1803
|
}
|
|
1344
|
-
if (
|
|
1345
|
-
return
|
|
1804
|
+
if (outcomes.includes("positive") && !outcomes.includes("negative")) {
|
|
1805
|
+
return "positive";
|
|
1346
1806
|
}
|
|
1347
|
-
|
|
1807
|
+
if (outcomes.includes("negative") && !outcomes.includes("positive")) {
|
|
1808
|
+
return "negative";
|
|
1809
|
+
}
|
|
1810
|
+
return "mixed";
|
|
1348
1811
|
}
|
|
1349
|
-
function
|
|
1350
|
-
|
|
1351
|
-
|
|
1812
|
+
function countDiffOutcomes(outcomes) {
|
|
1813
|
+
return {
|
|
1814
|
+
positive: outcomes.filter((outcome) => outcome === "positive").length,
|
|
1815
|
+
negative: outcomes.filter((outcome) => outcome === "negative").length,
|
|
1816
|
+
mixed: outcomes.filter((outcome) => outcome === "mixed").length,
|
|
1817
|
+
unchanged: outcomes.filter((outcome) => outcome === "unchanged").length
|
|
1818
|
+
};
|
|
1352
1819
|
}
|
|
1353
1820
|
|
|
1354
|
-
// packages/utils/src/lib/reports/
|
|
1355
|
-
import cliui from "@isaacs/cliui";
|
|
1821
|
+
// packages/utils/src/lib/reports/log-stdout-summary.ts
|
|
1356
1822
|
import chalk4 from "chalk";
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
return line + NEW_LINE;
|
|
1823
|
+
function log(msg = "") {
|
|
1824
|
+
ui().logger.log(msg);
|
|
1360
1825
|
}
|
|
1361
|
-
function
|
|
1826
|
+
function logStdoutSummary(report) {
|
|
1362
1827
|
const printCategories = report.categories.length > 0;
|
|
1363
|
-
|
|
1828
|
+
log(reportToHeaderSection2(report));
|
|
1829
|
+
log();
|
|
1830
|
+
logPlugins(report);
|
|
1831
|
+
if (printCategories) {
|
|
1832
|
+
logCategories(report);
|
|
1833
|
+
}
|
|
1834
|
+
log(`${FOOTER_PREFIX} ${CODE_PUSHUP_DOMAIN}`);
|
|
1835
|
+
log();
|
|
1364
1836
|
}
|
|
1365
1837
|
function reportToHeaderSection2(report) {
|
|
1366
1838
|
const { packageName, version: version2 } = report;
|
|
1367
1839
|
return `${chalk4.bold(reportHeadlineText)} - ${packageName}@${version2}`;
|
|
1368
1840
|
}
|
|
1369
|
-
function
|
|
1841
|
+
function logPlugins(report) {
|
|
1370
1842
|
const { plugins } = report;
|
|
1371
|
-
|
|
1843
|
+
plugins.forEach((plugin) => {
|
|
1372
1844
|
const { title, audits } = plugin;
|
|
1373
|
-
|
|
1845
|
+
log();
|
|
1846
|
+
log(chalk4.magentaBright.bold(`${title} audits`));
|
|
1847
|
+
log();
|
|
1374
1848
|
audits.forEach((audit) => {
|
|
1375
|
-
|
|
1849
|
+
ui().row([
|
|
1376
1850
|
{
|
|
1377
|
-
text:
|
|
1851
|
+
text: applyScoreColor({ score: audit.score, text: "\u25CF" }),
|
|
1378
1852
|
width: 2,
|
|
1379
1853
|
padding: [0, 1, 0, 0]
|
|
1380
1854
|
},
|
|
@@ -1388,34 +1862,40 @@ function reportToDetailSection(report) {
|
|
|
1388
1862
|
width: 10,
|
|
1389
1863
|
padding: [0, 0, 0, 0]
|
|
1390
1864
|
}
|
|
1391
|
-
);
|
|
1865
|
+
]);
|
|
1392
1866
|
});
|
|
1393
|
-
|
|
1394
|
-
}, "");
|
|
1395
|
-
}
|
|
1396
|
-
function reportToOverviewSection2({
|
|
1397
|
-
categories,
|
|
1398
|
-
plugins
|
|
1399
|
-
}) {
|
|
1400
|
-
const table = new CliTable3({
|
|
1401
|
-
// eslint-disable-next-line no-magic-numbers
|
|
1402
|
-
colWidths: [TERMINAL_WIDTH - 7 - 8 - 4, 7, 8],
|
|
1403
|
-
head: reportRawOverviewTableHeaders,
|
|
1404
|
-
colAligns: ["left", "right", "right"],
|
|
1405
|
-
style: {
|
|
1406
|
-
head: ["cyan"]
|
|
1407
|
-
}
|
|
1867
|
+
log();
|
|
1408
1868
|
});
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1869
|
+
}
|
|
1870
|
+
function logCategories({ categories, plugins }) {
|
|
1871
|
+
const hAlign = (idx) => idx === 0 ? "left" : "right";
|
|
1872
|
+
const rows = categories.map(({ title, score, refs }) => [
|
|
1873
|
+
title,
|
|
1874
|
+
applyScoreColor({ score }),
|
|
1875
|
+
countCategoryAudits(refs, plugins)
|
|
1876
|
+
]);
|
|
1877
|
+
const table = ui().table();
|
|
1878
|
+
table.columnWidths([TERMINAL_WIDTH - 9 - 10 - 4, 9, 10]);
|
|
1879
|
+
table.head(
|
|
1880
|
+
reportRawOverviewTableHeaders.map((heading, idx) => ({
|
|
1881
|
+
content: chalk4.cyan(heading),
|
|
1882
|
+
hAlign: hAlign(idx)
|
|
1883
|
+
}))
|
|
1884
|
+
);
|
|
1885
|
+
rows.forEach(
|
|
1886
|
+
(row) => table.row(
|
|
1887
|
+
row.map((content, idx) => ({
|
|
1888
|
+
content: content.toString(),
|
|
1889
|
+
hAlign: hAlign(idx)
|
|
1890
|
+
}))
|
|
1891
|
+
)
|
|
1415
1892
|
);
|
|
1416
|
-
|
|
1893
|
+
log(chalk4.magentaBright.bold("Categories"));
|
|
1894
|
+
log();
|
|
1895
|
+
table.render();
|
|
1896
|
+
log();
|
|
1417
1897
|
}
|
|
1418
|
-
function
|
|
1898
|
+
function applyScoreColor({ score, text }) {
|
|
1419
1899
|
const formattedScore = text ?? formatReportScore(score);
|
|
1420
1900
|
const style2 = text ? chalk4 : chalk4.bold;
|
|
1421
1901
|
if (score >= SCORE_COLOR_RANGE.GREEN_MIN) {
|
|
@@ -1438,7 +1918,7 @@ var GroupRefInvalidError = class extends Error {
|
|
|
1438
1918
|
function scoreReport(report) {
|
|
1439
1919
|
const allScoredAuditsAndGroups = /* @__PURE__ */ new Map();
|
|
1440
1920
|
const scoredPlugins = report.plugins.map((plugin) => {
|
|
1441
|
-
const { groups, ...pluginProps } = plugin;
|
|
1921
|
+
const { groups: groups2, ...pluginProps } = plugin;
|
|
1442
1922
|
plugin.audits.forEach((audit) => {
|
|
1443
1923
|
allScoredAuditsAndGroups.set(`${plugin.slug}-${audit.slug}-audit`, audit);
|
|
1444
1924
|
});
|
|
@@ -1451,7 +1931,7 @@ function scoreReport(report) {
|
|
|
1451
1931
|
}
|
|
1452
1932
|
return score;
|
|
1453
1933
|
}
|
|
1454
|
-
const scoredGroups =
|
|
1934
|
+
const scoredGroups = groups2?.map((group) => ({
|
|
1455
1935
|
...group,
|
|
1456
1936
|
score: calculateScore(group.refs, groupScoreFn)
|
|
1457
1937
|
})) ?? [];
|
|
@@ -1518,7 +1998,7 @@ function parseScoringParameters(refs, scoreFn) {
|
|
|
1518
1998
|
function sortReport(report) {
|
|
1519
1999
|
const { categories, plugins } = report;
|
|
1520
2000
|
const sortedCategories = categories.map((category) => {
|
|
1521
|
-
const { audits, groups } = category.refs.reduce(
|
|
2001
|
+
const { audits, groups: groups2 } = category.refs.reduce(
|
|
1522
2002
|
(acc, ref) => ({
|
|
1523
2003
|
...acc,
|
|
1524
2004
|
...ref.type === "group" ? {
|
|
@@ -1529,7 +2009,7 @@ function sortReport(report) {
|
|
|
1529
2009
|
}),
|
|
1530
2010
|
{ groups: [], audits: [] }
|
|
1531
2011
|
);
|
|
1532
|
-
const sortedAuditsAndGroups = [...audits, ...
|
|
2012
|
+
const sortedAuditsAndGroups = [...audits, ...groups2].sort(
|
|
1533
2013
|
compareCategoryAuditsAndGroups
|
|
1534
2014
|
);
|
|
1535
2015
|
const sortedRefs = [...category.refs].sort((a, b) => {
|
|
@@ -1566,9 +2046,9 @@ function sortPlugins(plugins) {
|
|
|
1566
2046
|
|
|
1567
2047
|
// packages/utils/src/lib/verbose-utils.ts
|
|
1568
2048
|
function getLogVerbose(verbose = false) {
|
|
1569
|
-
return (
|
|
2049
|
+
return (msg) => {
|
|
1570
2050
|
if (verbose) {
|
|
1571
|
-
|
|
2051
|
+
ui().logger.info(msg);
|
|
1572
2052
|
}
|
|
1573
2053
|
};
|
|
1574
2054
|
}
|
|
@@ -1586,7 +2066,7 @@ var verboseUtils = (verbose = false) => ({
|
|
|
1586
2066
|
|
|
1587
2067
|
// packages/core/package.json
|
|
1588
2068
|
var name = "@code-pushup/core";
|
|
1589
|
-
var version = "0.
|
|
2069
|
+
var version = "0.28.0";
|
|
1590
2070
|
|
|
1591
2071
|
// packages/core/src/lib/implementation/execute-plugin.ts
|
|
1592
2072
|
import chalk5 from "chalk";
|
|
@@ -1656,7 +2136,7 @@ async function executePlugin(pluginConfig, onProgress) {
|
|
|
1656
2136
|
audits: pluginConfigAudits,
|
|
1657
2137
|
description,
|
|
1658
2138
|
docsUrl,
|
|
1659
|
-
groups,
|
|
2139
|
+
groups: groups2,
|
|
1660
2140
|
...pluginMeta
|
|
1661
2141
|
} = pluginConfig;
|
|
1662
2142
|
const runnerResult = typeof runner === "object" ? await executeRunnerConfig(runner, onProgress) : await executeRunnerFunction(runner, onProgress);
|
|
@@ -1678,7 +2158,7 @@ async function executePlugin(pluginConfig, onProgress) {
|
|
|
1678
2158
|
audits: auditReports,
|
|
1679
2159
|
...description && { description },
|
|
1680
2160
|
...docsUrl && { docsUrl },
|
|
1681
|
-
...
|
|
2161
|
+
...groups2 && { groups: groups2 }
|
|
1682
2162
|
};
|
|
1683
2163
|
}
|
|
1684
2164
|
async function executePlugins(plugins, options2) {
|
|
@@ -1699,11 +2179,9 @@ async function executePlugins(plugins, options2) {
|
|
|
1699
2179
|
}
|
|
1700
2180
|
}, Promise.resolve([]));
|
|
1701
2181
|
progressBar?.endProgress("Done running plugins");
|
|
1702
|
-
const
|
|
1703
|
-
console.error(reason);
|
|
1704
|
-
};
|
|
2182
|
+
const errorsTransform = ({ reason }) => String(reason);
|
|
1705
2183
|
const results = await Promise.allSettled(pluginsResult);
|
|
1706
|
-
logMultipleResults(results, "Plugins", void 0,
|
|
2184
|
+
logMultipleResults(results, "Plugins", void 0, errorsTransform);
|
|
1707
2185
|
const { fulfilled, rejected } = groupByStatus(results);
|
|
1708
2186
|
if (rejected.length > 0) {
|
|
1709
2187
|
const errorMessages = rejected.map(({ reason }) => String(reason)).join(", ");
|
|
@@ -1758,7 +2236,7 @@ var PersistError = class extends Error {
|
|
|
1758
2236
|
async function persistReport(report, options2) {
|
|
1759
2237
|
const { outputDir, filename, format } = options2;
|
|
1760
2238
|
const sortedScoredReport = sortReport(scoreReport(report));
|
|
1761
|
-
|
|
2239
|
+
logStdoutSummary(sortedScoredReport);
|
|
1762
2240
|
const results = format.map((reportType) => {
|
|
1763
2241
|
switch (reportType) {
|
|
1764
2242
|
case "json":
|
|
@@ -1777,7 +2255,7 @@ async function persistReport(report, options2) {
|
|
|
1777
2255
|
try {
|
|
1778
2256
|
await mkdir2(outputDir, { recursive: true });
|
|
1779
2257
|
} catch (error) {
|
|
1780
|
-
|
|
2258
|
+
ui().logger.warning(error.toString());
|
|
1781
2259
|
throw new PersistDirError(outputDir);
|
|
1782
2260
|
}
|
|
1783
2261
|
}
|
|
@@ -1792,7 +2270,7 @@ async function persistReport(report, options2) {
|
|
|
1792
2270
|
}
|
|
1793
2271
|
async function persistResult(reportPath, content) {
|
|
1794
2272
|
return writeFile(reportPath, content).then(() => stat2(reportPath)).then((stats) => [reportPath, stats.size]).catch((error) => {
|
|
1795
|
-
|
|
2273
|
+
ui().logger.warning(error.toString());
|
|
1796
2274
|
throw new PersistError(reportPath);
|
|
1797
2275
|
});
|
|
1798
2276
|
}
|
|
@@ -1813,8 +2291,209 @@ async function collectAndPersistReports(options2) {
|
|
|
1813
2291
|
});
|
|
1814
2292
|
}
|
|
1815
2293
|
|
|
1816
|
-
// packages/core/src/lib/
|
|
2294
|
+
// packages/core/src/lib/compare.ts
|
|
2295
|
+
import { writeFile as writeFile2 } from "node:fs/promises";
|
|
1817
2296
|
import { join as join5 } from "node:path";
|
|
2297
|
+
|
|
2298
|
+
// packages/core/src/lib/implementation/compare-scorables.ts
|
|
2299
|
+
function compareCategories(reports) {
|
|
2300
|
+
const { pairs, added, removed } = matchArrayItemsByKey({
|
|
2301
|
+
before: reports.before.categories,
|
|
2302
|
+
after: reports.after.categories,
|
|
2303
|
+
key: "slug"
|
|
2304
|
+
});
|
|
2305
|
+
const { changed, unchanged } = comparePairs(
|
|
2306
|
+
pairs,
|
|
2307
|
+
({ before, after }) => before.score === after.score
|
|
2308
|
+
);
|
|
2309
|
+
return {
|
|
2310
|
+
changed: changed.map(categoryPairToDiff),
|
|
2311
|
+
unchanged: unchanged.map(categoryToResult),
|
|
2312
|
+
added: added.map(categoryToResult),
|
|
2313
|
+
removed: removed.map(categoryToResult)
|
|
2314
|
+
};
|
|
2315
|
+
}
|
|
2316
|
+
function compareGroups(reports) {
|
|
2317
|
+
const { pairs, added, removed } = matchArrayItemsByKey({
|
|
2318
|
+
before: listGroupsFromAllPlugins(reports.before),
|
|
2319
|
+
after: listGroupsFromAllPlugins(reports.after),
|
|
2320
|
+
key: ({ plugin, group }) => `${plugin.slug}/${group.slug}`
|
|
2321
|
+
});
|
|
2322
|
+
const { changed, unchanged } = comparePairs(
|
|
2323
|
+
pairs,
|
|
2324
|
+
({ before, after }) => before.group.score === after.group.score
|
|
2325
|
+
);
|
|
2326
|
+
return {
|
|
2327
|
+
changed: changed.map(pluginGroupPairToDiff),
|
|
2328
|
+
unchanged: unchanged.map(pluginGroupToResult),
|
|
2329
|
+
added: added.map(pluginGroupToResult),
|
|
2330
|
+
removed: removed.map(pluginGroupToResult)
|
|
2331
|
+
};
|
|
2332
|
+
}
|
|
2333
|
+
function compareAudits2(reports) {
|
|
2334
|
+
const { pairs, added, removed } = matchArrayItemsByKey({
|
|
2335
|
+
before: listAuditsFromAllPlugins(reports.before),
|
|
2336
|
+
after: listAuditsFromAllPlugins(reports.after),
|
|
2337
|
+
key: ({ plugin, audit }) => `${plugin.slug}/${audit.slug}`
|
|
2338
|
+
});
|
|
2339
|
+
const { changed, unchanged } = comparePairs(
|
|
2340
|
+
pairs,
|
|
2341
|
+
({ before, after }) => before.audit.value === after.audit.value && before.audit.score === after.audit.score
|
|
2342
|
+
);
|
|
2343
|
+
return {
|
|
2344
|
+
changed: changed.map(pluginAuditPairToDiff),
|
|
2345
|
+
unchanged: unchanged.map(pluginAuditToResult),
|
|
2346
|
+
added: added.map(pluginAuditToResult),
|
|
2347
|
+
removed: removed.map(pluginAuditToResult)
|
|
2348
|
+
};
|
|
2349
|
+
}
|
|
2350
|
+
function categoryToResult(category) {
|
|
2351
|
+
return {
|
|
2352
|
+
slug: category.slug,
|
|
2353
|
+
title: category.title,
|
|
2354
|
+
score: category.score
|
|
2355
|
+
};
|
|
2356
|
+
}
|
|
2357
|
+
function categoryPairToDiff({
|
|
2358
|
+
before,
|
|
2359
|
+
after
|
|
2360
|
+
}) {
|
|
2361
|
+
return {
|
|
2362
|
+
slug: after.slug,
|
|
2363
|
+
title: after.title,
|
|
2364
|
+
scores: {
|
|
2365
|
+
before: before.score,
|
|
2366
|
+
after: after.score,
|
|
2367
|
+
diff: after.score - before.score
|
|
2368
|
+
}
|
|
2369
|
+
};
|
|
2370
|
+
}
|
|
2371
|
+
function pluginGroupToResult({ group, plugin }) {
|
|
2372
|
+
return {
|
|
2373
|
+
slug: group.slug,
|
|
2374
|
+
title: group.title,
|
|
2375
|
+
plugin: {
|
|
2376
|
+
slug: plugin.slug,
|
|
2377
|
+
title: plugin.title
|
|
2378
|
+
},
|
|
2379
|
+
score: group.score
|
|
2380
|
+
};
|
|
2381
|
+
}
|
|
2382
|
+
function pluginGroupPairToDiff({
|
|
2383
|
+
before,
|
|
2384
|
+
after
|
|
2385
|
+
}) {
|
|
2386
|
+
return {
|
|
2387
|
+
slug: after.group.slug,
|
|
2388
|
+
title: after.group.title,
|
|
2389
|
+
plugin: {
|
|
2390
|
+
slug: after.plugin.slug,
|
|
2391
|
+
title: after.plugin.title
|
|
2392
|
+
},
|
|
2393
|
+
scores: {
|
|
2394
|
+
before: before.group.score,
|
|
2395
|
+
after: after.group.score,
|
|
2396
|
+
diff: after.group.score - before.group.score
|
|
2397
|
+
}
|
|
2398
|
+
};
|
|
2399
|
+
}
|
|
2400
|
+
function pluginAuditToResult({ audit, plugin }) {
|
|
2401
|
+
return {
|
|
2402
|
+
slug: audit.slug,
|
|
2403
|
+
title: audit.title,
|
|
2404
|
+
plugin: {
|
|
2405
|
+
slug: plugin.slug,
|
|
2406
|
+
title: plugin.title
|
|
2407
|
+
},
|
|
2408
|
+
score: audit.score,
|
|
2409
|
+
value: audit.value,
|
|
2410
|
+
displayValue: audit.displayValue
|
|
2411
|
+
};
|
|
2412
|
+
}
|
|
2413
|
+
function pluginAuditPairToDiff({
|
|
2414
|
+
before,
|
|
2415
|
+
after
|
|
2416
|
+
}) {
|
|
2417
|
+
return {
|
|
2418
|
+
slug: after.audit.slug,
|
|
2419
|
+
title: after.audit.title,
|
|
2420
|
+
plugin: {
|
|
2421
|
+
slug: after.plugin.slug,
|
|
2422
|
+
title: after.plugin.title
|
|
2423
|
+
},
|
|
2424
|
+
scores: {
|
|
2425
|
+
before: before.audit.score,
|
|
2426
|
+
after: after.audit.score,
|
|
2427
|
+
diff: after.audit.score - before.audit.score
|
|
2428
|
+
},
|
|
2429
|
+
values: {
|
|
2430
|
+
before: before.audit.value,
|
|
2431
|
+
after: after.audit.value,
|
|
2432
|
+
diff: after.audit.value - before.audit.value
|
|
2433
|
+
},
|
|
2434
|
+
displayValues: {
|
|
2435
|
+
before: before.audit.displayValue,
|
|
2436
|
+
after: after.audit.displayValue
|
|
2437
|
+
}
|
|
2438
|
+
};
|
|
2439
|
+
}
|
|
2440
|
+
|
|
2441
|
+
// packages/core/src/lib/compare.ts
|
|
2442
|
+
async function compareReportFiles(inputPaths, persistConfig) {
|
|
2443
|
+
const { outputDir, filename, format } = persistConfig;
|
|
2444
|
+
const [reportBefore, reportAfter] = await Promise.all([
|
|
2445
|
+
readJsonFile(inputPaths.before),
|
|
2446
|
+
readJsonFile(inputPaths.after)
|
|
2447
|
+
]);
|
|
2448
|
+
const reports = {
|
|
2449
|
+
before: reportSchema.parse(reportBefore),
|
|
2450
|
+
after: reportSchema.parse(reportAfter)
|
|
2451
|
+
};
|
|
2452
|
+
const reportsDiff = compareReports(reports);
|
|
2453
|
+
return Promise.all(
|
|
2454
|
+
format.map(async (fmt) => {
|
|
2455
|
+
const outputPath = join5(outputDir, `${filename}-diff.${fmt}`);
|
|
2456
|
+
const content = reportsDiffToFileContent(reportsDiff, fmt);
|
|
2457
|
+
await ensureDirectoryExists(outputDir);
|
|
2458
|
+
await writeFile2(outputPath, content);
|
|
2459
|
+
return outputPath;
|
|
2460
|
+
})
|
|
2461
|
+
);
|
|
2462
|
+
}
|
|
2463
|
+
function compareReports(reports) {
|
|
2464
|
+
const start = performance.now();
|
|
2465
|
+
const date = (/* @__PURE__ */ new Date()).toISOString();
|
|
2466
|
+
const commits = reports.before.commit != null && reports.after.commit != null ? { before: reports.before.commit, after: reports.after.commit } : null;
|
|
2467
|
+
const scoredReports = {
|
|
2468
|
+
before: scoreReport(reports.before),
|
|
2469
|
+
after: scoreReport(reports.after)
|
|
2470
|
+
};
|
|
2471
|
+
const categories = compareCategories(scoredReports);
|
|
2472
|
+
const groups2 = compareGroups(scoredReports);
|
|
2473
|
+
const audits = compareAudits2(scoredReports);
|
|
2474
|
+
const duration = calcDuration(start);
|
|
2475
|
+
return {
|
|
2476
|
+
commits,
|
|
2477
|
+
categories,
|
|
2478
|
+
groups: groups2,
|
|
2479
|
+
audits,
|
|
2480
|
+
packageName: name,
|
|
2481
|
+
version,
|
|
2482
|
+
date,
|
|
2483
|
+
duration
|
|
2484
|
+
};
|
|
2485
|
+
}
|
|
2486
|
+
function reportsDiffToFileContent(reportsDiff, format) {
|
|
2487
|
+
switch (format) {
|
|
2488
|
+
case "json":
|
|
2489
|
+
return JSON.stringify(reportsDiff, null, 2);
|
|
2490
|
+
case "md":
|
|
2491
|
+
return generateMdReportsDiff(reportsDiff);
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
|
|
2495
|
+
// packages/core/src/lib/implementation/read-rc-file.ts
|
|
2496
|
+
import { join as join6 } from "node:path";
|
|
1818
2497
|
var ConfigPathError = class extends Error {
|
|
1819
2498
|
constructor(configPath) {
|
|
1820
2499
|
super(`Provided path '${configPath}' is not valid.`);
|
|
@@ -1848,7 +2527,7 @@ async function autoloadRc(tsconfig) {
|
|
|
1848
2527
|
);
|
|
1849
2528
|
}
|
|
1850
2529
|
return readRcByPath(
|
|
1851
|
-
|
|
2530
|
+
join6(process.cwd(), `${CONFIG_FILE_NAME}.${ext}`),
|
|
1852
2531
|
tsconfig
|
|
1853
2532
|
);
|
|
1854
2533
|
}
|
|
@@ -1986,15 +2665,7 @@ var CLI_NAME = "Code PushUp CLI";
|
|
|
1986
2665
|
var CLI_SCRIPT_NAME = "code-pushup";
|
|
1987
2666
|
|
|
1988
2667
|
// packages/cli/src/lib/implementation/logging.ts
|
|
1989
|
-
import { cliui as cliui2 } from "@poppinss/cliui";
|
|
1990
2668
|
import chalk6 from "chalk";
|
|
1991
|
-
var singletonUiInstance;
|
|
1992
|
-
function ui() {
|
|
1993
|
-
if (singletonUiInstance === void 0) {
|
|
1994
|
-
singletonUiInstance = cliui2();
|
|
1995
|
-
}
|
|
1996
|
-
return singletonUiInstance;
|
|
1997
|
-
}
|
|
1998
2669
|
function renderConfigureCategoriesHint() {
|
|
1999
2670
|
ui().logger.info(
|
|
2000
2671
|
chalk6.gray(
|
|
@@ -2031,51 +2702,12 @@ function renderIntegratePortalHint() {
|
|
|
2031
2702
|
).render();
|
|
2032
2703
|
}
|
|
2033
2704
|
|
|
2034
|
-
// packages/cli/src/lib/implementation/global.utils.ts
|
|
2035
|
-
function filterKebabCaseKeys(obj) {
|
|
2036
|
-
return Object.entries(obj).filter(([key]) => !key.includes("-")).reduce(
|
|
2037
|
-
(acc, [key, value]) => typeof value === "string" || typeof value === "object" && Array.isArray(obj[key]) ? { ...acc, [key]: value } : typeof value === "object" && !Array.isArray(value) && value != null ? {
|
|
2038
|
-
...acc,
|
|
2039
|
-
[key]: filterKebabCaseKeys(value)
|
|
2040
|
-
} : { ...acc, [key]: value },
|
|
2041
|
-
{}
|
|
2042
|
-
);
|
|
2043
|
-
}
|
|
2044
|
-
function logErrorBeforeThrow(fn) {
|
|
2045
|
-
return async (...args) => {
|
|
2046
|
-
try {
|
|
2047
|
-
return await fn(...args);
|
|
2048
|
-
} catch (error) {
|
|
2049
|
-
console.error(error);
|
|
2050
|
-
await new Promise((resolve) => process.stdout.write("", resolve));
|
|
2051
|
-
throw error;
|
|
2052
|
-
}
|
|
2053
|
-
};
|
|
2054
|
-
}
|
|
2055
|
-
function coerceArray(param) {
|
|
2056
|
-
return [...new Set(toArray(param).flatMap((f) => f.split(",")))];
|
|
2057
|
-
}
|
|
2058
|
-
|
|
2059
|
-
// packages/cli/src/lib/implementation/only-plugins.options.ts
|
|
2060
|
-
var onlyPluginsOption = {
|
|
2061
|
-
describe: "List of plugins to run. If not set all plugins are run.",
|
|
2062
|
-
type: "array",
|
|
2063
|
-
default: [],
|
|
2064
|
-
coerce: coerceArray
|
|
2065
|
-
};
|
|
2066
|
-
function yargsOnlyPluginsOptionsDefinition() {
|
|
2067
|
-
return {
|
|
2068
|
-
onlyPlugins: onlyPluginsOption
|
|
2069
|
-
};
|
|
2070
|
-
}
|
|
2071
|
-
|
|
2072
2705
|
// packages/cli/src/lib/autorun/autorun-command.ts
|
|
2073
2706
|
function yargsAutorunCommandObject() {
|
|
2074
2707
|
const command = "autorun";
|
|
2075
2708
|
return {
|
|
2076
2709
|
command,
|
|
2077
2710
|
describe: "Shortcut for running collect followed by upload",
|
|
2078
|
-
builder: yargsOnlyPluginsOptionsDefinition(),
|
|
2079
2711
|
handler: async (args) => {
|
|
2080
2712
|
ui().logger.log(chalk7.bold(CLI_NAME));
|
|
2081
2713
|
ui().logger.info(chalk7.gray(`Run ${command}...`));
|
|
@@ -2112,7 +2744,6 @@ function yargsCollectCommandObject() {
|
|
|
2112
2744
|
return {
|
|
2113
2745
|
command,
|
|
2114
2746
|
describe: "Run Plugins and collect results",
|
|
2115
|
-
builder: yargsOnlyPluginsOptionsDefinition(),
|
|
2116
2747
|
handler: async (args) => {
|
|
2117
2748
|
const options2 = args;
|
|
2118
2749
|
ui().logger.log(chalk8.bold(CLI_NAME));
|
|
@@ -2149,15 +2780,76 @@ function renderUploadAutorunHint() {
|
|
|
2149
2780
|
).render();
|
|
2150
2781
|
}
|
|
2151
2782
|
|
|
2783
|
+
// packages/cli/src/lib/compare/compare-command.ts
|
|
2784
|
+
import chalk9 from "chalk";
|
|
2785
|
+
|
|
2786
|
+
// packages/cli/src/lib/implementation/compare.options.ts
|
|
2787
|
+
function yargsCompareOptionsDefinition() {
|
|
2788
|
+
return {
|
|
2789
|
+
before: {
|
|
2790
|
+
describe: "Path to source report.json",
|
|
2791
|
+
type: "string",
|
|
2792
|
+
demandOption: true
|
|
2793
|
+
},
|
|
2794
|
+
after: {
|
|
2795
|
+
describe: "Path to target report.json",
|
|
2796
|
+
type: "string",
|
|
2797
|
+
demandOption: true
|
|
2798
|
+
}
|
|
2799
|
+
};
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2802
|
+
// packages/cli/src/lib/compare/compare-command.ts
|
|
2803
|
+
function yargsCompareCommandObject() {
|
|
2804
|
+
const command = "compare";
|
|
2805
|
+
return {
|
|
2806
|
+
command,
|
|
2807
|
+
describe: "Compare 2 report files and create a diff file",
|
|
2808
|
+
builder: yargsCompareOptionsDefinition(),
|
|
2809
|
+
handler: async (args) => {
|
|
2810
|
+
ui().logger.log(chalk9.bold(CLI_NAME));
|
|
2811
|
+
ui().logger.info(chalk9.gray(`Run ${command}...`));
|
|
2812
|
+
const options2 = args;
|
|
2813
|
+
const { before, after, persist } = options2;
|
|
2814
|
+
const outputPaths = await compareReportFiles({ before, after }, persist);
|
|
2815
|
+
ui().logger.info(
|
|
2816
|
+
`Reports diff written to ${outputPaths.map((path) => chalk9.bold(path)).join(" and ")}`
|
|
2817
|
+
);
|
|
2818
|
+
}
|
|
2819
|
+
};
|
|
2820
|
+
}
|
|
2821
|
+
|
|
2822
|
+
// packages/cli/src/lib/implementation/global.utils.ts
|
|
2823
|
+
function filterKebabCaseKeys(obj) {
|
|
2824
|
+
return Object.entries(obj).filter(([key]) => !key.includes("-")).reduce(
|
|
2825
|
+
(acc, [key, value]) => typeof value === "string" || typeof value === "object" && Array.isArray(obj[key]) ? { ...acc, [key]: value } : typeof value === "object" && !Array.isArray(value) && value != null ? {
|
|
2826
|
+
...acc,
|
|
2827
|
+
[key]: filterKebabCaseKeys(value)
|
|
2828
|
+
} : { ...acc, [key]: value },
|
|
2829
|
+
{}
|
|
2830
|
+
);
|
|
2831
|
+
}
|
|
2832
|
+
function logErrorBeforeThrow(fn) {
|
|
2833
|
+
return async (...args) => {
|
|
2834
|
+
try {
|
|
2835
|
+
return await fn(...args);
|
|
2836
|
+
} catch (error) {
|
|
2837
|
+
console.error(error);
|
|
2838
|
+
await new Promise((resolve) => process.stdout.write("", resolve));
|
|
2839
|
+
throw error;
|
|
2840
|
+
}
|
|
2841
|
+
};
|
|
2842
|
+
}
|
|
2843
|
+
function coerceArray(param) {
|
|
2844
|
+
return [...new Set(toArray(param).flatMap((f) => f.split(",")))];
|
|
2845
|
+
}
|
|
2846
|
+
|
|
2152
2847
|
// packages/cli/src/lib/print-config/print-config-command.ts
|
|
2153
2848
|
function yargsConfigCommandObject() {
|
|
2154
2849
|
const command = "print-config";
|
|
2155
2850
|
return {
|
|
2156
2851
|
command,
|
|
2157
2852
|
describe: "Print config",
|
|
2158
|
-
builder: {
|
|
2159
|
-
onlyPlugins: onlyPluginsOption
|
|
2160
|
-
},
|
|
2161
2853
|
handler: (yargsArgs) => {
|
|
2162
2854
|
const { _, $0, ...args } = yargsArgs;
|
|
2163
2855
|
const cleanArgs = filterKebabCaseKeys(args);
|
|
@@ -2167,15 +2859,15 @@ function yargsConfigCommandObject() {
|
|
|
2167
2859
|
}
|
|
2168
2860
|
|
|
2169
2861
|
// packages/cli/src/lib/upload/upload-command.ts
|
|
2170
|
-
import
|
|
2862
|
+
import chalk10 from "chalk";
|
|
2171
2863
|
function yargsUploadCommandObject() {
|
|
2172
2864
|
const command = "upload";
|
|
2173
2865
|
return {
|
|
2174
2866
|
command,
|
|
2175
2867
|
describe: "Upload report results to the portal",
|
|
2176
2868
|
handler: async (args) => {
|
|
2177
|
-
ui().logger.log(
|
|
2178
|
-
ui().logger.info(
|
|
2869
|
+
ui().logger.log(chalk10.bold(CLI_NAME));
|
|
2870
|
+
ui().logger.info(chalk10.gray(`Run ${command}...`));
|
|
2179
2871
|
const options2 = args;
|
|
2180
2872
|
if (options2.upload == null) {
|
|
2181
2873
|
renderIntegratePortalHint();
|
|
@@ -2196,6 +2888,7 @@ var commands = [
|
|
|
2196
2888
|
yargsAutorunCommandObject(),
|
|
2197
2889
|
yargsCollectCommandObject(),
|
|
2198
2890
|
yargsUploadCommandObject(),
|
|
2891
|
+
yargsCompareCommandObject(),
|
|
2199
2892
|
yargsConfigCommandObject()
|
|
2200
2893
|
];
|
|
2201
2894
|
|
|
@@ -2222,9 +2915,9 @@ async function coreConfigMiddleware(processArgs) {
|
|
|
2222
2915
|
return {
|
|
2223
2916
|
...config != null && { config },
|
|
2224
2917
|
persist: {
|
|
2225
|
-
outputDir: cliPersist?.outputDir ?? rcPersist?.outputDir ??
|
|
2226
|
-
|
|
2227
|
-
|
|
2918
|
+
outputDir: cliPersist?.outputDir ?? rcPersist?.outputDir ?? DEFAULT_PERSIST_OUTPUT_DIR,
|
|
2919
|
+
filename: cliPersist?.filename ?? rcPersist?.filename ?? DEFAULT_PERSIST_FILENAME,
|
|
2920
|
+
format: cliPersist?.format ?? rcPersist?.format ?? DEFAULT_PERSIST_FORMAT
|
|
2228
2921
|
},
|
|
2229
2922
|
...upload2 != null && { upload: upload2 },
|
|
2230
2923
|
categories: rcCategories ?? [],
|
|
@@ -2234,58 +2927,64 @@ async function coreConfigMiddleware(processArgs) {
|
|
|
2234
2927
|
}
|
|
2235
2928
|
|
|
2236
2929
|
// packages/cli/src/lib/implementation/only-plugins.utils.ts
|
|
2237
|
-
import
|
|
2238
|
-
function
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
}
|
|
2244
|
-
function filterCategoryByPluginSlug(categories, {
|
|
2245
|
-
onlyPlugins,
|
|
2930
|
+
import chalk11 from "chalk";
|
|
2931
|
+
function validateOnlyPluginsOption({
|
|
2932
|
+
plugins,
|
|
2933
|
+
categories
|
|
2934
|
+
}, {
|
|
2935
|
+
onlyPlugins = [],
|
|
2246
2936
|
verbose = false
|
|
2247
|
-
}) {
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
if (!onlyPlugins?.length) {
|
|
2252
|
-
return categories;
|
|
2253
|
-
}
|
|
2254
|
-
return categories.filter(
|
|
2255
|
-
(category) => category.refs.every((ref) => {
|
|
2256
|
-
const isNotSkipped = onlyPlugins.includes(ref.plugin);
|
|
2257
|
-
if (!isNotSkipped && verbose) {
|
|
2258
|
-
console.info(
|
|
2259
|
-
`${chalk10.yellow("\u26A0")} Category "${category.title}" is ignored because it references audits from skipped plugin "${ref.plugin}"`
|
|
2260
|
-
);
|
|
2261
|
-
}
|
|
2262
|
-
return isNotSkipped;
|
|
2263
|
-
})
|
|
2937
|
+
} = {}) {
|
|
2938
|
+
const onlyPluginsSet = new Set(onlyPlugins);
|
|
2939
|
+
const missingPlugins = onlyPlugins.filter(
|
|
2940
|
+
(plugin) => !plugins.some(({ slug }) => slug === plugin)
|
|
2264
2941
|
);
|
|
2265
|
-
}
|
|
2266
|
-
function validateOnlyPluginsOption(plugins, {
|
|
2267
|
-
onlyPlugins,
|
|
2268
|
-
verbose = false
|
|
2269
|
-
}) {
|
|
2270
|
-
const missingPlugins = onlyPlugins?.length ? onlyPlugins.filter((plugin) => !plugins.some(({ slug }) => slug === plugin)) : [];
|
|
2271
2942
|
if (missingPlugins.length > 0 && verbose) {
|
|
2272
|
-
|
|
2273
|
-
`${
|
|
2943
|
+
ui().logger.info(
|
|
2944
|
+
`${chalk11.yellow(
|
|
2274
2945
|
"\u26A0"
|
|
2275
2946
|
)} The --onlyPlugin argument references plugins with "${missingPlugins.join(
|
|
2276
2947
|
'", "'
|
|
2277
2948
|
)}" slugs, but no such plugins are present in the configuration. Expected one of the following plugin slugs: "${plugins.map(({ slug }) => slug).join('", "')}".`
|
|
2278
2949
|
);
|
|
2279
2950
|
}
|
|
2951
|
+
if (categories.length > 0) {
|
|
2952
|
+
const removedCategorieSlugs = filterItemRefsBy(
|
|
2953
|
+
categories,
|
|
2954
|
+
({ plugin }) => !onlyPluginsSet.has(plugin)
|
|
2955
|
+
).map(({ slug }) => slug);
|
|
2956
|
+
ui().logger.info(
|
|
2957
|
+
`The --onlyPlugin argument removed categories with "${removedCategorieSlugs.join(
|
|
2958
|
+
'", "'
|
|
2959
|
+
)}" slugs.
|
|
2960
|
+
`
|
|
2961
|
+
);
|
|
2962
|
+
}
|
|
2280
2963
|
}
|
|
2281
2964
|
|
|
2282
2965
|
// packages/cli/src/lib/implementation/only-plugins.middleware.ts
|
|
2283
|
-
function onlyPluginsMiddleware(
|
|
2284
|
-
|
|
2966
|
+
function onlyPluginsMiddleware(originalProcessArgs) {
|
|
2967
|
+
const { categories = [], onlyPlugins: originalOnlyPlugins } = originalProcessArgs;
|
|
2968
|
+
if (originalOnlyPlugins && originalOnlyPlugins.length > 0) {
|
|
2969
|
+
const { verbose, plugins, onlyPlugins } = originalProcessArgs;
|
|
2970
|
+
validateOnlyPluginsOption(
|
|
2971
|
+
{ plugins, categories },
|
|
2972
|
+
{ onlyPlugins, verbose }
|
|
2973
|
+
);
|
|
2974
|
+
const onlyPluginsSet = new Set(onlyPlugins);
|
|
2975
|
+
return {
|
|
2976
|
+
...originalProcessArgs,
|
|
2977
|
+
plugins: plugins.filter(({ slug }) => onlyPluginsSet.has(slug)),
|
|
2978
|
+
categories: filterItemRefsBy(
|
|
2979
|
+
categories,
|
|
2980
|
+
({ plugin }) => onlyPluginsSet.has(plugin)
|
|
2981
|
+
)
|
|
2982
|
+
};
|
|
2983
|
+
}
|
|
2285
2984
|
return {
|
|
2286
|
-
...
|
|
2287
|
-
|
|
2288
|
-
categories
|
|
2985
|
+
...originalProcessArgs,
|
|
2986
|
+
// if undefined fill categories with empty array
|
|
2987
|
+
categories
|
|
2289
2988
|
};
|
|
2290
2989
|
}
|
|
2291
2990
|
|
|
@@ -2369,20 +3068,44 @@ function yargsGlobalOptionsDefinition() {
|
|
|
2369
3068
|
};
|
|
2370
3069
|
}
|
|
2371
3070
|
|
|
3071
|
+
// packages/cli/src/lib/implementation/only-plugins.options.ts
|
|
3072
|
+
var onlyPluginsOption = {
|
|
3073
|
+
describe: "List of plugins to run. If not set all plugins are run.",
|
|
3074
|
+
type: "array",
|
|
3075
|
+
default: [],
|
|
3076
|
+
coerce: coerceArray
|
|
3077
|
+
};
|
|
3078
|
+
function yargsOnlyPluginsOptionsDefinition() {
|
|
3079
|
+
return {
|
|
3080
|
+
onlyPlugins: onlyPluginsOption
|
|
3081
|
+
};
|
|
3082
|
+
}
|
|
3083
|
+
|
|
2372
3084
|
// packages/cli/src/lib/options.ts
|
|
2373
3085
|
var options = {
|
|
2374
3086
|
...yargsGlobalOptionsDefinition(),
|
|
2375
|
-
...yargsCoreConfigOptionsDefinition()
|
|
3087
|
+
...yargsCoreConfigOptionsDefinition(),
|
|
3088
|
+
...yargsOnlyPluginsOptionsDefinition()
|
|
3089
|
+
};
|
|
3090
|
+
var groups = {
|
|
3091
|
+
"Global Options:": [
|
|
3092
|
+
...Object.keys(yargsGlobalOptionsDefinition()),
|
|
3093
|
+
...Object.keys(yargsOnlyPluginsOptionsDefinition())
|
|
3094
|
+
],
|
|
3095
|
+
"Persist Options:": Object.keys(yargsPersistConfigOptionsDefinition()),
|
|
3096
|
+
"Upload Options:": Object.keys(yargsUploadConfigOptionsDefinition())
|
|
2376
3097
|
};
|
|
2377
3098
|
|
|
2378
3099
|
// packages/cli/src/lib/yargs-cli.ts
|
|
2379
|
-
import
|
|
3100
|
+
import chalk12 from "chalk";
|
|
2380
3101
|
import yargs from "yargs";
|
|
2381
3102
|
function yargsCli(argv, cfg) {
|
|
2382
3103
|
const { usageMessage, scriptName, noExitProcess } = cfg;
|
|
2383
3104
|
const commands2 = cfg.commands ?? [];
|
|
2384
3105
|
const middlewares2 = cfg.middlewares ?? [];
|
|
2385
3106
|
const options2 = cfg.options ?? {};
|
|
3107
|
+
const groups2 = cfg.groups ?? {};
|
|
3108
|
+
const examples = cfg.examples ?? [];
|
|
2386
3109
|
const cli2 = yargs(argv);
|
|
2387
3110
|
cli2.help().version(false).alias("h", "help").check((args) => {
|
|
2388
3111
|
const persist = args["persist"];
|
|
@@ -2394,11 +3117,17 @@ function yargsCli(argv, cfg) {
|
|
|
2394
3117
|
(config) => Array.isArray(config) ? config.at(-1) : config
|
|
2395
3118
|
).options(options2).wrap(TERMINAL_WIDTH);
|
|
2396
3119
|
if (usageMessage) {
|
|
2397
|
-
cli2.usage(
|
|
3120
|
+
cli2.usage(chalk12.bold(usageMessage));
|
|
2398
3121
|
}
|
|
2399
3122
|
if (scriptName) {
|
|
2400
3123
|
cli2.scriptName(scriptName);
|
|
2401
3124
|
}
|
|
3125
|
+
examples.forEach(
|
|
3126
|
+
([exampleName, description]) => cli2.example(exampleName, description)
|
|
3127
|
+
);
|
|
3128
|
+
Object.entries(groups2).forEach(
|
|
3129
|
+
([groupName, optionNames]) => cli2.group(optionNames, groupName)
|
|
3130
|
+
);
|
|
2402
3131
|
middlewares2.forEach(({ middlewareFunction, applyBeforeValidation }) => {
|
|
2403
3132
|
cli2.middleware(
|
|
2404
3133
|
logErrorBeforeThrow(middlewareFunction),
|
|
@@ -2439,6 +3168,29 @@ var cli = (args) => yargsCli(args, {
|
|
|
2439
3168
|
usageMessage: CLI_NAME,
|
|
2440
3169
|
scriptName: CLI_SCRIPT_NAME,
|
|
2441
3170
|
options,
|
|
3171
|
+
groups,
|
|
3172
|
+
examples: [
|
|
3173
|
+
[
|
|
3174
|
+
"code-pushup",
|
|
3175
|
+
"Run collect followed by upload based on configuration from code-pushup.config.* file."
|
|
3176
|
+
],
|
|
3177
|
+
[
|
|
3178
|
+
"code-pushup collect --tsconfig=tsconfig.base.json",
|
|
3179
|
+
"Run collect using custom tsconfig to parse code-pushup.config.ts file."
|
|
3180
|
+
],
|
|
3181
|
+
[
|
|
3182
|
+
"code-pushup collect --onlyPlugins=coverage",
|
|
3183
|
+
"Run collect with only coverage plugin, other plugins from config file will be skipped."
|
|
3184
|
+
],
|
|
3185
|
+
[
|
|
3186
|
+
"code-pushup upload --persist.outputDir=dist --persist.filename=cp-report --upload.apiKey=$CP_API_KEY",
|
|
3187
|
+
"Upload dist/cp-report.json to portal using API key from environment variable"
|
|
3188
|
+
],
|
|
3189
|
+
[
|
|
3190
|
+
"code-pushup print-config --config code-pushup.config.test.js",
|
|
3191
|
+
"Print resolved config object parsed from custom config location"
|
|
3192
|
+
]
|
|
3193
|
+
],
|
|
2442
3194
|
middlewares,
|
|
2443
3195
|
commands
|
|
2444
3196
|
});
|