@code-pushup/core 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/index.js +866 -193
- package/package.json +3 -2
- package/src/index.d.ts +2 -0
- package/src/lib/collect-and-persist.d.ts +1 -1
- package/src/lib/compare.d.ts +4 -0
- package/src/lib/history.d.ts +15 -0
- package/src/lib/implementation/collect.d.ts +1 -1
- package/src/lib/implementation/compare-scorables.d.ts +6 -0
- package/src/lib/implementation/execute-plugin.d.ts +1 -1
- package/src/lib/upload.d.ts +1 -1
package/index.js
CHANGED
|
@@ -84,6 +84,9 @@ var descriptionSchema = z.string({ description: "Description (markdown)" }).max(
|
|
|
84
84
|
var urlSchema = z.string().url();
|
|
85
85
|
var docsUrlSchema = urlSchema.optional().or(z.literal("")).describe("Documentation site");
|
|
86
86
|
var titleSchema = z.string({ description: "Descriptive name" }).max(MAX_TITLE_LENGTH);
|
|
87
|
+
var scoreSchema = z.number({
|
|
88
|
+
description: "Value between 0 and 1"
|
|
89
|
+
}).min(0).max(1);
|
|
87
90
|
function metaSchema(options) {
|
|
88
91
|
const {
|
|
89
92
|
descriptionDescription,
|
|
@@ -215,6 +218,8 @@ var issueSchema = z3.object(
|
|
|
215
218
|
);
|
|
216
219
|
|
|
217
220
|
// packages/models/src/lib/audit-output.ts
|
|
221
|
+
var auditValueSchema = nonnegativeIntSchema.describe("Raw numeric value");
|
|
222
|
+
var auditDisplayValueSchema = z4.string({ description: "Formatted value (e.g. '0.9 s', '2.1 MB')" }).optional();
|
|
218
223
|
var auditDetailsSchema = z4.object(
|
|
219
224
|
{
|
|
220
225
|
issues: z4.array(issueSchema, { description: "List of findings" })
|
|
@@ -224,11 +229,9 @@ var auditDetailsSchema = z4.object(
|
|
|
224
229
|
var auditOutputSchema = z4.object(
|
|
225
230
|
{
|
|
226
231
|
slug: slugSchema.describe("Reference to audit"),
|
|
227
|
-
displayValue:
|
|
228
|
-
value:
|
|
229
|
-
score:
|
|
230
|
-
description: "Value between 0 and 1"
|
|
231
|
-
}).min(0).max(1),
|
|
232
|
+
displayValue: auditDisplayValueSchema,
|
|
233
|
+
value: auditValueSchema,
|
|
234
|
+
score: scoreSchema,
|
|
232
235
|
details: auditDetailsSchema.optional()
|
|
233
236
|
},
|
|
234
237
|
{ description: "Audit information" }
|
|
@@ -555,6 +558,138 @@ var reportSchema = packageVersionSchema({
|
|
|
555
558
|
})
|
|
556
559
|
);
|
|
557
560
|
|
|
561
|
+
// packages/models/src/lib/reports-diff.ts
|
|
562
|
+
import { z as z14 } from "zod";
|
|
563
|
+
function makeComparisonSchema(schema) {
|
|
564
|
+
const sharedDescription = schema.description || "Result";
|
|
565
|
+
return z14.object({
|
|
566
|
+
before: schema.describe(`${sharedDescription} (source commit)`),
|
|
567
|
+
after: schema.describe(`${sharedDescription} (target commit)`)
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
function makeArraysComparisonSchema(diffSchema, resultSchema, description) {
|
|
571
|
+
return z14.object(
|
|
572
|
+
{
|
|
573
|
+
changed: z14.array(diffSchema),
|
|
574
|
+
unchanged: z14.array(resultSchema),
|
|
575
|
+
added: z14.array(resultSchema),
|
|
576
|
+
removed: z14.array(resultSchema)
|
|
577
|
+
},
|
|
578
|
+
{ description }
|
|
579
|
+
);
|
|
580
|
+
}
|
|
581
|
+
var scorableMetaSchema = z14.object({ slug: slugSchema, title: titleSchema });
|
|
582
|
+
var scorableWithPluginMetaSchema = scorableMetaSchema.merge(
|
|
583
|
+
z14.object({
|
|
584
|
+
plugin: pluginMetaSchema.pick({ slug: true, title: true }).describe("Plugin which defines it")
|
|
585
|
+
})
|
|
586
|
+
);
|
|
587
|
+
var scorableDiffSchema = scorableMetaSchema.merge(
|
|
588
|
+
z14.object({
|
|
589
|
+
scores: makeComparisonSchema(scoreSchema).merge(
|
|
590
|
+
z14.object({
|
|
591
|
+
diff: z14.number().min(-1).max(1).describe("Score change (`scores.after - scores.before`)")
|
|
592
|
+
})
|
|
593
|
+
).describe("Score comparison")
|
|
594
|
+
})
|
|
595
|
+
);
|
|
596
|
+
var scorableWithPluginDiffSchema = scorableDiffSchema.merge(
|
|
597
|
+
scorableWithPluginMetaSchema
|
|
598
|
+
);
|
|
599
|
+
var categoryDiffSchema = scorableDiffSchema;
|
|
600
|
+
var groupDiffSchema = scorableWithPluginDiffSchema;
|
|
601
|
+
var auditDiffSchema = scorableWithPluginDiffSchema.merge(
|
|
602
|
+
z14.object({
|
|
603
|
+
values: makeComparisonSchema(auditValueSchema).merge(
|
|
604
|
+
z14.object({
|
|
605
|
+
diff: z14.number().int().describe("Value change (`values.after - values.before`)")
|
|
606
|
+
})
|
|
607
|
+
).describe("Audit `value` comparison"),
|
|
608
|
+
displayValues: makeComparisonSchema(auditDisplayValueSchema).describe(
|
|
609
|
+
"Audit `displayValue` comparison"
|
|
610
|
+
)
|
|
611
|
+
})
|
|
612
|
+
);
|
|
613
|
+
var categoryResultSchema = scorableMetaSchema.merge(
|
|
614
|
+
z14.object({ score: scoreSchema })
|
|
615
|
+
);
|
|
616
|
+
var groupResultSchema = scorableWithPluginMetaSchema.merge(
|
|
617
|
+
z14.object({ score: scoreSchema })
|
|
618
|
+
);
|
|
619
|
+
var auditResultSchema = scorableWithPluginMetaSchema.merge(
|
|
620
|
+
auditOutputSchema.pick({ score: true, value: true, displayValue: true })
|
|
621
|
+
);
|
|
622
|
+
var reportsDiffSchema = z14.object({
|
|
623
|
+
commits: makeComparisonSchema(commitSchema).nullable().describe("Commits identifying compared reports"),
|
|
624
|
+
categories: makeArraysComparisonSchema(
|
|
625
|
+
categoryDiffSchema,
|
|
626
|
+
categoryResultSchema,
|
|
627
|
+
"Changes affecting categories"
|
|
628
|
+
),
|
|
629
|
+
groups: makeArraysComparisonSchema(
|
|
630
|
+
groupDiffSchema,
|
|
631
|
+
groupResultSchema,
|
|
632
|
+
"Changes affecting groups"
|
|
633
|
+
),
|
|
634
|
+
audits: makeArraysComparisonSchema(
|
|
635
|
+
auditDiffSchema,
|
|
636
|
+
auditResultSchema,
|
|
637
|
+
"Changes affecting audits"
|
|
638
|
+
)
|
|
639
|
+
}).merge(
|
|
640
|
+
packageVersionSchema({
|
|
641
|
+
versionDescription: "NPM version of the CLI (when `compare` was run)",
|
|
642
|
+
required: true
|
|
643
|
+
})
|
|
644
|
+
).merge(
|
|
645
|
+
executionMetaSchema({
|
|
646
|
+
descriptionDate: "Start date and time of the compare run",
|
|
647
|
+
descriptionDuration: "Duration of the compare run in ms"
|
|
648
|
+
})
|
|
649
|
+
);
|
|
650
|
+
|
|
651
|
+
// packages/utils/src/lib/diff.ts
|
|
652
|
+
function matchArrayItemsByKey({
|
|
653
|
+
before,
|
|
654
|
+
after,
|
|
655
|
+
key
|
|
656
|
+
}) {
|
|
657
|
+
const pairs = [];
|
|
658
|
+
const added = [];
|
|
659
|
+
const afterKeys = /* @__PURE__ */ new Set();
|
|
660
|
+
const keyFn = typeof key === "function" ? key : (item) => item[key];
|
|
661
|
+
for (const afterItem of after) {
|
|
662
|
+
const afterKey = keyFn(afterItem);
|
|
663
|
+
afterKeys.add(afterKey);
|
|
664
|
+
const match = before.find((beforeItem) => keyFn(beforeItem) === afterKey);
|
|
665
|
+
if (match) {
|
|
666
|
+
pairs.push({ before: match, after: afterItem });
|
|
667
|
+
} else {
|
|
668
|
+
added.push(afterItem);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
const removed = before.filter(
|
|
672
|
+
(beforeItem) => !afterKeys.has(keyFn(beforeItem))
|
|
673
|
+
);
|
|
674
|
+
return {
|
|
675
|
+
pairs,
|
|
676
|
+
added,
|
|
677
|
+
removed
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
function comparePairs(pairs, equalsFn) {
|
|
681
|
+
return pairs.reduce(
|
|
682
|
+
(acc, pair) => ({
|
|
683
|
+
...acc,
|
|
684
|
+
...equalsFn(pair) ? { unchanged: [...acc.unchanged, pair.after] } : { changed: [...acc.changed, pair] }
|
|
685
|
+
}),
|
|
686
|
+
{
|
|
687
|
+
changed: [],
|
|
688
|
+
unchanged: []
|
|
689
|
+
}
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
|
|
558
693
|
// packages/utils/src/lib/execute-process.ts
|
|
559
694
|
import { spawn } from "node:child_process";
|
|
560
695
|
|
|
@@ -563,13 +698,25 @@ import { join } from "node:path";
|
|
|
563
698
|
|
|
564
699
|
// packages/utils/src/lib/file-system.ts
|
|
565
700
|
import { bundleRequire } from "bundle-require";
|
|
566
|
-
import
|
|
701
|
+
import chalk2 from "chalk";
|
|
567
702
|
import { mkdir, readFile, readdir, rm, stat } from "node:fs/promises";
|
|
568
703
|
|
|
569
704
|
// packages/utils/src/lib/formatting.ts
|
|
570
705
|
function slugify(text) {
|
|
571
706
|
return text.trim().toLowerCase().replace(/\s+|\//g, "-").replace(/[^a-z\d-]/g, "");
|
|
572
707
|
}
|
|
708
|
+
function pluralize(text, amount) {
|
|
709
|
+
if (amount != null && Math.abs(amount) === 1) {
|
|
710
|
+
return text;
|
|
711
|
+
}
|
|
712
|
+
if (text.endsWith("y")) {
|
|
713
|
+
return `${text.slice(0, -1)}ies`;
|
|
714
|
+
}
|
|
715
|
+
if (text.endsWith("s")) {
|
|
716
|
+
return `${text}es`;
|
|
717
|
+
}
|
|
718
|
+
return `${text}s`;
|
|
719
|
+
}
|
|
573
720
|
function formatBytes(bytes, decimals = 2) {
|
|
574
721
|
const positiveBytes = Math.max(bytes, 0);
|
|
575
722
|
if (positiveBytes === 0) {
|
|
@@ -581,6 +728,9 @@ function formatBytes(bytes, decimals = 2) {
|
|
|
581
728
|
const i = Math.floor(Math.log(positiveBytes) / Math.log(k));
|
|
582
729
|
return `${Number.parseFloat((positiveBytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
|
|
583
730
|
}
|
|
731
|
+
function pluralizeToken(token, times) {
|
|
732
|
+
return `${times} ${Math.abs(times) === 1 ? token : pluralize(token)}`;
|
|
733
|
+
}
|
|
584
734
|
function formatDuration(duration) {
|
|
585
735
|
if (duration < 1e3) {
|
|
586
736
|
return `${duration} ms`;
|
|
@@ -608,33 +758,103 @@ function isPromiseRejectedResult(result) {
|
|
|
608
758
|
return result.status === "rejected";
|
|
609
759
|
}
|
|
610
760
|
|
|
761
|
+
// packages/utils/src/lib/logging.ts
|
|
762
|
+
import isaacs_cliui from "@isaacs/cliui";
|
|
763
|
+
import { cliui } from "@poppinss/cliui";
|
|
764
|
+
import chalk from "chalk";
|
|
765
|
+
|
|
766
|
+
// packages/utils/src/lib/reports/constants.ts
|
|
767
|
+
var TERMINAL_WIDTH = 80;
|
|
768
|
+
var NEW_LINE = "\n";
|
|
769
|
+
var SCORE_COLOR_RANGE = {
|
|
770
|
+
GREEN_MIN: 0.9,
|
|
771
|
+
YELLOW_MIN: 0.5
|
|
772
|
+
};
|
|
773
|
+
var FOOTER_PREFIX = "Made with \u2764 by";
|
|
774
|
+
var CODE_PUSHUP_DOMAIN = "code-pushup.dev";
|
|
775
|
+
var README_LINK = "https://github.com/flowup/quality-metrics-cli#readme";
|
|
776
|
+
var reportHeadlineText = "Code PushUp Report";
|
|
777
|
+
var reportOverviewTableHeaders = [
|
|
778
|
+
"\u{1F3F7} Category",
|
|
779
|
+
"\u2B50 Score",
|
|
780
|
+
"\u{1F6E1} Audits"
|
|
781
|
+
];
|
|
782
|
+
var reportRawOverviewTableHeaders = ["Category", "Score", "Audits"];
|
|
783
|
+
var reportMetaTableHeaders = [
|
|
784
|
+
"Commit",
|
|
785
|
+
"Version",
|
|
786
|
+
"Duration",
|
|
787
|
+
"Plugins",
|
|
788
|
+
"Categories",
|
|
789
|
+
"Audits"
|
|
790
|
+
];
|
|
791
|
+
var pluginMetaTableHeaders = [
|
|
792
|
+
"Plugin",
|
|
793
|
+
"Audits",
|
|
794
|
+
"Version",
|
|
795
|
+
"Duration"
|
|
796
|
+
];
|
|
797
|
+
var detailsTableHeaders = [
|
|
798
|
+
"Severity",
|
|
799
|
+
"Message",
|
|
800
|
+
"Source file",
|
|
801
|
+
"Line(s)"
|
|
802
|
+
];
|
|
803
|
+
|
|
804
|
+
// packages/utils/src/lib/logging.ts
|
|
805
|
+
var singletonUiInstance;
|
|
806
|
+
function ui() {
|
|
807
|
+
if (singletonUiInstance === void 0) {
|
|
808
|
+
singletonUiInstance = cliui();
|
|
809
|
+
}
|
|
810
|
+
return {
|
|
811
|
+
...singletonUiInstance,
|
|
812
|
+
row: (args) => {
|
|
813
|
+
logListItem(args);
|
|
814
|
+
}
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
var singletonisaacUi;
|
|
818
|
+
function logListItem(args) {
|
|
819
|
+
if (singletonisaacUi === void 0) {
|
|
820
|
+
singletonisaacUi = isaacs_cliui({ width: TERMINAL_WIDTH });
|
|
821
|
+
}
|
|
822
|
+
singletonisaacUi.div(...args);
|
|
823
|
+
const content = singletonisaacUi.toString();
|
|
824
|
+
singletonisaacUi.rows = [];
|
|
825
|
+
singletonUiInstance?.logger.log(content);
|
|
826
|
+
}
|
|
827
|
+
|
|
611
828
|
// packages/utils/src/lib/log-results.ts
|
|
612
|
-
function logMultipleResults(results, messagePrefix,
|
|
613
|
-
if (
|
|
829
|
+
function logMultipleResults(results, messagePrefix, succeededTransform, failedTransform) {
|
|
830
|
+
if (succeededTransform) {
|
|
614
831
|
const succeededResults = results.filter(isPromiseFulfilledResult);
|
|
615
832
|
logPromiseResults(
|
|
616
833
|
succeededResults,
|
|
617
834
|
`${messagePrefix} successfully: `,
|
|
618
|
-
|
|
835
|
+
succeededTransform
|
|
619
836
|
);
|
|
620
837
|
}
|
|
621
|
-
if (
|
|
838
|
+
if (failedTransform) {
|
|
622
839
|
const failedResults = results.filter(isPromiseRejectedResult);
|
|
623
840
|
logPromiseResults(
|
|
624
841
|
failedResults,
|
|
625
842
|
`${messagePrefix} failed: `,
|
|
626
|
-
|
|
843
|
+
failedTransform
|
|
627
844
|
);
|
|
628
845
|
}
|
|
629
846
|
}
|
|
630
|
-
function logPromiseResults(results, logMessage,
|
|
847
|
+
function logPromiseResults(results, logMessage, getMsg) {
|
|
631
848
|
if (results.length > 0) {
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
}
|
|
637
|
-
|
|
849
|
+
const log2 = results[0]?.status === "fulfilled" ? (m) => {
|
|
850
|
+
ui().logger.success(m);
|
|
851
|
+
} : (m) => {
|
|
852
|
+
ui().logger.warning(m);
|
|
853
|
+
};
|
|
854
|
+
log2(logMessage);
|
|
855
|
+
results.forEach((result) => {
|
|
856
|
+
log2(getMsg(result));
|
|
857
|
+
});
|
|
638
858
|
}
|
|
639
859
|
}
|
|
640
860
|
|
|
@@ -668,26 +888,24 @@ async function ensureDirectoryExists(baseDir) {
|
|
|
668
888
|
await mkdir(baseDir, { recursive: true });
|
|
669
889
|
return;
|
|
670
890
|
} catch (error) {
|
|
671
|
-
|
|
891
|
+
ui().logger.error(error.message);
|
|
672
892
|
if (error.code !== "EEXIST") {
|
|
673
893
|
throw error;
|
|
674
894
|
}
|
|
675
895
|
}
|
|
676
896
|
}
|
|
677
897
|
function logMultipleFileResults(fileResults, messagePrefix) {
|
|
678
|
-
const
|
|
898
|
+
const succeededTransform = (result) => {
|
|
679
899
|
const [fileName, size] = result.value;
|
|
680
|
-
const formattedSize = size ? ` (${
|
|
681
|
-
|
|
682
|
-
};
|
|
683
|
-
const failedCallback = (result) => {
|
|
684
|
-
console.warn(`- ${chalk.bold(result.reason)}`);
|
|
900
|
+
const formattedSize = size ? ` (${chalk2.gray(formatBytes(size))})` : "";
|
|
901
|
+
return `- ${chalk2.bold(fileName)}${formattedSize}`;
|
|
685
902
|
};
|
|
903
|
+
const failedTransform = (result) => `- ${chalk2.bold(result.reason)}`;
|
|
686
904
|
logMultipleResults(
|
|
687
905
|
fileResults,
|
|
688
906
|
messagePrefix,
|
|
689
|
-
|
|
690
|
-
|
|
907
|
+
succeededTransform,
|
|
908
|
+
failedTransform
|
|
691
909
|
);
|
|
692
910
|
}
|
|
693
911
|
var NoExportError = class extends Error {
|
|
@@ -706,48 +924,105 @@ async function importEsmModule(options) {
|
|
|
706
924
|
return mod.default;
|
|
707
925
|
}
|
|
708
926
|
|
|
709
|
-
// packages/utils/src/lib/reports/
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
927
|
+
// packages/utils/src/lib/reports/md/details.ts
|
|
928
|
+
function details(title, content, cfg = { open: false }) {
|
|
929
|
+
return `<details${cfg.open ? " open" : ""}>
|
|
930
|
+
<summary>${title}</summary>
|
|
931
|
+
|
|
932
|
+
${content}
|
|
933
|
+
|
|
934
|
+
</details>
|
|
935
|
+
`;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
// packages/utils/src/lib/reports/md/font-style.ts
|
|
939
|
+
var stylesMap = {
|
|
940
|
+
i: "_",
|
|
941
|
+
// italic
|
|
942
|
+
b: "**",
|
|
943
|
+
// bold
|
|
944
|
+
s: "~",
|
|
945
|
+
// strike through
|
|
946
|
+
c: "`"
|
|
947
|
+
// code
|
|
715
948
|
};
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
"
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
949
|
+
function style(text, styles = ["b"]) {
|
|
950
|
+
return styles.reduce((t, s) => `${stylesMap[s]}${t}${stylesMap[s]}`, text);
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
// packages/utils/src/lib/reports/md/headline.ts
|
|
954
|
+
function headline(text, hierarchy = 1) {
|
|
955
|
+
return `${"#".repeat(hierarchy)} ${text}`;
|
|
956
|
+
}
|
|
957
|
+
function h1(text) {
|
|
958
|
+
return headline(text, 1);
|
|
959
|
+
}
|
|
960
|
+
function h2(text) {
|
|
961
|
+
return headline(text, 2);
|
|
962
|
+
}
|
|
963
|
+
function h3(text) {
|
|
964
|
+
return headline(text, 3);
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
// packages/utils/src/lib/reports/md/image.ts
|
|
968
|
+
function image(src, alt) {
|
|
969
|
+
return ``;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
// packages/utils/src/lib/reports/md/link.ts
|
|
973
|
+
function link(href, text) {
|
|
974
|
+
return `[${text || href}](${href})`;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
// packages/utils/src/lib/reports/md/list.ts
|
|
978
|
+
function li(text, order = "unordered") {
|
|
979
|
+
const style2 = order === "unordered" ? "-" : "- [ ]";
|
|
980
|
+
return `${style2} ${text}`;
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
// packages/utils/src/lib/reports/md/paragraphs.ts
|
|
984
|
+
function paragraphs(...sections) {
|
|
985
|
+
return sections.filter(Boolean).join("\n\n");
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
// packages/utils/src/lib/reports/md/table.ts
|
|
989
|
+
var alignString = /* @__PURE__ */ new Map([
|
|
990
|
+
["l", ":--"],
|
|
991
|
+
["c", ":--:"],
|
|
992
|
+
["r", "--:"]
|
|
993
|
+
]);
|
|
994
|
+
function tableMd(data, align) {
|
|
995
|
+
if (data.length === 0) {
|
|
996
|
+
throw new Error("Data can't be empty");
|
|
997
|
+
}
|
|
998
|
+
const alignmentSetting = align ?? data[0]?.map(() => "c");
|
|
999
|
+
const tableContent = data.map((arr) => `|${arr.join("|")}|`);
|
|
1000
|
+
const alignmentRow = `|${alignmentSetting?.map((s) => alignString.get(s)).join("|")}|`;
|
|
1001
|
+
return tableContent[0] + NEW_LINE + alignmentRow + NEW_LINE + tableContent.slice(1).join(NEW_LINE);
|
|
1002
|
+
}
|
|
1003
|
+
function tableHtml(data) {
|
|
1004
|
+
if (data.length === 0) {
|
|
1005
|
+
throw new Error("Data can't be empty");
|
|
1006
|
+
}
|
|
1007
|
+
const tableContent = data.map((arr, index) => {
|
|
1008
|
+
if (index === 0) {
|
|
1009
|
+
const headerRow = arr.map((s) => `<th>${s}</th>`).join("");
|
|
1010
|
+
return `<tr>${headerRow}</tr>`;
|
|
1011
|
+
}
|
|
1012
|
+
const row = arr.map((s) => `<td>${s}</td>`).join("");
|
|
1013
|
+
return `<tr>${row}</tr>`;
|
|
1014
|
+
});
|
|
1015
|
+
return `<table>${tableContent.join("")}</table>`;
|
|
1016
|
+
}
|
|
746
1017
|
|
|
747
1018
|
// packages/utils/src/lib/reports/utils.ts
|
|
748
1019
|
function formatReportScore(score) {
|
|
749
1020
|
return Math.round(score * 100).toString();
|
|
750
1021
|
}
|
|
1022
|
+
function formatScoreWithColor(score, options) {
|
|
1023
|
+
const styledNumber = options?.skipBold ? formatReportScore(score) : style(formatReportScore(score));
|
|
1024
|
+
return `${getRoundScoreMarker(score)} ${styledNumber}`;
|
|
1025
|
+
}
|
|
751
1026
|
function getRoundScoreMarker(score) {
|
|
752
1027
|
if (score >= SCORE_COLOR_RANGE.GREEN_MIN) {
|
|
753
1028
|
return "\u{1F7E2}";
|
|
@@ -766,6 +1041,30 @@ function getSquaredScoreMarker(score) {
|
|
|
766
1041
|
}
|
|
767
1042
|
return "\u{1F7E5}";
|
|
768
1043
|
}
|
|
1044
|
+
function getDiffMarker(diff) {
|
|
1045
|
+
if (diff > 0) {
|
|
1046
|
+
return "\u2191";
|
|
1047
|
+
}
|
|
1048
|
+
if (diff < 0) {
|
|
1049
|
+
return "\u2193";
|
|
1050
|
+
}
|
|
1051
|
+
return "";
|
|
1052
|
+
}
|
|
1053
|
+
function colorByScoreDiff(text, diff) {
|
|
1054
|
+
const color = diff > 0 ? "green" : diff < 0 ? "red" : "gray";
|
|
1055
|
+
return shieldsBadge(text, color);
|
|
1056
|
+
}
|
|
1057
|
+
function shieldsBadge(text, color) {
|
|
1058
|
+
return image(
|
|
1059
|
+
`https://img.shields.io/badge/${encodeURIComponent(text)}-${color}`,
|
|
1060
|
+
text
|
|
1061
|
+
);
|
|
1062
|
+
}
|
|
1063
|
+
function formatDiffNumber(diff) {
|
|
1064
|
+
const number = Math.abs(diff) === Number.POSITIVE_INFINITY ? "\u221E" : `${Math.abs(diff)}`;
|
|
1065
|
+
const sign = diff < 0 ? "\u2212" : "+";
|
|
1066
|
+
return `${sign}${number}`;
|
|
1067
|
+
}
|
|
769
1068
|
function getSeverityIcon(severity) {
|
|
770
1069
|
if (severity === "error") {
|
|
771
1070
|
return "\u{1F6A8}";
|
|
@@ -776,7 +1075,7 @@ function getSeverityIcon(severity) {
|
|
|
776
1075
|
return "\u2139\uFE0F";
|
|
777
1076
|
}
|
|
778
1077
|
function calcDuration(start, stop) {
|
|
779
|
-
return Math.
|
|
1078
|
+
return Math.round((stop ?? performance.now()) - start);
|
|
780
1079
|
}
|
|
781
1080
|
function countCategoryAudits(refs, plugins) {
|
|
782
1081
|
const groupLookup = plugins.reduce(
|
|
@@ -939,7 +1238,7 @@ var ProcessError = class extends Error {
|
|
|
939
1238
|
}
|
|
940
1239
|
};
|
|
941
1240
|
function executeProcess(cfg) {
|
|
942
|
-
const { observer, cwd, command, args } = cfg;
|
|
1241
|
+
const { observer, cwd, command, args, alwaysResolve = false } = cfg;
|
|
943
1242
|
const { onStdout, onError, onComplete } = observer ?? {};
|
|
944
1243
|
const date = (/* @__PURE__ */ new Date()).toISOString();
|
|
945
1244
|
const start = performance.now();
|
|
@@ -959,7 +1258,7 @@ function executeProcess(cfg) {
|
|
|
959
1258
|
});
|
|
960
1259
|
process2.on("close", (code) => {
|
|
961
1260
|
const timings = { date, duration: calcDuration(start) };
|
|
962
|
-
if (code === 0) {
|
|
1261
|
+
if (code === 0 || alwaysResolve) {
|
|
963
1262
|
onComplete?.();
|
|
964
1263
|
resolve({ code, stdout, stderr, ...timings });
|
|
965
1264
|
} else {
|
|
@@ -976,6 +1275,9 @@ import { isAbsolute, join as join2, relative } from "node:path";
|
|
|
976
1275
|
import { simpleGit } from "simple-git";
|
|
977
1276
|
|
|
978
1277
|
// packages/utils/src/lib/transform.ts
|
|
1278
|
+
function objectToEntries(obj) {
|
|
1279
|
+
return Object.entries(obj);
|
|
1280
|
+
}
|
|
979
1281
|
function deepClone(obj) {
|
|
980
1282
|
return obj == null || typeof obj !== "object" ? obj : structuredClone(obj);
|
|
981
1283
|
}
|
|
@@ -985,14 +1287,14 @@ function toUnixPath(path) {
|
|
|
985
1287
|
|
|
986
1288
|
// packages/utils/src/lib/git.ts
|
|
987
1289
|
async function getLatestCommit(git = simpleGit()) {
|
|
988
|
-
const
|
|
1290
|
+
const log2 = await git.log({
|
|
989
1291
|
maxCount: 1,
|
|
990
1292
|
format: { hash: "%H", message: "%s", author: "%an", date: "%aI" }
|
|
991
1293
|
});
|
|
992
|
-
if (!
|
|
1294
|
+
if (!log2.latest) {
|
|
993
1295
|
return null;
|
|
994
1296
|
}
|
|
995
|
-
return commitSchema.parse(
|
|
1297
|
+
return commitSchema.parse(log2.latest);
|
|
996
1298
|
}
|
|
997
1299
|
function getGitRoot(git = simpleGit()) {
|
|
998
1300
|
return git.revparse("--show-toplevel");
|
|
@@ -1011,9 +1313,6 @@ function groupByStatus(results) {
|
|
|
1011
1313
|
);
|
|
1012
1314
|
}
|
|
1013
1315
|
|
|
1014
|
-
// packages/utils/src/lib/logging.ts
|
|
1015
|
-
import chalk2 from "chalk";
|
|
1016
|
-
|
|
1017
1316
|
// packages/utils/src/lib/progress.ts
|
|
1018
1317
|
import chalk3 from "chalk";
|
|
1019
1318
|
import { MultiProgressBars } from "multi-progress-bars";
|
|
@@ -1069,80 +1368,16 @@ function getProgressBar(taskName) {
|
|
|
1069
1368
|
};
|
|
1070
1369
|
}
|
|
1071
1370
|
|
|
1072
|
-
// packages/utils/src/lib/reports/
|
|
1073
|
-
function
|
|
1074
|
-
return
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
</details>
|
|
1078
|
-
`;
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
// packages/utils/src/lib/reports/md/font-style.ts
|
|
1082
|
-
var stylesMap = {
|
|
1083
|
-
i: "_",
|
|
1084
|
-
// italic
|
|
1085
|
-
b: "**",
|
|
1086
|
-
// bold
|
|
1087
|
-
s: "~",
|
|
1088
|
-
// strike through
|
|
1089
|
-
c: "`"
|
|
1090
|
-
// code
|
|
1091
|
-
};
|
|
1092
|
-
function style(text, styles = ["b"]) {
|
|
1093
|
-
return styles.reduce((t, s) => `${stylesMap[s]}${t}${stylesMap[s]}`, text);
|
|
1094
|
-
}
|
|
1095
|
-
|
|
1096
|
-
// packages/utils/src/lib/reports/md/headline.ts
|
|
1097
|
-
function headline(text, hierarchy = 1) {
|
|
1098
|
-
return `${"#".repeat(hierarchy)} ${text}`;
|
|
1099
|
-
}
|
|
1100
|
-
function h2(text) {
|
|
1101
|
-
return headline(text, 2);
|
|
1102
|
-
}
|
|
1103
|
-
function h3(text) {
|
|
1104
|
-
return headline(text, 3);
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
// packages/utils/src/lib/reports/md/link.ts
|
|
1108
|
-
function link(href, text) {
|
|
1109
|
-
return `[${text || href}](${href})`;
|
|
1110
|
-
}
|
|
1111
|
-
|
|
1112
|
-
// packages/utils/src/lib/reports/md/list.ts
|
|
1113
|
-
function li(text, order = "unordered") {
|
|
1114
|
-
const style2 = order === "unordered" ? "-" : "- [ ]";
|
|
1115
|
-
return `${style2} ${text}`;
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
|
-
// packages/utils/src/lib/reports/md/table.ts
|
|
1119
|
-
var alignString = /* @__PURE__ */ new Map([
|
|
1120
|
-
["l", ":--"],
|
|
1121
|
-
["c", ":--:"],
|
|
1122
|
-
["r", "--:"]
|
|
1123
|
-
]);
|
|
1124
|
-
function tableMd(data, align) {
|
|
1125
|
-
if (data.length === 0) {
|
|
1126
|
-
throw new Error("Data can't be empty");
|
|
1127
|
-
}
|
|
1128
|
-
const alignmentSetting = align ?? data[0]?.map(() => "c");
|
|
1129
|
-
const tableContent = data.map((arr) => `|${arr.join("|")}|`);
|
|
1130
|
-
const alignmentRow = `|${alignmentSetting?.map((s) => alignString.get(s)).join("|")}|`;
|
|
1131
|
-
return tableContent[0] + NEW_LINE + alignmentRow + NEW_LINE + tableContent.slice(1).join(NEW_LINE);
|
|
1371
|
+
// packages/utils/src/lib/reports/flatten-plugins.ts
|
|
1372
|
+
function listGroupsFromAllPlugins(report) {
|
|
1373
|
+
return report.plugins.flatMap(
|
|
1374
|
+
(plugin) => plugin.groups?.map((group) => ({ plugin, group })) ?? []
|
|
1375
|
+
);
|
|
1132
1376
|
}
|
|
1133
|
-
function
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
const tableContent = data.map((arr, index) => {
|
|
1138
|
-
if (index === 0) {
|
|
1139
|
-
const headerRow = arr.map((s) => `<th>${s}</th>`).join("");
|
|
1140
|
-
return `<tr>${headerRow}</tr>`;
|
|
1141
|
-
}
|
|
1142
|
-
const row = arr.map((s) => `<td>${s}</td>`).join("");
|
|
1143
|
-
return `<tr>${row}</tr>`;
|
|
1144
|
-
});
|
|
1145
|
-
return `<table>${tableContent.join("")}</table>`;
|
|
1377
|
+
function listAuditsFromAllPlugins(report) {
|
|
1378
|
+
return report.plugins.flatMap(
|
|
1379
|
+
(plugin) => plugin.audits.map((audit) => ({ plugin, audit }))
|
|
1380
|
+
);
|
|
1146
1381
|
}
|
|
1147
1382
|
|
|
1148
1383
|
// packages/utils/src/lib/reports/generate-md-report.ts
|
|
@@ -1332,30 +1567,261 @@ function getAuditResult(audit, isHtml = false) {
|
|
|
1332
1567
|
return isHtml ? `<b>${displayValue || value}</b>` : style(String(displayValue || value));
|
|
1333
1568
|
}
|
|
1334
1569
|
|
|
1335
|
-
// packages/utils/src/lib/reports/generate-
|
|
1336
|
-
|
|
1570
|
+
// packages/utils/src/lib/reports/generate-md-reports-diff.ts
|
|
1571
|
+
var MAX_ROWS = 100;
|
|
1572
|
+
function generateMdReportsDiff(diff) {
|
|
1573
|
+
return paragraphs(
|
|
1574
|
+
formatDiffHeaderSection(diff),
|
|
1575
|
+
formatDiffCategoriesSection(diff),
|
|
1576
|
+
formatDiffGroupsSection(diff),
|
|
1577
|
+
formatDiffAuditsSection(diff)
|
|
1578
|
+
);
|
|
1579
|
+
}
|
|
1580
|
+
function formatDiffHeaderSection(diff) {
|
|
1581
|
+
const outcomeTexts = {
|
|
1582
|
+
positive: `\u{1F973} Code PushUp report has ${style("improved")}`,
|
|
1583
|
+
negative: `\u{1F61F} Code PushUp report has ${style("regressed")}`,
|
|
1584
|
+
mixed: `\u{1F928} Code PushUp report has both ${style(
|
|
1585
|
+
"improvements and regressions"
|
|
1586
|
+
)}`,
|
|
1587
|
+
unchanged: `\u{1F610} Code PushUp report is ${style("unchanged")}`
|
|
1588
|
+
};
|
|
1589
|
+
const outcome = mergeDiffOutcomes(
|
|
1590
|
+
changesToDiffOutcomes([
|
|
1591
|
+
...diff.categories.changed,
|
|
1592
|
+
...diff.groups.changed,
|
|
1593
|
+
...diff.audits.changed
|
|
1594
|
+
])
|
|
1595
|
+
);
|
|
1596
|
+
const styleCommit = (commit) => style(commit.hash.slice(0, 7), ["c"]);
|
|
1597
|
+
const styleCommits = (commits) => {
|
|
1598
|
+
const src = styleCommit(commits.before);
|
|
1599
|
+
const tgt = styleCommit(commits.after);
|
|
1600
|
+
return `compared target commit ${tgt} with source commit ${src}`;
|
|
1601
|
+
};
|
|
1602
|
+
return paragraphs(
|
|
1603
|
+
h1("Code PushUp"),
|
|
1604
|
+
diff.commits ? `${outcomeTexts[outcome]} \u2013 ${styleCommits(diff.commits)}.` : `${outcomeTexts[outcome]}.`
|
|
1605
|
+
);
|
|
1606
|
+
}
|
|
1607
|
+
function formatDiffCategoriesSection(diff) {
|
|
1608
|
+
const { changed, unchanged, added } = diff.categories;
|
|
1609
|
+
const categoriesCount = changed.length + unchanged.length + added.length;
|
|
1610
|
+
const hasChanges = unchanged.length < categoriesCount;
|
|
1611
|
+
if (categoriesCount === 0) {
|
|
1612
|
+
return "";
|
|
1613
|
+
}
|
|
1614
|
+
return paragraphs(
|
|
1615
|
+
h2("\u{1F3F7}\uFE0F Categories"),
|
|
1616
|
+
categoriesCount > 0 && tableMd(
|
|
1617
|
+
[
|
|
1618
|
+
[
|
|
1619
|
+
"\u{1F3F7}\uFE0F Category",
|
|
1620
|
+
hasChanges ? "\u2B50 Current score" : "\u2B50 Score",
|
|
1621
|
+
"\u2B50 Previous score",
|
|
1622
|
+
"\u{1F504} Score change"
|
|
1623
|
+
],
|
|
1624
|
+
...sortChanges(changed).map((category) => [
|
|
1625
|
+
category.title,
|
|
1626
|
+
formatScoreWithColor(category.scores.after),
|
|
1627
|
+
formatScoreWithColor(category.scores.before, { skipBold: true }),
|
|
1628
|
+
formatScoreChange(category.scores.diff)
|
|
1629
|
+
]),
|
|
1630
|
+
...added.map((category) => [
|
|
1631
|
+
category.title,
|
|
1632
|
+
formatScoreWithColor(category.score),
|
|
1633
|
+
style("n/a (\\*)", ["i"]),
|
|
1634
|
+
style("n/a (\\*)", ["i"])
|
|
1635
|
+
]),
|
|
1636
|
+
...unchanged.map((category) => [
|
|
1637
|
+
category.title,
|
|
1638
|
+
formatScoreWithColor(category.score),
|
|
1639
|
+
formatScoreWithColor(category.score, { skipBold: true }),
|
|
1640
|
+
"\u2013"
|
|
1641
|
+
])
|
|
1642
|
+
].map((row) => hasChanges ? row : row.slice(0, 2)),
|
|
1643
|
+
hasChanges ? ["l", "c", "c", "c"] : ["l", "c"]
|
|
1644
|
+
),
|
|
1645
|
+
added.length > 0 && style("(\\*) New category.", ["i"])
|
|
1646
|
+
);
|
|
1647
|
+
}
|
|
1648
|
+
function formatDiffGroupsSection(diff) {
|
|
1649
|
+
if (diff.groups.changed.length + diff.groups.unchanged.length === 0) {
|
|
1650
|
+
return "";
|
|
1651
|
+
}
|
|
1652
|
+
return paragraphs(
|
|
1653
|
+
h2("\u{1F397}\uFE0F Groups"),
|
|
1654
|
+
formatGroupsOrAuditsDetails("group", diff.groups, {
|
|
1655
|
+
headings: [
|
|
1656
|
+
"\u{1F50C} Plugin",
|
|
1657
|
+
"\u{1F5C3}\uFE0F Group",
|
|
1658
|
+
"\u2B50 Current score",
|
|
1659
|
+
"\u2B50 Previous score",
|
|
1660
|
+
"\u{1F504} Score change"
|
|
1661
|
+
],
|
|
1662
|
+
rows: sortChanges(diff.groups.changed).map((group) => [
|
|
1663
|
+
group.plugin.title,
|
|
1664
|
+
group.title,
|
|
1665
|
+
formatScoreWithColor(group.scores.after),
|
|
1666
|
+
formatScoreWithColor(group.scores.before, { skipBold: true }),
|
|
1667
|
+
formatScoreChange(group.scores.diff)
|
|
1668
|
+
]),
|
|
1669
|
+
align: ["l", "l", "c", "c", "c"]
|
|
1670
|
+
})
|
|
1671
|
+
);
|
|
1672
|
+
}
|
|
1673
|
+
function formatDiffAuditsSection(diff) {
|
|
1674
|
+
return paragraphs(
|
|
1675
|
+
h2("\u{1F6E1}\uFE0F Audits"),
|
|
1676
|
+
formatGroupsOrAuditsDetails("audit", diff.audits, {
|
|
1677
|
+
headings: [
|
|
1678
|
+
"\u{1F50C} Plugin",
|
|
1679
|
+
"\u{1F6E1}\uFE0F Audit",
|
|
1680
|
+
"\u{1F4CF} Current value",
|
|
1681
|
+
"\u{1F4CF} Previous value",
|
|
1682
|
+
"\u{1F504} Value change"
|
|
1683
|
+
],
|
|
1684
|
+
rows: sortChanges(diff.audits.changed).map((audit) => [
|
|
1685
|
+
audit.plugin.title,
|
|
1686
|
+
audit.title,
|
|
1687
|
+
`${getSquaredScoreMarker(audit.scores.after)} ${style(
|
|
1688
|
+
audit.displayValues.after || audit.values.after.toString()
|
|
1689
|
+
)}`,
|
|
1690
|
+
`${getSquaredScoreMarker(audit.scores.before)} ${audit.displayValues.before || audit.values.before.toString()}`,
|
|
1691
|
+
formatValueChange(audit)
|
|
1692
|
+
]),
|
|
1693
|
+
align: ["l", "l", "c", "c", "c"]
|
|
1694
|
+
})
|
|
1695
|
+
);
|
|
1696
|
+
}
|
|
1697
|
+
function formatGroupsOrAuditsDetails(token, { changed, unchanged }, table) {
|
|
1698
|
+
return changed.length === 0 ? summarizeUnchanged(token, { changed, unchanged }) : details(
|
|
1699
|
+
summarizeDiffOutcomes(changesToDiffOutcomes(changed), token),
|
|
1700
|
+
paragraphs(
|
|
1701
|
+
tableMd(
|
|
1702
|
+
[table.headings, ...table.rows.slice(0, MAX_ROWS)],
|
|
1703
|
+
table.align
|
|
1704
|
+
),
|
|
1705
|
+
changed.length > MAX_ROWS && style(
|
|
1706
|
+
`Only the ${MAX_ROWS} most affected ${pluralize(
|
|
1707
|
+
token
|
|
1708
|
+
)} are listed above for brevity.`,
|
|
1709
|
+
["i"]
|
|
1710
|
+
),
|
|
1711
|
+
unchanged.length > 0 && summarizeUnchanged(token, { changed, unchanged })
|
|
1712
|
+
)
|
|
1713
|
+
);
|
|
1714
|
+
}
|
|
1715
|
+
function formatScoreChange(diff) {
|
|
1716
|
+
const marker = getDiffMarker(diff);
|
|
1717
|
+
const text = formatDiffNumber(Math.round(diff * 100));
|
|
1718
|
+
return colorByScoreDiff(`${marker} ${text}`, diff);
|
|
1719
|
+
}
|
|
1720
|
+
function formatValueChange({
|
|
1721
|
+
values,
|
|
1722
|
+
scores
|
|
1723
|
+
}) {
|
|
1724
|
+
const marker = getDiffMarker(values.diff);
|
|
1725
|
+
const percentage = values.before === 0 ? values.diff > 0 ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY : Math.round(100 * values.diff / values.before);
|
|
1726
|
+
const text = `${formatDiffNumber(percentage)}\u2009%`;
|
|
1727
|
+
return colorByScoreDiff(`${marker} ${text}`, scores.diff);
|
|
1728
|
+
}
|
|
1729
|
+
function summarizeUnchanged(token, { changed, unchanged }) {
|
|
1730
|
+
return [
|
|
1731
|
+
changed.length > 0 ? pluralizeToken(`other ${token}`, unchanged.length) : `All of ${pluralizeToken(token, unchanged.length)}`,
|
|
1732
|
+
unchanged.length === 1 ? "is" : "are",
|
|
1733
|
+
"unchanged."
|
|
1734
|
+
].join(" ");
|
|
1735
|
+
}
|
|
1736
|
+
function summarizeDiffOutcomes(outcomes, token) {
|
|
1737
|
+
return objectToEntries(countDiffOutcomes(outcomes)).filter(
|
|
1738
|
+
(entry) => entry[0] !== "unchanged" && entry[1] > 0
|
|
1739
|
+
).map(([outcome, count]) => {
|
|
1740
|
+
const formattedCount = `<strong>${count}</strong> ${pluralize(
|
|
1741
|
+
token,
|
|
1742
|
+
count
|
|
1743
|
+
)}`;
|
|
1744
|
+
switch (outcome) {
|
|
1745
|
+
case "positive":
|
|
1746
|
+
return `\u{1F44D} ${formattedCount} improved`;
|
|
1747
|
+
case "negative":
|
|
1748
|
+
return `\u{1F44E} ${formattedCount} regressed`;
|
|
1749
|
+
case "mixed":
|
|
1750
|
+
return `${formattedCount} changed without impacting score`;
|
|
1751
|
+
}
|
|
1752
|
+
}).join(", ");
|
|
1753
|
+
}
|
|
1754
|
+
function sortChanges(changes) {
|
|
1755
|
+
return [...changes].sort(
|
|
1756
|
+
(a, b) => Math.abs(b.scores.diff) - Math.abs(a.scores.diff) || Math.abs(b.values?.diff ?? 0) - Math.abs(a.values?.diff ?? 0)
|
|
1757
|
+
);
|
|
1758
|
+
}
|
|
1759
|
+
function changesToDiffOutcomes(changes) {
|
|
1760
|
+
return changes.map((change) => {
|
|
1761
|
+
if (change.scores.diff > 0) {
|
|
1762
|
+
return "positive";
|
|
1763
|
+
}
|
|
1764
|
+
if (change.scores.diff < 0) {
|
|
1765
|
+
return "negative";
|
|
1766
|
+
}
|
|
1767
|
+
if (change.values != null && change.values.diff !== 0) {
|
|
1768
|
+
return "mixed";
|
|
1769
|
+
}
|
|
1770
|
+
return "unchanged";
|
|
1771
|
+
});
|
|
1772
|
+
}
|
|
1773
|
+
function mergeDiffOutcomes(outcomes) {
|
|
1774
|
+
if (outcomes.every((outcome) => outcome === "unchanged")) {
|
|
1775
|
+
return "unchanged";
|
|
1776
|
+
}
|
|
1777
|
+
if (outcomes.includes("positive") && !outcomes.includes("negative")) {
|
|
1778
|
+
return "positive";
|
|
1779
|
+
}
|
|
1780
|
+
if (outcomes.includes("negative") && !outcomes.includes("positive")) {
|
|
1781
|
+
return "negative";
|
|
1782
|
+
}
|
|
1783
|
+
return "mixed";
|
|
1784
|
+
}
|
|
1785
|
+
function countDiffOutcomes(outcomes) {
|
|
1786
|
+
return {
|
|
1787
|
+
positive: outcomes.filter((outcome) => outcome === "positive").length,
|
|
1788
|
+
negative: outcomes.filter((outcome) => outcome === "negative").length,
|
|
1789
|
+
mixed: outcomes.filter((outcome) => outcome === "mixed").length,
|
|
1790
|
+
unchanged: outcomes.filter((outcome) => outcome === "unchanged").length
|
|
1791
|
+
};
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
// packages/utils/src/lib/reports/log-stdout-summary.ts
|
|
1337
1795
|
import chalk4 from "chalk";
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
return line + NEW_LINE;
|
|
1796
|
+
function log(msg = "") {
|
|
1797
|
+
ui().logger.log(msg);
|
|
1341
1798
|
}
|
|
1342
|
-
function
|
|
1799
|
+
function logStdoutSummary(report) {
|
|
1343
1800
|
const printCategories = report.categories.length > 0;
|
|
1344
|
-
|
|
1801
|
+
log(reportToHeaderSection2(report));
|
|
1802
|
+
log();
|
|
1803
|
+
logPlugins(report);
|
|
1804
|
+
if (printCategories) {
|
|
1805
|
+
logCategories(report);
|
|
1806
|
+
}
|
|
1807
|
+
log(`${FOOTER_PREFIX} ${CODE_PUSHUP_DOMAIN}`);
|
|
1808
|
+
log();
|
|
1345
1809
|
}
|
|
1346
1810
|
function reportToHeaderSection2(report) {
|
|
1347
1811
|
const { packageName, version: version2 } = report;
|
|
1348
1812
|
return `${chalk4.bold(reportHeadlineText)} - ${packageName}@${version2}`;
|
|
1349
1813
|
}
|
|
1350
|
-
function
|
|
1814
|
+
function logPlugins(report) {
|
|
1351
1815
|
const { plugins } = report;
|
|
1352
|
-
|
|
1816
|
+
plugins.forEach((plugin) => {
|
|
1353
1817
|
const { title, audits } = plugin;
|
|
1354
|
-
|
|
1818
|
+
log();
|
|
1819
|
+
log(chalk4.magentaBright.bold(`${title} audits`));
|
|
1820
|
+
log();
|
|
1355
1821
|
audits.forEach((audit) => {
|
|
1356
|
-
ui.
|
|
1822
|
+
ui().row([
|
|
1357
1823
|
{
|
|
1358
|
-
text:
|
|
1824
|
+
text: applyScoreColor({ score: audit.score, text: "\u25CF" }),
|
|
1359
1825
|
width: 2,
|
|
1360
1826
|
padding: [0, 1, 0, 0]
|
|
1361
1827
|
},
|
|
@@ -1369,34 +1835,40 @@ function reportToDetailSection(report) {
|
|
|
1369
1835
|
width: 10,
|
|
1370
1836
|
padding: [0, 0, 0, 0]
|
|
1371
1837
|
}
|
|
1372
|
-
);
|
|
1838
|
+
]);
|
|
1373
1839
|
});
|
|
1374
|
-
|
|
1375
|
-
}, "");
|
|
1376
|
-
}
|
|
1377
|
-
function reportToOverviewSection2({
|
|
1378
|
-
categories,
|
|
1379
|
-
plugins
|
|
1380
|
-
}) {
|
|
1381
|
-
const table = new CliTable3({
|
|
1382
|
-
// eslint-disable-next-line no-magic-numbers
|
|
1383
|
-
colWidths: [TERMINAL_WIDTH - 7 - 8 - 4, 7, 8],
|
|
1384
|
-
head: reportRawOverviewTableHeaders,
|
|
1385
|
-
colAligns: ["left", "right", "right"],
|
|
1386
|
-
style: {
|
|
1387
|
-
head: ["cyan"]
|
|
1388
|
-
}
|
|
1840
|
+
log();
|
|
1389
1841
|
});
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1842
|
+
}
|
|
1843
|
+
function logCategories({ categories, plugins }) {
|
|
1844
|
+
const hAlign = (idx) => idx === 0 ? "left" : "right";
|
|
1845
|
+
const rows = categories.map(({ title, score, refs }) => [
|
|
1846
|
+
title,
|
|
1847
|
+
applyScoreColor({ score }),
|
|
1848
|
+
countCategoryAudits(refs, plugins)
|
|
1849
|
+
]);
|
|
1850
|
+
const table = ui().table();
|
|
1851
|
+
table.columnWidths([TERMINAL_WIDTH - 9 - 10 - 4, 9, 10]);
|
|
1852
|
+
table.head(
|
|
1853
|
+
reportRawOverviewTableHeaders.map((heading, idx) => ({
|
|
1854
|
+
content: chalk4.cyan(heading),
|
|
1855
|
+
hAlign: hAlign(idx)
|
|
1856
|
+
}))
|
|
1396
1857
|
);
|
|
1397
|
-
|
|
1858
|
+
rows.forEach(
|
|
1859
|
+
(row) => table.row(
|
|
1860
|
+
row.map((content, idx) => ({
|
|
1861
|
+
content: content.toString(),
|
|
1862
|
+
hAlign: hAlign(idx)
|
|
1863
|
+
}))
|
|
1864
|
+
)
|
|
1865
|
+
);
|
|
1866
|
+
log(chalk4.magentaBright.bold("Categories"));
|
|
1867
|
+
log();
|
|
1868
|
+
table.render();
|
|
1869
|
+
log();
|
|
1398
1870
|
}
|
|
1399
|
-
function
|
|
1871
|
+
function applyScoreColor({ score, text }) {
|
|
1400
1872
|
const formattedScore = text ?? formatReportScore(score);
|
|
1401
1873
|
const style2 = text ? chalk4 : chalk4.bold;
|
|
1402
1874
|
if (score >= SCORE_COLOR_RANGE.GREEN_MIN) {
|
|
@@ -1547,9 +2019,9 @@ function sortPlugins(plugins) {
|
|
|
1547
2019
|
|
|
1548
2020
|
// packages/utils/src/lib/verbose-utils.ts
|
|
1549
2021
|
function getLogVerbose(verbose = false) {
|
|
1550
|
-
return (
|
|
2022
|
+
return (msg) => {
|
|
1551
2023
|
if (verbose) {
|
|
1552
|
-
|
|
2024
|
+
ui().logger.info(msg);
|
|
1553
2025
|
}
|
|
1554
2026
|
};
|
|
1555
2027
|
}
|
|
@@ -1567,7 +2039,7 @@ var verboseUtils = (verbose = false) => ({
|
|
|
1567
2039
|
|
|
1568
2040
|
// packages/core/package.json
|
|
1569
2041
|
var name = "@code-pushup/core";
|
|
1570
|
-
var version = "0.
|
|
2042
|
+
var version = "0.28.0";
|
|
1571
2043
|
|
|
1572
2044
|
// packages/core/src/lib/implementation/execute-plugin.ts
|
|
1573
2045
|
import chalk5 from "chalk";
|
|
@@ -1680,11 +2152,9 @@ async function executePlugins(plugins, options) {
|
|
|
1680
2152
|
}
|
|
1681
2153
|
}, Promise.resolve([]));
|
|
1682
2154
|
progressBar?.endProgress("Done running plugins");
|
|
1683
|
-
const
|
|
1684
|
-
console.error(reason);
|
|
1685
|
-
};
|
|
2155
|
+
const errorsTransform = ({ reason }) => String(reason);
|
|
1686
2156
|
const results = await Promise.allSettled(pluginsResult);
|
|
1687
|
-
logMultipleResults(results, "Plugins", void 0,
|
|
2157
|
+
logMultipleResults(results, "Plugins", void 0, errorsTransform);
|
|
1688
2158
|
const { fulfilled, rejected } = groupByStatus(results);
|
|
1689
2159
|
if (rejected.length > 0) {
|
|
1690
2160
|
const errorMessages = rejected.map(({ reason }) => String(reason)).join(", ");
|
|
@@ -1739,7 +2209,7 @@ var PersistError = class extends Error {
|
|
|
1739
2209
|
async function persistReport(report, options) {
|
|
1740
2210
|
const { outputDir, filename, format } = options;
|
|
1741
2211
|
const sortedScoredReport = sortReport(scoreReport(report));
|
|
1742
|
-
|
|
2212
|
+
logStdoutSummary(sortedScoredReport);
|
|
1743
2213
|
const results = format.map((reportType) => {
|
|
1744
2214
|
switch (reportType) {
|
|
1745
2215
|
case "json":
|
|
@@ -1758,7 +2228,7 @@ async function persistReport(report, options) {
|
|
|
1758
2228
|
try {
|
|
1759
2229
|
await mkdir2(outputDir, { recursive: true });
|
|
1760
2230
|
} catch (error) {
|
|
1761
|
-
|
|
2231
|
+
ui().logger.warning(error.toString());
|
|
1762
2232
|
throw new PersistDirError(outputDir);
|
|
1763
2233
|
}
|
|
1764
2234
|
}
|
|
@@ -1773,7 +2243,7 @@ async function persistReport(report, options) {
|
|
|
1773
2243
|
}
|
|
1774
2244
|
async function persistResult(reportPath, content) {
|
|
1775
2245
|
return writeFile(reportPath, content).then(() => stat2(reportPath)).then((stats) => [reportPath, stats.size]).catch((error) => {
|
|
1776
|
-
|
|
2246
|
+
ui().logger.warning(error.toString());
|
|
1777
2247
|
throw new PersistError(reportPath);
|
|
1778
2248
|
});
|
|
1779
2249
|
}
|
|
@@ -1794,8 +2264,209 @@ async function collectAndPersistReports(options) {
|
|
|
1794
2264
|
});
|
|
1795
2265
|
}
|
|
1796
2266
|
|
|
1797
|
-
// packages/core/src/lib/
|
|
2267
|
+
// packages/core/src/lib/compare.ts
|
|
2268
|
+
import { writeFile as writeFile2 } from "node:fs/promises";
|
|
1798
2269
|
import { join as join5 } from "node:path";
|
|
2270
|
+
|
|
2271
|
+
// packages/core/src/lib/implementation/compare-scorables.ts
|
|
2272
|
+
function compareCategories(reports) {
|
|
2273
|
+
const { pairs, added, removed } = matchArrayItemsByKey({
|
|
2274
|
+
before: reports.before.categories,
|
|
2275
|
+
after: reports.after.categories,
|
|
2276
|
+
key: "slug"
|
|
2277
|
+
});
|
|
2278
|
+
const { changed, unchanged } = comparePairs(
|
|
2279
|
+
pairs,
|
|
2280
|
+
({ before, after }) => before.score === after.score
|
|
2281
|
+
);
|
|
2282
|
+
return {
|
|
2283
|
+
changed: changed.map(categoryPairToDiff),
|
|
2284
|
+
unchanged: unchanged.map(categoryToResult),
|
|
2285
|
+
added: added.map(categoryToResult),
|
|
2286
|
+
removed: removed.map(categoryToResult)
|
|
2287
|
+
};
|
|
2288
|
+
}
|
|
2289
|
+
function compareGroups(reports) {
|
|
2290
|
+
const { pairs, added, removed } = matchArrayItemsByKey({
|
|
2291
|
+
before: listGroupsFromAllPlugins(reports.before),
|
|
2292
|
+
after: listGroupsFromAllPlugins(reports.after),
|
|
2293
|
+
key: ({ plugin, group }) => `${plugin.slug}/${group.slug}`
|
|
2294
|
+
});
|
|
2295
|
+
const { changed, unchanged } = comparePairs(
|
|
2296
|
+
pairs,
|
|
2297
|
+
({ before, after }) => before.group.score === after.group.score
|
|
2298
|
+
);
|
|
2299
|
+
return {
|
|
2300
|
+
changed: changed.map(pluginGroupPairToDiff),
|
|
2301
|
+
unchanged: unchanged.map(pluginGroupToResult),
|
|
2302
|
+
added: added.map(pluginGroupToResult),
|
|
2303
|
+
removed: removed.map(pluginGroupToResult)
|
|
2304
|
+
};
|
|
2305
|
+
}
|
|
2306
|
+
function compareAudits2(reports) {
|
|
2307
|
+
const { pairs, added, removed } = matchArrayItemsByKey({
|
|
2308
|
+
before: listAuditsFromAllPlugins(reports.before),
|
|
2309
|
+
after: listAuditsFromAllPlugins(reports.after),
|
|
2310
|
+
key: ({ plugin, audit }) => `${plugin.slug}/${audit.slug}`
|
|
2311
|
+
});
|
|
2312
|
+
const { changed, unchanged } = comparePairs(
|
|
2313
|
+
pairs,
|
|
2314
|
+
({ before, after }) => before.audit.value === after.audit.value && before.audit.score === after.audit.score
|
|
2315
|
+
);
|
|
2316
|
+
return {
|
|
2317
|
+
changed: changed.map(pluginAuditPairToDiff),
|
|
2318
|
+
unchanged: unchanged.map(pluginAuditToResult),
|
|
2319
|
+
added: added.map(pluginAuditToResult),
|
|
2320
|
+
removed: removed.map(pluginAuditToResult)
|
|
2321
|
+
};
|
|
2322
|
+
}
|
|
2323
|
+
function categoryToResult(category) {
|
|
2324
|
+
return {
|
|
2325
|
+
slug: category.slug,
|
|
2326
|
+
title: category.title,
|
|
2327
|
+
score: category.score
|
|
2328
|
+
};
|
|
2329
|
+
}
|
|
2330
|
+
function categoryPairToDiff({
|
|
2331
|
+
before,
|
|
2332
|
+
after
|
|
2333
|
+
}) {
|
|
2334
|
+
return {
|
|
2335
|
+
slug: after.slug,
|
|
2336
|
+
title: after.title,
|
|
2337
|
+
scores: {
|
|
2338
|
+
before: before.score,
|
|
2339
|
+
after: after.score,
|
|
2340
|
+
diff: after.score - before.score
|
|
2341
|
+
}
|
|
2342
|
+
};
|
|
2343
|
+
}
|
|
2344
|
+
function pluginGroupToResult({ group, plugin }) {
|
|
2345
|
+
return {
|
|
2346
|
+
slug: group.slug,
|
|
2347
|
+
title: group.title,
|
|
2348
|
+
plugin: {
|
|
2349
|
+
slug: plugin.slug,
|
|
2350
|
+
title: plugin.title
|
|
2351
|
+
},
|
|
2352
|
+
score: group.score
|
|
2353
|
+
};
|
|
2354
|
+
}
|
|
2355
|
+
function pluginGroupPairToDiff({
|
|
2356
|
+
before,
|
|
2357
|
+
after
|
|
2358
|
+
}) {
|
|
2359
|
+
return {
|
|
2360
|
+
slug: after.group.slug,
|
|
2361
|
+
title: after.group.title,
|
|
2362
|
+
plugin: {
|
|
2363
|
+
slug: after.plugin.slug,
|
|
2364
|
+
title: after.plugin.title
|
|
2365
|
+
},
|
|
2366
|
+
scores: {
|
|
2367
|
+
before: before.group.score,
|
|
2368
|
+
after: after.group.score,
|
|
2369
|
+
diff: after.group.score - before.group.score
|
|
2370
|
+
}
|
|
2371
|
+
};
|
|
2372
|
+
}
|
|
2373
|
+
function pluginAuditToResult({ audit, plugin }) {
|
|
2374
|
+
return {
|
|
2375
|
+
slug: audit.slug,
|
|
2376
|
+
title: audit.title,
|
|
2377
|
+
plugin: {
|
|
2378
|
+
slug: plugin.slug,
|
|
2379
|
+
title: plugin.title
|
|
2380
|
+
},
|
|
2381
|
+
score: audit.score,
|
|
2382
|
+
value: audit.value,
|
|
2383
|
+
displayValue: audit.displayValue
|
|
2384
|
+
};
|
|
2385
|
+
}
|
|
2386
|
+
function pluginAuditPairToDiff({
|
|
2387
|
+
before,
|
|
2388
|
+
after
|
|
2389
|
+
}) {
|
|
2390
|
+
return {
|
|
2391
|
+
slug: after.audit.slug,
|
|
2392
|
+
title: after.audit.title,
|
|
2393
|
+
plugin: {
|
|
2394
|
+
slug: after.plugin.slug,
|
|
2395
|
+
title: after.plugin.title
|
|
2396
|
+
},
|
|
2397
|
+
scores: {
|
|
2398
|
+
before: before.audit.score,
|
|
2399
|
+
after: after.audit.score,
|
|
2400
|
+
diff: after.audit.score - before.audit.score
|
|
2401
|
+
},
|
|
2402
|
+
values: {
|
|
2403
|
+
before: before.audit.value,
|
|
2404
|
+
after: after.audit.value,
|
|
2405
|
+
diff: after.audit.value - before.audit.value
|
|
2406
|
+
},
|
|
2407
|
+
displayValues: {
|
|
2408
|
+
before: before.audit.displayValue,
|
|
2409
|
+
after: after.audit.displayValue
|
|
2410
|
+
}
|
|
2411
|
+
};
|
|
2412
|
+
}
|
|
2413
|
+
|
|
2414
|
+
// packages/core/src/lib/compare.ts
|
|
2415
|
+
async function compareReportFiles(inputPaths, persistConfig) {
|
|
2416
|
+
const { outputDir, filename, format } = persistConfig;
|
|
2417
|
+
const [reportBefore, reportAfter] = await Promise.all([
|
|
2418
|
+
readJsonFile(inputPaths.before),
|
|
2419
|
+
readJsonFile(inputPaths.after)
|
|
2420
|
+
]);
|
|
2421
|
+
const reports = {
|
|
2422
|
+
before: reportSchema.parse(reportBefore),
|
|
2423
|
+
after: reportSchema.parse(reportAfter)
|
|
2424
|
+
};
|
|
2425
|
+
const reportsDiff = compareReports(reports);
|
|
2426
|
+
return Promise.all(
|
|
2427
|
+
format.map(async (fmt) => {
|
|
2428
|
+
const outputPath = join5(outputDir, `${filename}-diff.${fmt}`);
|
|
2429
|
+
const content = reportsDiffToFileContent(reportsDiff, fmt);
|
|
2430
|
+
await ensureDirectoryExists(outputDir);
|
|
2431
|
+
await writeFile2(outputPath, content);
|
|
2432
|
+
return outputPath;
|
|
2433
|
+
})
|
|
2434
|
+
);
|
|
2435
|
+
}
|
|
2436
|
+
function compareReports(reports) {
|
|
2437
|
+
const start = performance.now();
|
|
2438
|
+
const date = (/* @__PURE__ */ new Date()).toISOString();
|
|
2439
|
+
const commits = reports.before.commit != null && reports.after.commit != null ? { before: reports.before.commit, after: reports.after.commit } : null;
|
|
2440
|
+
const scoredReports = {
|
|
2441
|
+
before: scoreReport(reports.before),
|
|
2442
|
+
after: scoreReport(reports.after)
|
|
2443
|
+
};
|
|
2444
|
+
const categories = compareCategories(scoredReports);
|
|
2445
|
+
const groups = compareGroups(scoredReports);
|
|
2446
|
+
const audits = compareAudits2(scoredReports);
|
|
2447
|
+
const duration = calcDuration(start);
|
|
2448
|
+
return {
|
|
2449
|
+
commits,
|
|
2450
|
+
categories,
|
|
2451
|
+
groups,
|
|
2452
|
+
audits,
|
|
2453
|
+
packageName: name,
|
|
2454
|
+
version,
|
|
2455
|
+
date,
|
|
2456
|
+
duration
|
|
2457
|
+
};
|
|
2458
|
+
}
|
|
2459
|
+
function reportsDiffToFileContent(reportsDiff, format) {
|
|
2460
|
+
switch (format) {
|
|
2461
|
+
case "json":
|
|
2462
|
+
return JSON.stringify(reportsDiff, null, 2);
|
|
2463
|
+
case "md":
|
|
2464
|
+
return generateMdReportsDiff(reportsDiff);
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2468
|
+
// packages/core/src/lib/implementation/read-rc-file.ts
|
|
2469
|
+
import { join as join6 } from "node:path";
|
|
1799
2470
|
var ConfigPathError = class extends Error {
|
|
1800
2471
|
constructor(configPath) {
|
|
1801
2472
|
super(`Provided path '${configPath}' is not valid.`);
|
|
@@ -1829,7 +2500,7 @@ async function autoloadRc(tsconfig) {
|
|
|
1829
2500
|
);
|
|
1830
2501
|
}
|
|
1831
2502
|
return readRcByPath(
|
|
1832
|
-
|
|
2503
|
+
join6(process.cwd(), `${CONFIG_FILE_NAME}.${ext}`),
|
|
1833
2504
|
tsconfig
|
|
1834
2505
|
);
|
|
1835
2506
|
}
|
|
@@ -1969,6 +2640,8 @@ export {
|
|
|
1969
2640
|
autoloadRc,
|
|
1970
2641
|
collect,
|
|
1971
2642
|
collectAndPersistReports,
|
|
2643
|
+
compareReportFiles,
|
|
2644
|
+
compareReports,
|
|
1972
2645
|
executePlugin,
|
|
1973
2646
|
executePlugins,
|
|
1974
2647
|
persistReport,
|